PyFiberModes 0.12.0__tar.gz → 0.13.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 (155) hide show
  1. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/.gitignore +2 -0
  2. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/.zenodo.json +1 -1
  3. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/CHANGELOG.md +36 -1
  4. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PKG-INFO +17 -6
  5. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/__init__.py +13 -2
  6. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/_version.py +2 -2
  7. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/analysis.py +113 -4
  8. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/coordinates.py +12 -7
  9. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/directories.py +0 -12
  10. pyfibermodes-0.13.1/PyFiberModes/exceptions.py +17 -0
  11. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/factory.py +10 -3
  12. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/fiber.py +421 -222
  13. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/field.py +223 -104
  14. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/fundamentals.py +4 -5
  15. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/loader.py +1 -2
  16. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/mode.py +26 -21
  17. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/mode_instances.py +1 -2
  18. pyfibermodes-0.13.1/PyFiberModes/models.py +63 -0
  19. pyfibermodes-0.13.1/PyFiberModes/optimization.py +173 -0
  20. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/propagation.py +96 -3
  21. pyfibermodes-0.13.1/PyFiberModes/services.py +162 -0
  22. pyfibermodes-0.13.1/PyFiberModes/solver/__init__.py +18 -0
  23. pyfibermodes-0.13.1/PyFiberModes/solver/base_solver.py +329 -0
  24. pyfibermodes-0.13.1/PyFiberModes/solver/mlsif/__init__.py +3 -0
  25. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/solver/mlsif/neff.py +211 -182
  26. pyfibermodes-0.13.1/PyFiberModes/solver/protocols.py +32 -0
  27. pyfibermodes-0.13.1/PyFiberModes/solver/results.py +56 -0
  28. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/solver/ssif/__init__.py +2 -0
  29. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/solver/ssif/cutoff.py +63 -4
  30. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/solver/ssif/neff.py +225 -158
  31. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/solver/tlsif/__init__.py +2 -0
  32. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/solver/tlsif/cutoff.py +167 -66
  33. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/source.py +16 -2
  34. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/stepindex.py +33 -16
  35. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes.egg-info/PKG-INFO +17 -6
  36. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes.egg-info/SOURCES.txt +32 -1
  37. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes.egg-info/requires.txt +9 -5
  38. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/README.rst +8 -0
  39. pyfibermodes-0.13.1/benchmarks/README.md +11 -0
  40. pyfibermodes-0.13.1/benchmarks/test_field_performance.py +32 -0
  41. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/conda.recipe/meta.yaml +1 -2
  42. pyfibermodes-0.13.1/docs/examples/analysis/README.rst +6 -0
  43. pyfibermodes-0.13.1/docs/examples/analysis/plot_coupled_mode_exchange.py +22 -0
  44. pyfibermodes-0.13.1/docs/examples/analysis/plot_structured_wavelength_sweep.py +27 -0
  45. pyfibermodes-0.13.1/docs/examples/benchmarks/README.rst +7 -0
  46. pyfibermodes-0.13.1/docs/examples/benchmarks/plot_field_grid_scaling.py +28 -0
  47. pyfibermodes-0.13.1/docs/examples/fields/README.rst +7 -0
  48. pyfibermodes-0.13.1/docs/examples/fields/plot_vector_components.py +24 -0
  49. pyfibermodes-0.13.1/docs/examples/validation/README.rst +7 -0
  50. pyfibermodes-0.13.1/docs/examples/validation/plot_effective_index_bounds.py +25 -0
  51. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/docs/source/_static/default.css +21 -1
  52. pyfibermodes-0.13.1/docs/source/_static/favicon.png +0 -0
  53. pyfibermodes-0.13.1/docs/source/_static/logo.png +0 -0
  54. pyfibermodes-0.13.1/docs/source/_static/thumbnail.png +0 -0
  55. pyfibermodes-0.13.1/docs/source/architecture.rst +37 -0
  56. pyfibermodes-0.13.1/docs/source/code/analysis.rst +26 -0
  57. pyfibermodes-0.13.1/docs/source/code/fibers.rst +15 -0
  58. pyfibermodes-0.13.1/docs/source/code/fields.rst +11 -0
  59. pyfibermodes-0.13.1/docs/source/code/fundamentals.rst +22 -0
  60. pyfibermodes-0.13.1/docs/source/code.rst +42 -0
  61. pyfibermodes-0.13.1/docs/source/conf.py +193 -0
  62. pyfibermodes-0.13.1/docs/source/examples.rst +73 -0
  63. pyfibermodes-0.13.1/docs/source/getting_started.rst +34 -0
  64. pyfibermodes-0.13.1/docs/source/index.rst +23 -0
  65. pyfibermodes-0.13.1/docs/source/performance.rst +20 -0
  66. pyfibermodes-0.13.1/docs/source/references.rst +12 -0
  67. pyfibermodes-0.13.1/docs/source/theory.rst +34 -0
  68. pyfibermodes-0.13.1/docs/source/troubleshooting.rst +33 -0
  69. pyfibermodes-0.13.1/docs/source/workflows.rst +35 -0
  70. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/pyproject.toml +11 -6
  71. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/tests/test_api.py +2 -2
  72. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/tests/test_architecture.py +17 -8
  73. pyfibermodes-0.13.1/tests/test_architecture_enhancements.py +86 -0
  74. pyfibermodes-0.13.1/tests/test_docstrings.py +34 -0
  75. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/tests/test_field.py +50 -3
  76. pyfibermodes-0.13.1/tests/test_physics_regression.py +41 -0
  77. pyfibermodes-0.12.0/PyFiberModes/__future__.py +0 -108
  78. pyfibermodes-0.12.0/PyFiberModes/optimization.py +0 -86
  79. pyfibermodes-0.12.0/PyFiberModes/solver/__init__.py +0 -3
  80. pyfibermodes-0.12.0/PyFiberModes/solver/base_solver.py +0 -184
  81. pyfibermodes-0.12.0/PyFiberModes/solver/mlsif/__init__.py +0 -1
  82. pyfibermodes-0.12.0/docs/source/_static/thumbnail.png +0 -0
  83. pyfibermodes-0.12.0/docs/source/code.rst +0 -55
  84. pyfibermodes-0.12.0/docs/source/conf.py +0 -177
  85. pyfibermodes-0.12.0/docs/source/examples.rst +0 -14
  86. pyfibermodes-0.12.0/docs/source/index.rst +0 -14
  87. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/.coveragerc +0 -0
  88. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/.github/dependabot.yml +0 -0
  89. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/.github/workflows/deploy_PyPi.yml +0 -0
  90. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/.github/workflows/deploy_anaconda.yml +0 -0
  91. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/.github/workflows/deploy_coverage.yml +0 -0
  92. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/.github/workflows/deploy_documentation.yml +0 -0
  93. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/.github/workflows/quality.yml +0 -0
  94. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/.github/workflows/tests.yml +0 -0
  95. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/CITATION.cff +0 -0
  96. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/CONTRIBUTING.md +0 -0
  97. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/LICENSE +0 -0
  98. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/MANIFEST.in +0 -0
  99. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/Makefile +0 -0
  100. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/fiber_files/1550BHP.yaml +0 -0
  101. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/fiber_files/DCF13.yaml +0 -0
  102. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/fiber_files/DCF1300S_20.yaml +0 -0
  103. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/fiber_files/DCF1300S_26.yaml +0 -0
  104. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/fiber_files/DCF1300S_33.yaml +0 -0
  105. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/fiber_files/DCF1300S_42.yaml +0 -0
  106. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/fiber_files/F2028M12.yaml +0 -0
  107. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/fiber_files/F2028M21.yaml +0 -0
  108. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/fiber_files/F2028M24.yaml +0 -0
  109. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/fiber_files/F2058G1.yaml +0 -0
  110. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/fiber_files/F2058L1.yaml +0 -0
  111. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/fiber_files/HI1060.yaml +0 -0
  112. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/fiber_files/HP630.yaml +0 -0
  113. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/fiber_files/SM1950.yaml +0 -0
  114. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/fiber_files/SMF28.yaml +0 -0
  115. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/fiber_files/fluorine_doped_1%_capillary.yaml +0 -0
  116. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/fiber_files/fluorine_doped_2%_capillary.yaml +0 -0
  117. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/fiber_files/test_fiber.yaml +0 -0
  118. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes/fiber_files/test_multimode_fiber.yaml +0 -0
  119. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes.egg-info/dependency_links.txt +0 -0
  120. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/PyFiberModes.egg-info/top_level.txt +0 -0
  121. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/docs/Makefile +0 -0
  122. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/docs/examples/DCF/README.rst +0 -0
  123. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/docs/examples/DCF/plot_DCF_fields.py +0 -0
  124. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/docs/examples/README.rst +0 -0
  125. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/docs/examples/SMF28/README.rst +0 -0
  126. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/docs/examples/SMF28/plot_smf28.py +0 -0
  127. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/docs/examples/SMF28/plot_smf28_dispersion_vs_wavelength.py +0 -0
  128. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/docs/examples/SMF28/plot_smf28_group_index_vs_wavelength.py +0 -0
  129. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/docs/examples/basic/README.rst +0 -0
  130. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/docs/examples/basic/plot_U_vs_V.py +0 -0
  131. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/docs/examples/basic/plot_comparison_solvers.py +0 -0
  132. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/docs/examples/basic/plot_dispersion.py +0 -0
  133. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/docs/examples/basic/plot_effective_index.py +0 -0
  134. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/docs/examples/basic/plot_fiber_LP_modes.py +0 -0
  135. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/docs/examples/basic/plot_group_index.py +0 -0
  136. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/docs/examples/basic/plot_mode_field.py +0 -0
  137. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/docs/examples/basic/plot_mode_field_1.py +0 -0
  138. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/docs/examples/basic/plot_mode_field_2.py +0 -0
  139. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/docs/examples/basic/plot_neff_tapered_fiber.py +0 -0
  140. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/docs/make.bat +0 -0
  141. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/pytest.ini +0 -0
  142. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/setup.cfg +0 -0
  143. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/tests/__init__.py +0 -0
  144. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/tests/helpers.py +0 -0
  145. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/tests/test_advanced_features.py +0 -0
  146. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/tests/test_coordinates.py +0 -0
  147. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/tests/test_fiber.py +0 -0
  148. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/tests/test_file_loading.py +0 -0
  149. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/tests/test_release_tools.py +0 -0
  150. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/tests/test_ssif.py +0 -0
  151. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/tests/test_tlsif.py +0 -0
  152. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/tests/test_validation.py +0 -0
  153. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/tools/check_release.py +0 -0
  154. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/tools/next_release_version.py +0 -0
  155. {pyfibermodes-0.12.0 → pyfibermodes-0.13.1}/tools/release_tag.py +0 -0
