PyFiberModes 0.17.7__tar.gz → 1.0.0__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 (176) hide show
  1. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/.zenodo.json +1 -1
  2. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/CHANGELOG.md +6 -1
  3. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PKG-INFO +29 -14
  4. pyfibermodes-0.17.7/PyFiberModes/fiber.py → pyfibermodes-1.0.0/PyFiberModes/_fiber.py +2 -2
  5. pyfibermodes-0.17.7/PyFiberModes/field.py → pyfibermodes-1.0.0/PyFiberModes/_field.py +2 -2
  6. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/_version.py +2 -2
  7. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/analysis.py +45 -19
  8. pyfibermodes-1.0.0/PyFiberModes/coordinates.py +153 -0
  9. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/factory.py +14 -13
  10. pyfibermodes-1.0.0/PyFiberModes/fiber.py +447 -0
  11. pyfibermodes-1.0.0/PyFiberModes/field.py +181 -0
  12. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/fundamentals.py +1 -3
  13. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/loader.py +2 -1
  14. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/materials.py +43 -22
  15. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/models.py +9 -6
  16. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/optimization.py +39 -11
  17. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/plotting.py +1 -1
  18. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/propagation.py +36 -10
  19. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/services.py +1 -1
  20. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/solver/base_solver.py +4 -2
  21. pyfibermodes-1.0.0/PyFiberModes/source.py +100 -0
  22. pyfibermodes-1.0.0/PyFiberModes/units.py +67 -0
  23. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes.egg-info/PKG-INFO +29 -14
  24. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes.egg-info/SOURCES.txt +5 -0
  25. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes.egg-info/requires.txt +2 -0
  26. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/README.rst +26 -13
  27. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/benchmarks/test_field_performance.py +6 -5
  28. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/conda.recipe/meta.yaml +3 -1
  29. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/SMF28/plot_smf28.py +2 -1
  30. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/SMF28/plot_smf28_dispersion_vs_wavelength.py +2 -1
  31. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/SMF28/plot_smf28_group_index_vs_wavelength.py +2 -1
  32. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/analysis/plot_coupled_mode_exchange.py +5 -4
  33. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/analysis/plot_guided_mode_discovery.py +2 -1
  34. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/analysis/plot_structured_wavelength_sweep.py +5 -4
  35. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/basic/plot_U_vs_V.py +2 -1
  36. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/basic/plot_comparison_solvers.py +3 -2
  37. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/basic/plot_dispersion.py +6 -5
  38. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/basic/plot_effective_index.py +4 -3
  39. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/basic/plot_fiber_LP_modes.py +4 -3
  40. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/basic/plot_group_index.py +4 -3
  41. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/basic/plot_mode_field.py +4 -3
  42. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/basic/plot_mode_field_1.py +5 -4
  43. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/basic/plot_mode_field_2.py +3 -2
  44. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/basic/plot_neff_tapered_fiber.py +4 -3
  45. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/benchmarks/plot_component_batching_speedup.py +4 -3
  46. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/benchmarks/plot_field_grid_scaling.py +3 -2
  47. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/benchmarks/plot_modal_cache_speedup.py +2 -1
  48. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/fields/plot_confinement_by_radius.py +5 -4
  49. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/fields/plot_polarization_rotation.py +4 -3
  50. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/fields/plot_vector_components.py +4 -3
  51. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/specialty_fibers/plot_refractive_index_profile.py +4 -3
  52. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/specialty_fibers/plot_specialty_fiber_catalog.py +2 -1
  53. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/specialty_fibers/plot_specialty_mode_indices.py +2 -1
  54. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/validation/plot_effective_index_bounds.py +3 -2
  55. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/validation/plot_field_area_convergence.py +4 -3
  56. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/validation/plot_normalized_effective_index.py +3 -2
  57. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/getting_started.rst +9 -5
  58. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/migration_guide.rst +40 -3
  59. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/public_api_contract.rst +15 -5
  60. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/user_guide/fibers.rst +14 -9
  61. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/user_guide/fields.rst +10 -4
  62. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/user_guide/modes.rst +7 -1
  63. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/user_guide/sweeps.rst +17 -9
  64. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/pyproject.toml +3 -1
  65. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/tests/api_snapshot.json +2 -2
  66. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/tests/test_advanced_features.py +11 -10
  67. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/tests/test_api.py +7 -5
  68. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/tests/test_architecture_enhancements.py +9 -8
  69. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/tests/test_coordinates.py +28 -27
  70. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/tests/test_fiber.py +7 -5
  71. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/tests/test_field.py +2 -2
  72. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/tests/test_file_loading.py +3 -2
  73. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/tests/test_physics_regression.py +6 -5
  74. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/tests/test_three_layer_solver.py +4 -2
  75. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/tests/test_two_layer_solver.py +3 -1
  76. pyfibermodes-1.0.0/tests/test_units.py +247 -0
  77. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/tests/test_validation.py +7 -5
  78. pyfibermodes-0.17.7/PyFiberModes/source.py +0 -81
  79. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/.coveragerc +0 -0
  80. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/.github/dependabot.yml +0 -0
  81. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/.github/workflows/deploy_coverage.yml +0 -0
  82. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/.github/workflows/deploy_documentation.yml +0 -0
  83. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/.github/workflows/deploy_release.yml +0 -0
  84. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/.github/workflows/quality.yml +0 -0
  85. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/.github/workflows/tests.yml +0 -0
  86. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/.gitignore +0 -0
  87. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/CITATION.cff +0 -0
  88. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/CONTRIBUTING.md +0 -0
  89. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/LICENSE +0 -0
  90. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/MANIFEST.in +0 -0
  91. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/Makefile +0 -0
  92. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/__init__.py +0 -0
  93. /pyfibermodes-0.17.7/PyFiberModes/coordinates.py → /pyfibermodes-1.0.0/PyFiberModes/_coordinates.py +0 -0
  94. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/directories.py +0 -0
  95. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/exceptions.py +0 -0
  96. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/fiber_files/1550BHP.yaml +0 -0
  97. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/fiber_files/DCF13.yaml +0 -0
  98. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/fiber_files/DCF1300S_20.yaml +0 -0
  99. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/fiber_files/DCF1300S_26.yaml +0 -0
  100. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/fiber_files/DCF1300S_33.yaml +0 -0
  101. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/fiber_files/DCF1300S_42.yaml +0 -0
  102. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/fiber_files/F2028M12.yaml +0 -0
  103. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/fiber_files/F2028M21.yaml +0 -0
  104. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/fiber_files/F2028M24.yaml +0 -0
  105. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/fiber_files/F2058G1.yaml +0 -0
  106. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/fiber_files/F2058L1.yaml +0 -0
  107. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/fiber_files/HI1060.yaml +0 -0
  108. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/fiber_files/HP630.yaml +0 -0
  109. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/fiber_files/SM1950.yaml +0 -0
  110. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/fiber_files/SMF28.yaml +0 -0
  111. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/fiber_files/fluorine_doped_1%_capillary.yaml +0 -0
  112. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/fiber_files/fluorine_doped_2%_capillary.yaml +0 -0
  113. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/fiber_files/test_fiber.yaml +0 -0
  114. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/fiber_files/test_multimode_fiber.yaml +0 -0
  115. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/mode.py +0 -0
  116. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/mode_instances.py +0 -0
  117. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/solver/__init__.py +0 -0
  118. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/solver/multilayer/__init__.py +0 -0
  119. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/solver/multilayer/effective_index.py +0 -0
  120. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/solver/protocols.py +0 -0
  121. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/solver/results.py +0 -0
  122. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/solver/three_layer/__init__.py +0 -0
  123. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/solver/three_layer/cutoff.py +0 -0
  124. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/solver/two_layer/__init__.py +0 -0
  125. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/solver/two_layer/cutoff.py +0 -0
  126. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/solver/two_layer/effective_index.py +0 -0
  127. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes/stepindex.py +0 -0
  128. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes.egg-info/dependency_links.txt +0 -0
  129. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/PyFiberModes.egg-info/top_level.txt +0 -0
  130. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/benchmarks/README.md +0 -0
  131. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/Makefile +0 -0
  132. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/README.rst +0 -0
  133. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/SMF28/README.rst +0 -0
  134. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/analysis/README.rst +0 -0
  135. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/basic/README.rst +0 -0
  136. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/benchmarks/README.rst +0 -0
  137. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/fields/README.rst +0 -0
  138. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/specialty_fibers/README.rst +0 -0
  139. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/examples/validation/README.rst +0 -0
  140. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/make.bat +0 -0
  141. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/redirect.html +0 -0
  142. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/_static/default.css +0 -0
  143. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/_static/favicon.png +0 -0
  144. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/_static/logo.png +0 -0
  145. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/_static/thumbnail.png +0 -0
  146. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/api_policy.rst +0 -0
  147. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/architecture.rst +0 -0
  148. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/code/analysis.rst +0 -0
  149. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/code/fibers.rst +0 -0
  150. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/code/fields.rst +0 -0
  151. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/code/fundamentals.rst +0 -0
  152. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/code.rst +0 -0
  153. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/conf.py +0 -0
  154. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/examples.rst +0 -0
  155. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/guide.rst +0 -0
  156. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/index.rst +0 -0
  157. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/performance.rst +0 -0
  158. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/references.rst +0 -0
  159. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/resources.rst +0 -0
  160. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/theory.rst +0 -0
  161. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/troubleshooting.rst +0 -0
  162. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/user_guide/index.rst +0 -0
  163. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/user_guide/reproducibility.rst +0 -0
  164. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/user_guide/units.rst +0 -0
  165. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/docs/source/workflows.rst +0 -0
  166. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/pytest.ini +0 -0
  167. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/setup.cfg +0 -0
  168. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/tests/__init__.py +0 -0
  169. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/tests/helpers.py +0 -0
  170. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/tests/test_architecture.py +0 -0
  171. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/tests/test_docstrings.py +0 -0
  172. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/tests/test_public_api.py +0 -0
  173. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/tests/test_release_tools.py +0 -0
  174. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/tools/check_release.py +0 -0
  175. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/tools/next_release_version.py +0 -0
  176. {pyfibermodes-0.17.7 → pyfibermodes-1.0.0}/tools/release_tag.py +0 -0