@@ -8,6 +8,8 @@
8
8
  !development
9
9
  !docs
10
10
  !tests
11
+ !benchmarks/
12
+ !benchmarks/**
11
13
  !.flake8
12
14
  !.gitignore
13
15
  !.pre-commit-config.yaml
@@ -9,7 +9,7 @@
9
9
  "license": "MIT",
10
10
  "upload_type": "software",
11
11
  "access_right": "open",
12
- "version": "0.12.0",
12
+ "version": "0.13.1",
13
13
  "publication_date": "2026-09-22",
14
14
  "keywords": [
15
15
  "optical fibers",
@@ -6,6 +6,39 @@ use [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.13.1] - 2026-09-22
10
+
11
+ ### Added
12
+
13
+ - Added validated immutable layer and solver settings, structural solver
14
+ protocols, domain-specific exceptions, and structured solver diagnostics.
15
+ - Added deterministic physical-reference, convergence, caching, architecture,
16
+ and opt-in `pytest-benchmark` performance suites.
17
+
18
+ ### Changed
19
+
20
+ - Unified cylindrical coordinates under one canonical model, split modal and
21
+ field orchestration into focused services, and added geometry-aware caching.
22
+ - Vectorized radial index lookup and routed field construction through a
23
+ single batched radial-field interface.
24
+ - Standardized the group-velocity API on `get_group_velocity` and removed
25
+ unreferenced experimental modules, unfinished solver branches, misspelled
26
+ methods, lowercase directory aliases, and obsolete compatibility wrappers.
27
+
28
+ ## [0.13.0] - 2026-09-22
29
+
30
+ ### Changed
31
+
32
+ - Replaced MPSPlots styling and colormaps with native Matplotlib equivalents,
33
+ removing MPSPlots from all runtime and documentation dependencies.
34
+ - Field maps now share one radial solver pass across all electric and magnetic
35
+ components and cache interpolated, azimuthal, and batched component arrays.
36
+ - Added NumPyDoc documentation across modules, public APIs, data models, and
37
+ numerical solver helpers, with structural coverage enforced by tests.
38
+ - Reorganized the documentation into learning, theory, workflow, performance,
39
+ troubleshooting, gallery, and task-oriented API sections; added four new
40
+ example series and a unified logo and favicon identity.
41
+
9
42
  ## [0.12.0] - 2026-09-22
10
43
 
11
44
  ### Changed
@@ -41,7 +74,9 @@ use [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
41
74
 
42
75
  - Established the current tagged baseline for subsequent documented releases.
43
76
 
44
- [Unreleased]: https://github.com/MartinPdeS/PyFiberModes/compare/v0.12.0...HEAD
77
+ [Unreleased]: https://github.com/MartinPdeS/PyFiberModes/compare/v0.13.1...HEAD
78
+ [0.13.1]: https://github.com/MartinPdeS/PyFiberModes/releases/tag/v0.13.1
79
+ [0.13.0]: https://github.com/MartinPdeS/PyFiberModes/releases/tag/v0.13.0
45
80
  [0.12.0]: https://github.com/MartinPdeS/PyFiberModes/releases/tag/v0.12.0
46
81
  [0.11.0]: https://github.com/MartinPdeS/PyFiberModes/releases/tag/v0.11.0
47
82
  [0.10.0]: https://github.com/MartinPdeS/PyFiberModes/releases/tag/v0.10.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyFiberModes
3
- Version: 0.12.0
3
+ Version: 0.13.1
4
4
  Summary: A package for light propagation in fiber optics.
5
5
  Author-email: Martin Poinsinet de Sivry-Houle <martin.poinsinet.de.sivry@gmail.com>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -697,28 +697,36 @@ Requires-Python: >=3.10
697
697
  Description-Content-Type: text/x-rst
698
698
  License-File: LICENSE
699
699
  Requires-Dist: PyOptik<3,>=2
700
- Requires-Dist: MPSPlots
701
700
  Requires-Dist: PyFinitDiff
701
+ Requires-Dist: matplotlib>=3.8
702
702
  Requires-Dist: pyyaml
703
703
  Requires-Dist: numpy
704
704
  Requires-Dist: scipy
705
705
  Provides-Extra: testing
706
- Requires-Dist: pytest<9.0,>=7.4; extra == "testing"
706
+ Requires-Dist: pytest<10.0,>=7.4; extra == "testing"
707
707
  Requires-Dist: pytest-cov<8,>=2; extra == "testing"
708
708
  Requires-Dist: pytest-json-report~=1.5; extra == "testing"
709
709
  Requires-Dist: coverage~=7.6; extra == "testing"
710
710
  Requires-Dist: ruff<1,>=0.8; extra == "testing"
711
711
  Requires-Dist: tomli<3,>=2; python_version < "3.11" and extra == "testing"
712
+ Provides-Extra: benchmarking
713
+ Requires-Dist: pytest-benchmark<6,>=5; extra == "benchmarking"
712
714
  Provides-Extra: documentation
713
- Requires-Dist: numpydoc==1.9.0; extra == "documentation"
715
+ Requires-Dist: numpydoc==1.10.0; extra == "documentation"
714
716
  Requires-Dist: sphinx>=5.1.1; extra == "documentation"
715
- Requires-Dist: sphinx-gallery==0.19.0; extra == "documentation"
717
+ Requires-Dist: sphinx-gallery==0.21.0; extra == "documentation"
718
+ Requires-Dist: sphinx-design<1,>=0.6; extra == "documentation"
716
719
  Requires-Dist: sphinx-rtd-theme==3.1.0; extra == "documentation"
717
- Requires-Dist: pydata-sphinx-theme==0.14.1; extra == "documentation"
720
+ Requires-Dist: pydata-sphinx-theme==0.17.1; extra == "documentation"
718
721
  Provides-Extra: dev
719
722
  Requires-Dist: ruff<1,>=0.8; extra == "dev"
720
723
  Dynamic: license-file
721
724
 
725
+ .. image:: https://raw.githubusercontent.com/MartinPdeS/PyFiberModes/master/docs/source/_static/logo.png
726
+ :width: 620
727
+ :align: center
728
+ :alt: PyFiberModes logo
729
+
722
730
  .. list-table::
723
731
  :widths: 35 65
724
732
  :header-rows: 1
@@ -769,6 +777,9 @@ Features
769
777
  * Constrained optimization of layer radii, refractive indices, and custom parameters.
770
778
  * Constant or longitudinally varying coupled-mode propagation.
771
779
  * A catalog of common and example fiber definitions stored as readable YAML files.
780
+ * Structured solver diagnostics, validated configuration models, and explicit
781
+ solver protocols for extending numerical backends.
782
+ * Geometry-aware modal caching and vectorized radial index and field workflows.
772
783
 
773
784
  Installation
774
785
  ------------
@@ -1,7 +1,14 @@
1
+ """Public API for circular optical-fiber mode analysis."""
2
+
1
3
  from PyFiberModes.mode import Mode, Family # noqa: F401
2
4
  from PyFiberModes.mode_instances import * # noqa: F403
3
5
  from PyFiberModes.factory import FiberFactory
6
+ from PyFiberModes.fiber import Fiber, load_fiber
4
7
  from PyFiberModes.field import Field
8
+ from PyFiberModes.coordinates import CartesianCoordinates, CylindricalCoordinates
9
+ from PyFiberModes.exceptions import ConvergenceError, SolverError, ValidationError
10
+ from PyFiberModes.models import LayerSpec, SolverSettings
11
+ from PyFiberModes.solver.results import SolverResult
5
12
  from PyFiberModes.analysis import ModeSweepResult, find_modes, sweep_modes
6
13
  from PyFiberModes.optimization import DesignParameter, DesignResult, optimize_fiber
7
14
  from PyFiberModes.propagation import CoupledModeSystem, PropagationResult, coupling_matrix, overlap
@@ -15,11 +22,15 @@ except ImportError:
15
22
 
16
23
 
17
24
  __all__ = [ # noqa: F405
18
- 'Wavelength',
19
25
  'Mode',
20
- 'ModeFamily',
26
+ 'Family',
27
+ 'Fiber',
28
+ 'load_fiber',
21
29
  'FiberFactory',
22
30
  'Field',
31
+ 'CartesianCoordinates', 'CylindricalCoordinates',
32
+ 'LayerSpec', 'SolverSettings', 'SolverResult',
33
+ 'ValidationError', 'SolverError', 'ConvergenceError',
23
34
  'ModeSweepResult', 'find_modes', 'sweep_modes',
24
35
  'DesignParameter', 'DesignResult', 'optimize_fiber',
25
36
  'CoupledModeSystem', 'PropagationResult', 'coupling_matrix', 'overlap',
@@ -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.12.0'
22
- __version_tuple__ = version_tuple = (0, 12, 0)
21
+ __version__ = version = '0.13.1'
22
+ __version_tuple__ = version_tuple = (0, 13, 1)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -11,10 +11,21 @@ from PyFiberModes.mode import Mode
11
11
 
12
12
  @dataclass(frozen=True)
13
13
  class ModeSweepResult:
14
- """Dense, dependency-free result returned by :func:`sweep_modes`.
14
+ """Store modal quantities evaluated over a parameter sweep.
15
15
 
16
16
  Rows correspond to ``parameters`` and columns to ``modes``. Missing or
17
17
  unguided solutions are represented by NaN.
18
+
19
+ Parameters
20
+ ----------
21
+ parameter_name : str
22
+ Name of the swept parameter.
23
+ parameters : numpy.ndarray
24
+ One-dimensional parameter values.
25
+ modes : tuple of Mode
26
+ Modes represented by the columns of every value array.
27
+ values : dict of str to numpy.ndarray
28
+ Modal metrics shaped ``(n_parameters, n_modes)``.
18
29
  """
19
30
 
20
31
  parameter_name: str
@@ -23,13 +34,44 @@ class ModeSweepResult:
23
34
  values: dict[str, np.ndarray]
24
35
 
25
36
  def __getitem__(self, metric: str) -> np.ndarray:
37
+ """Return the array associated with a metric name.
38
+
39
+ Parameters
40
+ ----------
41
+ metric : str
42
+ Metric key stored in :attr:`values`.
43
+
44
+ Returns
45
+ -------
46
+ numpy.ndarray
47
+ Metric values shaped ``(n_parameters, n_modes)``.
48
+ """
26
49
  return self.values[metric]
27
50
 
28
51
  def for_mode(self, mode: Mode) -> dict[str, np.ndarray]:
52
+ """Extract every metric for one mode.
53
+
54
+ Parameters
55
+ ----------
56
+ mode : Mode
57
+ Mode whose column is requested.
58
+
59
+ Returns
60
+ -------
61
+ dict of str to numpy.ndarray
62
+ Mapping from metric names to one-dimensional sweep values.
63
+ """
29
64
  index = self.modes.index(mode)
30
65
  return {name: value[:, index] for name, value in self.values.items()}
31
66
 
32
67
  def as_records(self) -> list[dict]:
68
+ """Convert the dense result into row-oriented records.
69
+
70
+ Returns
71
+ -------
72
+ list of dict
73
+ One record for every parameter and mode combination.
74
+ """
33
75
  return [
34
76
  {self.parameter_name: float(parameter), "mode": mode, **{
35
77
  name: float(array[i, j]) for name, array in self.values.items()
@@ -42,7 +84,22 @@ class ModeSweepResult:
42
84
  def candidate_modes(
43
85
  families: Sequence[str] = ("LP",), max_nu: int = 6, max_m: int = 6
44
86
  ) -> tuple[Mode, ...]:
45
- """Generate deterministic candidate modes for automatic discovery."""
87
+ """Generate a deterministic bounded set of mode candidates.
88
+
89
+ Parameters
90
+ ----------
91
+ families : sequence of str, optional
92
+ Mode families to enumerate.
93
+ max_nu : int, optional
94
+ Largest azimuthal order to include.
95
+ max_m : int, optional
96
+ Largest radial order to include.
97
+
98
+ Returns
99
+ -------
100
+ tuple of Mode
101
+ Candidates ordered by family, azimuthal order, and radial order.
102
+ """
46
103
  modes = []
47
104
  for family in families:
48
105
  nus = (0,) if family in ("TE", "TM") else range(max_nu + 1)
@@ -54,7 +111,25 @@ def candidate_modes(
54
111
 
55
112
 
56
113
  def find_modes(fiber, families=("LP",), max_nu=6, max_m=6) -> tuple[Mode, ...]:
57
- """Return all guided modes among a bounded set of candidates."""
114
+ """Find guided modes supported by a fiber.
115
+
116
+ Parameters
117
+ ----------
118
+ fiber : Fiber
119
+ Fiber on which effective indices are evaluated.
120
+ families : sequence of str, optional
121
+ Mode families to search.
122
+ max_nu : int, optional
123
+ Largest azimuthal order to test.
124
+ max_m : int, optional
125
+ Largest radial order to test.
126
+
127
+ Returns
128
+ -------
129
+ tuple of Mode
130
+ Candidates with finite effective indices between the cladding and
131
+ maximum material indices.
132
+ """
58
133
  found = []
59
134
  for mode in candidate_modes(families, max_nu, max_m):
60
135
  try:
@@ -79,7 +154,40 @@ def sweep_modes(
79
154
  max_nu=6,
80
155
  max_m=6,
81
156
  ) -> ModeSweepResult:
82
- """Track modes over wavelength or any parameter handled by ``setter``."""
157
+ """Evaluate modal metrics over wavelength or another scalar parameter.
158
+
159
+ Parameters
160
+ ----------
161
+ fiber : Fiber
162
+ Fiber used as the immutable sweep template.
163
+ parameters : iterable of float
164
+ Ordered parameter values.
165
+ modes : sequence of Mode, optional
166
+ Modes to evaluate. When omitted, modes are discovered automatically.
167
+ parameter_name : str, optional
168
+ Name recorded in the returned result.
169
+ setter : callable, optional
170
+ Function accepting ``(fiber, value)`` for non-wavelength sweeps.
171
+ metrics : sequence of str, optional
172
+ Suffixes of ``Fiber.get_<metric>`` methods to evaluate.
173
+ families : sequence of str, optional
174
+ Families used during automatic discovery.
175
+ max_nu : int, optional
176
+ Maximum discovered azimuthal order.
177
+ max_m : int, optional
178
+ Maximum discovered radial order.
179
+
180
+ Returns
181
+ -------
182
+ ModeSweepResult
183
+ Dense arrays for each requested metric.
184
+
185
+ Raises
186
+ ------
187
+ ValueError
188
+ If ``parameters`` is empty or not one-dimensional, or when a custom
189
+ parameter is requested without a setter.
190
+ """
83
191
  parameters = np.asarray(tuple(parameters), dtype=float)
84
192
  if parameters.ndim != 1 or not len(parameters):
85
193
  raise ValueError("parameters must be a non-empty one-dimensional sequence")
@@ -89,6 +197,7 @@ def sweep_modes(
89
197
  raise ValueError("a setter is required for non-wavelength sweeps")
90
198
 
91
199
  def setter(current_fiber, value):
200
+ """Update wavelength for the default sweep behavior."""
92
201
  current_fiber.update_wavelength(value)
93
202
  if modes is None:
94
203
  discovered = []
@@ -1,3 +1,5 @@
1
+ """Cartesian and cylindrical sampling-grid definitions."""
2
+
1
3
  import numpy as np
2
4
  from dataclasses import dataclass
3
5
  from typing import Tuple
@@ -8,7 +10,7 @@ class CylindricalCoordinates:
8
10
  """
9
11
  Represents a set of points in cylindrical coordinates.
10
12
 
11
- Attributes
13
+ Parameters
12
14
  ----------
13
15
  rho : np.ndarray
14
16
  Radial distance from the z-axis.
@@ -51,7 +53,7 @@ class CartesianCoordinates:
51
53
  """
52
54
  Represents a set of points in cartesian coordinates.
53
55
 
54
- Attributes
56
+ Parameters
55
57
  ----------
56
58
  x : np.ndarray
57
59
  x-coordinates (must be a 1D array).
@@ -71,9 +73,10 @@ class CartesianCoordinates:
71
73
  is_3D: bool = False
72
74
 
73
75
  def __post_init__(self) -> None:
74
- self.x = self.x.astype(float)
75
- self.y = self.y.astype(float)
76
- self.z = self.z.astype(float)
76
+ """Convert all coordinate arrays to floating-point arrays."""
77
+ self.x = np.asarray(self.x, dtype=float)
78
+ self.y = np.asarray(self.y, dtype=float)
79
+ self.z = np.asarray(self.z, dtype=float)
77
80
 
78
81
  @property
79
82
  def x_boundaries(self) -> Tuple[float, float]:
@@ -116,7 +119,8 @@ class CartesianCoordinates:
116
119
  """
117
120
  if self.is_structured:
118
121
  x_min, x_max = self.x_boundaries
119
- return (x_max - x_min) / len(self.x)
122
+ unique_x = np.unique(self.x)
123
+ return float(unique_x[1] - unique_x[0])
120
124
  raise ValueError("dx value cannot be inferred from an unstructured mesh.")
121
125
 
122
126
  @property
@@ -136,7 +140,8 @@ class CartesianCoordinates:
136
140
  """
137
141
  if self.is_structured:
138
142
  y_min, y_max = self.y_boundaries
139
- return (y_max - y_min) / len(self.y)
143
+ unique_y = np.unique(self.y)
144
+ return float(unique_y[1] - unique_y[0])
140
145
  raise ValueError("dx value cannot be inferred from an unstructured mesh.")
141
146
 
142
147
  @classmethod
@@ -9,11 +9,6 @@ __all__ = [
9
9
  "DOCS_PATH",
10
10
  "EXAMPLES_PATH",
11
11
  "DOC_CSS_PATH",
12
- "root_path",
13
- "project_path",
14
- "examples_path",
15
- "doc_path",
16
- "doc_css_path",
17
12
  ]
18
13
 
19
14
  PACKAGE_PATH = Path(__file__).resolve().parent
@@ -21,10 +16,3 @@ PROJECT_PATH = PACKAGE_PATH.parent
21
16
  DOCS_PATH = PROJECT_PATH / "docs"
22
17
  EXAMPLES_PATH = DOCS_PATH / "examples"
23
18
  DOC_CSS_PATH = DOCS_PATH / "source" / "_static" / "default.css"
24
-
25
- # Backwards-compatible aliases for the original public names.
26
- root_path = PACKAGE_PATH
27
- project_path = PROJECT_PATH
28
- doc_path = DOCS_PATH
29
- examples_path = EXAMPLES_PATH
30
- doc_css_path = DOC_CSS_PATH
@@ -0,0 +1,17 @@
1
+ """Domain-specific exceptions raised by PyFiberModes."""
2
+
3
+
4
+ class PyFiberModesError(Exception):
5
+ """Base class for package-specific errors."""
6
+
7
+
8
+ class ValidationError(PyFiberModesError, ValueError):
9
+ """Indicate invalid physical geometry or solver configuration."""
10
+
11
+
12
+ class SolverError(PyFiberModesError, RuntimeError):
13
+ """Indicate that a modal solver could not complete normally."""
14
+
15
+
16
+ class ConvergenceError(SolverError):
17
+ """Indicate that a numerical solver did not converge to a valid root."""
@@ -1,5 +1,4 @@
1
- #!/usr/bin/env python
2
- # -*- coding: utf-8 -*-
1
+ """Factories for constructing parameterized multilayer fibers."""
3
2
 
4
3
  from itertools import product
5
4
  from dataclasses import dataclass, field
@@ -12,7 +11,7 @@ class ProxyLayer:
12
11
  """
13
12
  Represents a layer configuration in a fiber, with name, radius, and refractive index.
14
13
 
15
- Attributes
14
+ Parameters
16
15
  ----------
17
16
  name : str
18
17
  Name of the layer.
@@ -26,6 +25,7 @@ class ProxyLayer:
26
25
  index: list = field(default_factory=list)
27
26
 
28
27
  def __post_init__(self):
28
+ """Normalize scalar layer parameters into one-dimensional arrays."""
29
29
  self.name = [self.name]
30
30
  self.radius = np.atleast_1d(self.radius)
31
31
  self.index = np.atleast_1d(self.index)
@@ -64,6 +64,13 @@ class FiberFactory:
64
64
  """
65
65
 
66
66
  def __init__(self, wavelength: float):
67
+ """Initialize an empty fiber-configuration factory.
68
+
69
+ Parameters
70
+ ----------
71
+ wavelength : float
72
+ Vacuum wavelength in meters for generated fibers.
73
+ """
67
74
  self.layers_list = []
68
75
  self.neff_solver = None
69
76
  self.cutoff_solver = None