@@ -9,7 +9,7 @@
9
9
  "license": "MIT",
10
10
  "upload_type": "software",
11
11
  "access_right": "open",
12
- "version": "0.17.7",
12
+ "version": "1.0.0",
13
13
  "publication_date": "2026-10-23",
14
14
  "keywords": [
15
15
  "optical fibers",
@@ -6,6 +6,10 @@ use [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.0.0] - 2026-10-23
10
+
11
+ - Require TypedUnit/Pint quantities for physical inputs and return unit-bearing fiber, field, sweep, material, design and propagation results. Numerical solvers continue to operate in SI internally.
12
+
9
13
  ## [0.17.7] - 2026-10-23
10
14
 
11
15
  ## [0.17.6] - 2026-10-22
@@ -175,7 +179,8 @@ use [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
175
179
 
176
180
  - Established the current tagged baseline for subsequent documented releases.
177
181
 
178
- [Unreleased]: https://github.com/MartinPdeS/PyFiberModes/compare/v0.17.7...HEAD
182
+ [Unreleased]: https://github.com/MartinPdeS/PyFiberModes/compare/v1.0.0...HEAD
183
+ [1.0.0]: https://github.com/MartinPdeS/PyFiberModes/releases/tag/v1.0.0
179
184
  [0.17.7]: https://github.com/MartinPdeS/PyFiberModes/releases/tag/v0.17.7
180
185
  [0.17.6]: https://github.com/MartinPdeS/PyFiberModes/releases/tag/v0.17.6
181
186
  [0.17.5]: https://github.com/MartinPdeS/PyFiberModes/releases/tag/v0.17.5
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyFiberModes
3
- Version: 0.17.7
3
+ Version: 1.0.0
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
@@ -699,6 +699,8 @@ Requires-Python: >=3.11
699
699
  Description-Content-Type: text/x-rst
700
700
  License-File: LICENSE
701
701
  Requires-Dist: PyFinitDiff
702
+ Requires-Dist: TypedUnit>=0.2.9
703
+ Requires-Dist: pint<0.26,>=0.24
702
704
  Requires-Dist: pyyaml
703
705
  Requires-Dist: numpy
704
706
  Requires-Dist: scipy
@@ -818,22 +820,25 @@ First mode calculation
818
820
  ----------------------
819
821
 
820
822
  Load the bundled SMF-28 definition at 1550 nm and solve its fundamental LP
821
- mode. Lengths and wavelengths are expressed in SI units.
823
+ mode. Physical inputs require Pint quantities; lengths may use any compatible unit.
824
+ Results also carry units, using the shared TypedUnit registry as in PyMieSim.
822
825
 
823
826
  .. code-block:: python
824
827
 
828
+ from PyFiberModes.units import ureg
829
+
825
830
  from PyFiberModes import LP01
826
831
  from PyFiberModes.fiber import load_fiber
827
832
 
828
- fiber = load_fiber("SMF28", wavelength=1550e-9)
833
+ fiber = load_fiber("SMF28", wavelength=1550e-9 * ureg.meter)
829
834
 
830
835
  effective_index = fiber.get_effective_index(LP01)
831
836
  propagation_constant = fiber.get_propagation_constant(LP01)
832
837
  dispersion = fiber.get_dispersion(LP01)
833
838
 
834
839
  print(f"n_eff = {effective_index:.9f}")
835
- print(f"beta = {propagation_constant:.6e} rad/m")
836
- print(f"D = {dispersion:.3f} ps/(nm km)")
840
+ print(f"beta = {propagation_constant:.6e}")
841
+ print(f"D = {dispersion:.3f}")
837
842
 
838
843
  ``load_fiber`` accepts the stem of any YAML definition in
839
844
  ``PyFiberModes/fiber_files``. A fiber can also be assembled programmatically
@@ -848,6 +853,8 @@ fiber index:
848
853
 
849
854
  .. code-block:: python
850
855
 
856
+ from PyFiberModes.units import ureg
857
+
851
858
  modes = fiber.find_modes(
852
859
  families=("LP",),
853
860
  max_nu=4,
@@ -869,9 +876,11 @@ values and whose columns correspond to modes.
869
876
 
870
877
  .. code-block:: python
871
878
 
879
+ from PyFiberModes.units import ureg
880
+
872
881
  import numpy as np
873
882
 
874
- wavelengths = np.linspace(1260e-9, 1650e-9, 101)
883
+ wavelengths = np.linspace(1260e-9, 1650e-9, 101) * ureg.meter
875
884
  result = fiber.sweep(
876
885
  wavelengths,
877
886
  modes=modes,
@@ -892,16 +901,18 @@ Create a two-dimensional field grid from a solved mode:
892
901
 
893
902
  .. code-block:: python
894
903
 
904
+ from PyFiberModes.units import ureg
905
+
895
906
  field = fiber.get_mode_field(
896
907
  mode=LP01,
897
- limit=20e-6,
908
+ limit=20e-6 * ureg.meter,
898
909
  n_point=201,
899
910
  )
900
911
 
901
912
  electric_x = field.Ex()
902
913
  intensity = field.Emod() ** 2
903
914
  effective_area = field.get_effective_area()
904
- confinement = field.get_confinement_factor(radius=4.1e-6)
915
+ confinement = field.get_confinement_factor(radius=4.1e-6 * ureg.meter)
905
916
  power = field.get_power()
906
917
  sx, sy, sz = field.get_poynting_vector()
907
918
 
@@ -921,17 +932,19 @@ scalar value to minimize.
921
932
 
922
933
  .. code-block:: python
923
934
 
935
+ from PyFiberModes.units import ureg
936
+
924
937
  from PyFiberModes import DesignParameter, optimize_fiber
925
938
 
926
939
  core_radius = DesignParameter.layer_radius(
927
940
  layer=0,
928
- bounds=(3.0e-6, 6.0e-6),
941
+ bounds=(3.0e-6 * ureg.meter, 6.0e-6 * ureg.meter),
929
942
  )
930
943
 
931
944
  design = optimize_fiber(
932
945
  fiber,
933
946
  parameters=[core_radius],
934
- objective=lambda candidate: abs(candidate.get_dispersion(LP01)),
947
+ objective=lambda candidate: abs(candidate.get_dispersion(LP01).to("ps / nm / km").magnitude),
935
948
  constraints=(
936
949
  lambda candidate: candidate.get_effective_index(LP01)
937
950
  - candidate.last_layer.refractive_index,
@@ -956,10 +969,12 @@ electric field. Modal amplitudes then evolve according to
956
969
 
957
970
  .. code-block:: python
958
971
 
972
+ from PyFiberModes.units import ureg
973
+
959
974
  import numpy as np
960
975
  from PyFiberModes import CoupledModeSystem
961
976
 
962
- multimode_fiber = load_fiber("test_multimode_fiber", wavelength=1550e-9)
977
+ multimode_fiber = load_fiber("test_multimode_fiber", wavelength=1550e-9 * ureg.meter)
963
978
  multimode_modes = multimode_fiber.find_modes(
964
979
  families=("LP",),
965
980
  max_nu=3,
@@ -968,16 +983,16 @@ electric field. Modal amplitudes then evolve according to
968
983
  selected_modes = multimode_modes[:2]
969
984
  fields = [multimode_fiber.get_mode_field(mode, n_point=151) for mode in selected_modes]
970
985
  betas = [multimode_fiber.get_propagation_constant(mode) for mode in selected_modes]
971
- relative_betas = np.asarray(betas) - betas[0]
986
+ relative_betas = ureg.Quantity.from_list(betas) - betas[0]
972
987
  mode_overlap = fields[0].overlap(fields[1])
973
988
 
974
989
  coupling = np.array([
975
990
  [0.0, 0.25],
976
991
  [0.25, 0.0],
977
- ])
992
+ ]) / ureg.meter
978
993
  system = CoupledModeSystem(betas=relative_betas, coupling=coupling)
979
994
 
980
- z = np.linspace(0, 20, 500)
995
+ z = np.linspace(0, 20, 500) * ureg.meter
981
996
  propagation = system.propagate(initial=[1.0, 0.0], z=z)
982
997
  modal_power = propagation.powers
983
998
 
@@ -12,7 +12,7 @@ from scipy.constants import c
12
12
  from PyFiberModes.stepindex import StepIndex
13
13
  from PyFiberModes import Mode
14
14
  from PyFinitDiff.finite_difference_1D import get_function_derivative
15
- from PyFiberModes.field import Field
15
+ from PyFiberModes._field import Field
16
16
 
17
17
  from PyFiberModes.fundamentals import (
18
18
  get_radial_field,
@@ -20,7 +20,7 @@ from PyFiberModes.fundamentals import (
20
20
  )
21
21
 
22
22
  from PyFiberModes import loader
23
- from PyFiberModes.coordinates import CylindricalCoordinates
23
+ from PyFiberModes._coordinates import CylindricalCoordinates
24
24
  from PyFiberModes.exceptions import ConvergenceError, ValidationError
25
25
  from PyFiberModes.materials import DEFAULT_MATERIALS, MaterialRegistry
26
26
  from PyFiberModes.services import FieldAnalysis, ModalAnalysis
@@ -7,8 +7,8 @@ import numpy as np
7
7
  from PyFiberModes.mode_instances import HE11
8
8
  from PyFiberModes.mode import Mode
9
9
  from dataclasses import dataclass
10
- from PyFiberModes.coordinates import CartesianCoordinates
11
- from PyFiberModes.coordinates import CylindricalCoordinates as CylindricalCoordinates # noqa: F401
10
+ from PyFiberModes._coordinates import CartesianCoordinates
11
+ from PyFiberModes._coordinates import CylindricalCoordinates as CylindricalCoordinates # noqa: F401
12
12
 
13
13
 
14
14
  @dataclass
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.17.7'
32
- __version_tuple__ = version_tuple = (0, 17, 7)
31
+ __version__ = version = '1.0.0'
32
+ __version_tuple__ = version_tuple = (1, 0, 0)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -8,6 +8,7 @@ import numpy as np
8
8
 
9
9
  from PyFiberModes.exceptions import ConvergenceError, ValidationError
10
10
  from PyFiberModes.mode import Mode
11
+ from PyFiberModes.units import MODAL_UNITS, Quantity, magnitude, quantity
11
12
 
12
13
 
13
14
  @dataclass(frozen=True)
@@ -30,9 +31,9 @@ class ModeSweepResult:
30
31
  """
31
32
 
32
33
  parameter_name: str
33
- parameters: np.ndarray
34
+ parameters: np.ndarray | Quantity
34
35
  modes: tuple[Mode, ...]
35
- values: dict[str, np.ndarray]
36
+ values: dict[str, np.ndarray | Quantity]
36
37
 
37
38
  def __getitem__(self, metric: str) -> np.ndarray:
38
39
  """Return the array associated with a metric name.
@@ -74,9 +75,11 @@ class ModeSweepResult:
74
75
  One record for every parameter and mode combination.
75
76
  """
76
77
  return [
77
- {self.parameter_name: float(parameter), "mode": mode, **{
78
- name: float(array[i, j]) for name, array in self.values.items()
79
- }}
78
+ {
79
+ self.parameter_name: parameter,
80
+ "mode": mode,
81
+ **{name: array[i, j] for name, array in self.values.items()},
82
+ }
80
83
  for i, parameter in enumerate(self.parameters)
81
84
  for j, mode in enumerate(self.modes)
82
85
  ]
@@ -137,11 +140,7 @@ def find_modes(fiber, families=("LP",), max_nu=6, max_m=6) -> tuple[Mode, ...]:
137
140
  for mode in candidate_modes(families, max_nu, max_m):
138
141
  try:
139
142
  effective_index = fiber.get_effective_index(mode)
140
- guided = (
141
- fiber.last_layer.refractive_index
142
- < effective_index
143
- <= fiber.maximum_index
144
- )
143
+ guided = fiber.last_layer.refractive_index < effective_index <= fiber.maximum_index
145
144
  if np.isfinite(effective_index) and guided:
146
145
  found.append(mode)
147
146
  except ConvergenceError:
@@ -167,8 +166,8 @@ def sweep_modes(
167
166
  ----------
168
167
  fiber : Fiber
169
168
  Fiber used as the immutable sweep template.
170
- parameters : iterable of float
171
- Ordered parameter values.
169
+ parameters : pint.Quantity or iterable of pint.Quantity
170
+ Ordered parameter values with explicit units for public fibers.
172
171
  modes : sequence of Mode, optional
173
172
  Modes to evaluate. When omitted, modes are discovered automatically.
174
173
  parameter_name : str, optional
@@ -195,7 +194,26 @@ def sweep_modes(
195
194
  If ``parameters`` is empty or not one-dimensional, or when a custom
196
195
  parameter is requested without a setter.
197
196
  """
198
- parameters = np.asarray(tuple(parameters), dtype=float)
197
+ unit_aware = hasattr(fiber, "_si")
198
+ parameter_unit = None
199
+ if unit_aware:
200
+ if isinstance(parameters, Quantity):
201
+ parameter_unit = parameters.units
202
+ if setter is None:
203
+ magnitude(parameters, "meter", "parameters")
204
+ parameters = np.asarray(parameters.magnitude, dtype=float)
205
+ else:
206
+ items = tuple(parameters)
207
+ if not items:
208
+ raise ValueError("parameters must be a non-empty one-dimensional sequence")
209
+ parameter_unit = items[0].units if isinstance(items[0], Quantity) else "meter"
210
+ parameters = np.asarray(
211
+ [magnitude(item, parameter_unit, "parameters") for item in items], dtype=float
212
+ )
213
+ if setter is None:
214
+ quantity(parameters, parameter_unit).to("meter")
215
+ else:
216
+ parameters = np.asarray(tuple(parameters), dtype=float)
199
217
  if parameters.ndim != 1 or not len(parameters):
200
218
  raise ValueError("parameters must be a non-empty one-dimensional sequence")
201
219
  if setter is None:
@@ -205,11 +223,14 @@ def sweep_modes(
205
223
  def setter(current_fiber, value):
206
224
  """Update wavelength for the default sweep behavior."""
207
225
  current_fiber.update_wavelength(value)
226
+
208
227
  if modes is None:
209
228
  discovered = []
210
229
  for parameter in parameters:
211
230
  parameter_fiber = deepcopy(fiber)
212
- setter(parameter_fiber, float(parameter))
231
+ setter(
232
+ parameter_fiber, quantity(parameter, parameter_unit) if unit_aware else parameter
233
+ )
213
234
  for mode in find_modes(parameter_fiber, families, max_nu, max_m):
214
235
  if mode not in discovered:
215
236
  discovered.append(mode)
@@ -218,17 +239,22 @@ def sweep_modes(
218
239
  values = {metric: np.full((len(parameters), len(modes)), np.nan) for metric in metrics}
219
240
  parameter_fibers = tuple(deepcopy(fiber) for _ in parameters)
220
241
  for parameter_fiber, parameter in zip(parameter_fibers, parameters, strict=True):
221
- setter(parameter_fiber, float(parameter))
242
+ setter(parameter_fiber, quantity(parameter, parameter_unit) if unit_aware else parameter)
222
243
 
223
244
  for i, parameter_fiber in enumerate(parameter_fibers):
224
- metric_functions = {
225
- metric: getattr(parameter_fiber, f"get_{metric}") for metric in metrics
226
- }
245
+ metric_functions = {metric: getattr(parameter_fiber, f"get_{metric}") for metric in metrics}
227
246
  for metric, metric_function in metric_functions.items():
228
247
  for j, mode in enumerate(modes):
229
248
  try:
230
249
  value = metric_function(mode)
231
- values[metric][i, j] = value if np.isfinite(value) else np.nan
250
+ values[metric][i, j] = (
251
+ (value.to(MODAL_UNITS[metric]).magnitude if unit_aware else value)
252
+ if np.isfinite(value)
253
+ else np.nan
254
+ )
232
255
  except ConvergenceError:
233
256
  pass
257
+ if unit_aware:
258
+ parameters = quantity(parameters, parameter_unit)
259
+ values = {metric: quantity(array, MODAL_UNITS[metric]) for metric, array in values.items()}
234
260
  return ModeSweepResult(parameter_name, parameters, modes, values)
@@ -0,0 +1,153 @@
1
+ """Length-valued Cartesian and cylindrical coordinate grids."""
2
+
3
+ from dataclasses import dataclass
4
+ import numpy as np
5
+
6
+ from PyFiberModes import _coordinates
7
+ from PyFiberModes.units import Angle, Length, magnitude, quantity
8
+
9
+
10
+ @dataclass
11
+ class CylindricalCoordinates:
12
+ """Cylindrical positions with length-valued rho/z and angle-valued phi."""
13
+
14
+ rho: Length
15
+ phi: Angle
16
+ z: Length
17
+
18
+ def __post_init__(self):
19
+ """Validate and normalize coordinate quantities."""
20
+ self.rho = quantity(magnitude(self.rho, "meter", "rho"), "meter")
21
+ self.phi = quantity(magnitude(self.phi, "radian", "phi"), "radian")
22
+ self.z = quantity(magnitude(self.z, "meter", "z"), "meter")
23
+
24
+ def to_cartesian(self):
25
+ """Convert cylindrical positions to Cartesian positions."""
26
+ return CartesianCoordinates(
27
+ x=self.rho * np.cos(self.phi),
28
+ y=self.rho * np.sin(self.phi),
29
+ z=self.z,
30
+ )
31
+
32
+ def to_cylindrical(self):
33
+ """Return these cylindrical positions."""
34
+ return self
35
+
36
+
37
+ @dataclass
38
+ class CartesianCoordinates:
39
+ """Cartesian positions with explicit length units on every axis."""
40
+
41
+ x: Length
42
+ y: Length
43
+ z: Length
44
+ is_structured: bool = False
45
+ is_3D: bool = False
46
+
47
+ def __post_init__(self):
48
+ """Validate lengths and normalize numerical coordinate arrays to meters."""
49
+ for name in ("x", "y", "z"):
50
+ setattr(
51
+ self,
52
+ name,
53
+ quantity(
54
+ np.asarray(magnitude(getattr(self, name), "meter", name), dtype=float), "meter"
55
+ ),
56
+ )
57
+
58
+ @classmethod
59
+ def _from_si(cls, coordinates):
60
+ """Attach length units to a numerical coordinate grid."""
61
+ return cls(
62
+ x=quantity(coordinates.x, "meter"),
63
+ y=quantity(coordinates.y, "meter"),
64
+ z=quantity(coordinates.z, "meter"),
65
+ is_structured=coordinates.is_structured,
66
+ is_3D=coordinates.is_3D,
67
+ )
68
+
69
+ @property
70
+ def x_boundaries(self):
71
+ """Minimum and maximum x positions."""
72
+ return self.x.min(), self.x.max()
73
+
74
+ @property
75
+ def y_boundaries(self):
76
+ """Minimum and maximum y positions."""
77
+ return self.y.min(), self.y.max()
78
+
79
+ @property
80
+ def dx(self):
81
+ """Structured grid spacing along x."""
82
+ if not self.is_structured:
83
+ raise ValueError("dx value cannot be inferred from an unstructured mesh")
84
+ return quantity(np.diff(np.unique(self.x.magnitude))[0], self.x.units)
85
+
86
+ @property
87
+ def dy(self):
88
+ """Structured grid spacing along y."""
89
+ if not self.is_structured:
90
+ raise ValueError("dy value cannot be inferred from an unstructured mesh")
91
+ return quantity(np.diff(np.unique(self.y.magnitude))[0], self.y.units)
92
+
93
+ @classmethod
94
+ def generate_from_square(cls, length: Length, center=None, n_points: int = 100):
95
+ """Generate a square grid with a length-valued side and center."""
96
+ center_si = (
97
+ (0.0, 0.0)
98
+ if center is None
99
+ else tuple(magnitude(item, "meter", "center") for item in center)
100
+ )
101
+ return cls._from_si(
102
+ _coordinates.CartesianCoordinates.generate_from_square(
103
+ length=magnitude(length, "meter", "length"),
104
+ center=center_si,
105
+ n_points=n_points,
106
+ )
107
+ )
108
+
109
+ @classmethod
110
+ def generate_from_boundaries(cls, x_limits, y_limits, z_limits, x_points, y_points, z_points):
111
+ """Generate a structured grid from length-valued axis limits."""
112
+ limits = [
113
+ tuple(magnitude(item, "meter", "limits") for item in axis)
114
+ for axis in (x_limits, y_limits, z_limits)
115
+ ]
116
+ return cls._from_si(
117
+ _coordinates.CartesianCoordinates.generate_from_boundaries(
118
+ x_limits=limits[0],
119
+ y_limits=limits[1],
120
+ z_limits=limits[2],
121
+ x_points=x_points,
122
+ y_points=y_points,
123
+ z_points=z_points,
124
+ )
125
+ )
126
+
127
+ def to_cylindrical(self):
128
+ """Convert positions to cylindrical coordinates."""
129
+ return CylindricalCoordinates(
130
+ rho=np.sqrt(self.x**2 + self.y**2),
131
+ phi=np.arctan2(self.y, self.x),
132
+ z=self.z,
133
+ )
134
+
135
+ def centering(self):
136
+ """Center each axis in place."""
137
+ self.x -= self.x.mean()
138
+ self.y -= self.y.mean()
139
+ self.z -= self.z.mean()
140
+
141
+ def shift_coordinates(self, shift):
142
+ """Shift the grid by three length-valued displacements."""
143
+ # Validate all inputs before mutating the grid.
144
+ values = [quantity(magnitude(item, "meter", "shift"), "meter") for item in shift]
145
+ self.x += values[0]
146
+ self.y += values[1]
147
+ self.z += values[2]
148
+
149
+ def scale_coordinates(self, scale):
150
+ """Scale axes by dimensionless factors."""
151
+ self.x *= float(scale[0])
152
+ self.y *= float(scale[1])
153
+ self.z *= float(scale[2])
@@ -4,6 +4,7 @@ from itertools import product
4
4
  from dataclasses import dataclass, field
5
5
  import numpy as np
6
6
  from PyFiberModes.fiber import Fiber
7
+ from PyFiberModes.units import Length, RefractiveIndex, magnitude, quantity, ureg
7
8
 
8
9
 
9
10
  @dataclass
@@ -15,9 +16,9 @@ class ProxyLayer:
15
16
  ----------
16
17
  name : str
17
18
  Name of the layer.
18
- radius : list[float]
19
+ radius : pint.Quantity
19
20
  Radius values for the layer (in meters).
20
- index : list[float]
21
+ index : pint.Quantity
21
22
  Refractive index values for the layer.
22
23
  """
23
24
  name: str
@@ -27,8 +28,8 @@ class ProxyLayer:
27
28
  def __post_init__(self):
28
29
  """Normalize scalar layer parameters into one-dimensional arrays."""
29
30
  self.name = [self.name]
30
- self.radius = np.atleast_1d(self.radius)
31
- self.index = np.atleast_1d(self.index)
31
+ self.radius = quantity(np.atleast_1d(magnitude(self.radius, "meter", "radius")), "meter")
32
+ self.index = quantity(np.atleast_1d(magnitude(self.index, "dimensionless", "index")), "dimensionless")
32
33
 
33
34
  def get_generator(self):
34
35
  """
@@ -48,40 +49,40 @@ class FiberFactory:
48
49
 
49
50
  Parameters
50
51
  ----------
51
- wavelength : float
52
+ wavelength : TypedUnit.Length
52
53
  Wavelength (in meters) used for fiber simulations.
53
54
 
54
55
  Attributes
55
56
  ----------
56
57
  layers_list : list[ProxyLayer]
57
58
  List of ProxyLayer objects representing the layers of the fiber.
58
- wavelength : float
59
+ wavelength : TypedUnit.Length
59
60
  Wavelength used in simulations.
60
61
  """
61
62
 
62
- def __init__(self, wavelength: float):
63
+ def __init__(self, wavelength: Length):
63
64
  """Initialize an empty fiber-configuration factory.
64
65
 
65
66
  Parameters
66
67
  ----------
67
- wavelength : float
68
+ wavelength : TypedUnit.Length
68
69
  Vacuum wavelength in meters for generated fibers.
69
70
  """
70
71
  self.layers_list = []
71
- self.wavelength = wavelength
72
+ self.wavelength = quantity(magnitude(wavelength, "meter", "wavelength"), "meter")
72
73
 
73
- def add_layer(self, index: float, name: str = "", radius: float = 0.0):
74
+ def add_layer(self, index: RefractiveIndex, name: str = "", radius: Length = np.inf * ureg.meter):
74
75
  """
75
76
  Add a new layer to the fiber factory.
76
77
 
77
78
  Parameters
78
79
  ----------
79
- index : float
80
+ index : TypedUnit.RefractiveIndex
80
81
  Refractive index of the layer.
81
82
  name : str, optional
82
83
  Name of the layer (default is an empty string).
83
- radius : float, optional
84
- Radius of the layer (default is 0.0 meters).
84
+ radius : TypedUnit.Length, optional
85
+ Radius of the layer (defaults to an unbounded outer layer).
85
86
  """
86
87
  layer = ProxyLayer(name=name, radius=radius, index=index)
87
88
  self.layers_list.append(layer)