miepython 3.1.0__tar.gz → 3.2.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 (45) hide show
  1. {miepython-3.1.0 → miepython-3.2.0}/CHANGELOG.rst +6 -1
  2. {miepython-3.1.0 → miepython-3.2.0}/CITATION.cff +3 -3
  3. {miepython-3.1.0 → miepython-3.2.0}/PKG-INFO +13 -11
  4. {miepython-3.1.0 → miepython-3.2.0}/README.rst +3 -3
  5. {miepython-3.1.0 → miepython-3.2.0}/miepython/__init__.py +53 -41
  6. miepython-3.2.0/miepython/_backend.py +49 -0
  7. {miepython-3.1.0 → miepython-3.2.0}/miepython/core.py +1 -2
  8. {miepython-3.1.0 → miepython-3.2.0}/miepython/field.py +21 -20
  9. {miepython-3.1.0 → miepython-3.2.0}/miepython/vsh.py +2 -2
  10. {miepython-3.1.0 → miepython-3.2.0}/miepython.egg-info/PKG-INFO +13 -11
  11. {miepython-3.1.0 → miepython-3.2.0}/miepython.egg-info/SOURCES.txt +1 -0
  12. {miepython-3.1.0 → miepython-3.2.0}/miepython.egg-info/requires.txt +6 -5
  13. {miepython-3.1.0 → miepython-3.2.0}/pyproject.toml +13 -9
  14. {miepython-3.1.0 → miepython-3.2.0}/tests/test_all_examples.py +1 -1
  15. miepython-3.2.0/tests/test_bessel.py +201 -0
  16. {miepython-3.1.0 → miepython-3.2.0}/tests/test_field.py +69 -2
  17. miepython-3.2.0/tests/test_jit.py +596 -0
  18. {miepython-3.1.0 → miepython-3.2.0}/tests/test_jit_D.py +58 -54
  19. {miepython-3.1.0 → miepython-3.2.0}/tests/test_jit_abcd.py +164 -145
  20. {miepython-3.1.0 → miepython-3.2.0}/tests/test_mie_backend_speed.py +4 -0
  21. miepython-3.2.0/tests/test_nojit.py +602 -0
  22. {miepython-3.1.0 → miepython-3.2.0}/tests/test_nojit_D.py +58 -54
  23. {miepython-3.1.0 → miepython-3.2.0}/tests/test_nojit_abcd.py +164 -145
  24. {miepython-3.1.0 → miepython-3.2.0}/tests/test_rayleigh.py +0 -4
  25. {miepython-3.1.0 → miepython-3.2.0}/tests/test_vsh.py +3 -3
  26. miepython-3.1.0/tests/test_bessel.py +0 -188
  27. miepython-3.1.0/tests/test_jit.py +0 -612
  28. miepython-3.1.0/tests/test_nojit.py +0 -622
  29. {miepython-3.1.0 → miepython-3.2.0}/LICENSE.txt +0 -0
  30. {miepython-3.1.0 → miepython-3.2.0}/MANIFEST.in +0 -0
  31. {miepython-3.1.0 → miepython-3.2.0}/miepython/bessel.py +0 -0
  32. {miepython-3.1.0 → miepython-3.2.0}/miepython/data/Johnson.txt +0 -0
  33. {miepython-3.1.0 → miepython-3.2.0}/miepython/data/segelstein81_index.txt +0 -0
  34. {miepython-3.1.0 → miepython-3.2.0}/miepython/mie_jit.py +0 -0
  35. {miepython-3.1.0 → miepython-3.2.0}/miepython/mie_nojit.py +0 -0
  36. {miepython-3.1.0 → miepython-3.2.0}/miepython/monte_carlo.py +0 -0
  37. {miepython-3.1.0 → miepython-3.2.0}/miepython/rayleigh.py +0 -0
  38. {miepython-3.1.0 → miepython-3.2.0}/miepython/util.py +0 -0
  39. {miepython-3.1.0 → miepython-3.2.0}/miepython.egg-info/dependency_links.txt +0 -0
  40. {miepython-3.1.0 → miepython-3.2.0}/miepython.egg-info/top_level.txt +0 -0
  41. {miepython-3.1.0 → miepython-3.2.0}/setup.cfg +0 -0
  42. {miepython-3.1.0 → miepython-3.2.0}/tests/test_all_notebooks.py +0 -0
  43. {miepython-3.1.0 → miepython-3.2.0}/tests/test_jit_speed.py +1 -1
  44. {miepython-3.1.0 → miepython-3.2.0}/tests/test_nojit_speed.py +1 -1
  45. {miepython-3.1.0 → miepython-3.2.0}/tests/test_unpolarized_intensity.py +0 -0
@@ -1,7 +1,12 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
- 3.0.6 (02/07/2026)
4
+ 3.2.0 (03/06/2026)
5
+ -------------------
6
+ * fix error in E & H calculations in the near field when y≠0 (thanks @dorianherle)
7
+ * add regression test
8
+
9
+ 3.1.0 (02/07/2026)
5
10
  -------------------
6
11
  * add near-field E and H field APIs, fix boundary continuity/medium handling, and expand validation tests
7
12
  * add field calculation utilities, field module cleanup/help text improvements, and precomputed E/H reference data (via scattnlay)
@@ -2,9 +2,9 @@ cff-version: 1.2.0
2
2
  type: software
3
3
  message: If you use this software, please cite it as below.
4
4
  title: 'miepython: Pure python calculation of Mie scattering'
5
- version: 3.0.5
5
+ version: 3.1.0
6
6
  doi: 10.5281/zenodo.7949264
7
- date-released: '2026-01-03'
7
+ date-released: '2026-02-08'
8
8
  url: https://doi.org/10.5281/zenodo.7949264
9
9
  repository-code: https://github.com/scottprahl/miepython
10
10
  license: MIT
@@ -33,7 +33,7 @@ preferred-citation:
33
33
  type: software
34
34
  title: 'miepython: Pure python calculation of Mie scattering'
35
35
  year: 2026
36
- version: 3.0.5
36
+ version: 3.1.0
37
37
  doi: 10.5281/zenodo.7949264
38
38
  url: https://doi.org/10.5281/zenodo.7949264
39
39
  authors:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: miepython
3
- Version: 3.1.0
3
+ Version: 3.2.0
4
4
  Summary: Mie scattering of a plane wave by a sphere
5
5
  Author-email: Scott Prahl <scott.prahl@oit.edu>
6
6
  License-Expression: MIT
@@ -14,11 +14,12 @@ Classifier: Development Status :: 5 - Production/Stable
14
14
  Classifier: Intended Audience :: Science/Research
15
15
  Classifier: Programming Language :: Python
16
16
  Classifier: Programming Language :: Python :: 3
17
- Classifier: Programming Language :: Python :: 3.9
18
17
  Classifier: Programming Language :: Python :: 3.10
19
18
  Classifier: Programming Language :: Python :: 3.11
20
19
  Classifier: Programming Language :: Python :: 3.12
21
- Requires-Python: >=3.9
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Requires-Python: >=3.10
22
23
  Description-Content-Type: text/x-rst
23
24
  License-File: LICENSE.txt
24
25
  Requires-Dist: numpy
@@ -26,8 +27,9 @@ Requires-Dist: matplotlib
26
27
  Requires-Dist: scipy
27
28
  Requires-Dist: numba; sys_platform != "emscripten"
28
29
  Provides-Extra: docs
29
- Requires-Dist: Sphinx==8.1.3; extra == "docs"
30
- Requires-Dist: nbsphinx==0.9.7; extra == "docs"
30
+ Requires-Dist: Sphinx>=8.1.3; extra == "docs"
31
+ Requires-Dist: nbsphinx>=0.9.7; extra == "docs"
32
+ Requires-Dist: ipython; extra == "docs"
31
33
  Requires-Dist: sphinx_automodapi; extra == "docs"
32
34
  Requires-Dist: sphinx_rtd_theme; extra == "docs"
33
35
  Provides-Extra: dev
@@ -36,7 +38,7 @@ Requires-Dist: imageio; extra == "dev"
36
38
  Requires-Dist: requests; extra == "dev"
37
39
  Requires-Dist: ruff; extra == "dev"
38
40
  Requires-Dist: yamllint; extra == "dev"
39
- Requires-Dist: rstcheck; extra == "dev"
41
+ Requires-Dist: rstcheck[sphinx,toml]; extra == "dev"
40
42
  Requires-Dist: pylint; extra == "dev"
41
43
  Requires-Dist: pyroma; extra == "dev"
42
44
  Requires-Dist: black[jupyter]; extra == "dev"
@@ -49,8 +51,8 @@ Requires-Dist: jupyterlab; extra == "dev"
49
51
  Requires-Dist: nbconvert; extra == "dev"
50
52
  Requires-Dist: nbformat; extra == "dev"
51
53
  Provides-Extra: lite
52
- Requires-Dist: jupyterlite-core==0.6.4; extra == "lite"
53
- Requires-Dist: jupyterlite-pyodide-kernel==0.6.1; extra == "lite"
54
+ Requires-Dist: jupyterlite-core<0.8,>=0.7; extra == "lite"
55
+ Requires-Dist: jupyterlite-pyodide-kernel<0.8,>=0.7; extra == "lite"
54
56
  Dynamic: license-file
55
57
 
56
58
  .. |pypi| image:: https://img.shields.io/pypi/v/miepython?color=68CA66
@@ -208,19 +210,19 @@ Citation
208
210
 
209
211
  If you use ``laserbeamsize`` in academic, instructional, or applied technical work, please cite:
210
212
 
211
- Prahl, S. (2026). *miepython: Pure python calculation of Mie scattering* (Version 3.0.5) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.7949263
213
+ Prahl, S. (2026). *miepython: Pure python calculation of Mie scattering* (Version 3.1.0) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.7949263
212
214
 
213
215
  BibTeX:
214
216
 
215
217
  .. code-block:: bibtex
216
218
 
217
- @software{prahl_miepython_2025,
219
+ @software{prahl_miepython_2026,
218
220
  author = {Scott Prahl},
219
221
  title = {{miepython}: A Python library for Mie scattering calculations},
220
222
  url = {https://github.com/scottprahl/miepython},
221
223
  doi = {10.5281/zenodo.7949263},
222
224
  year = {2026},
223
- version = {3.0.5},
225
+ version = {3.1.0},
224
226
  publisher = {Zenodo}
225
227
  }
226
228
 
@@ -153,19 +153,19 @@ Citation
153
153
 
154
154
  If you use ``laserbeamsize`` in academic, instructional, or applied technical work, please cite:
155
155
 
156
- Prahl, S. (2026). *miepython: Pure python calculation of Mie scattering* (Version 3.0.5) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.7949263
156
+ Prahl, S. (2026). *miepython: Pure python calculation of Mie scattering* (Version 3.1.0) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.7949263
157
157
 
158
158
  BibTeX:
159
159
 
160
160
  .. code-block:: bibtex
161
161
 
162
- @software{prahl_miepython_2025,
162
+ @software{prahl_miepython_2026,
163
163
  author = {Scott Prahl},
164
164
  title = {{miepython}: A Python library for Mie scattering calculations},
165
165
  url = {https://github.com/scottprahl/miepython},
166
166
  doi = {10.5281/zenodo.7949263},
167
167
  year = {2026},
168
- version = {3.0.5},
168
+ version = {3.1.0},
169
169
  publisher = {Zenodo}
170
170
  }
171
171
 
@@ -62,47 +62,58 @@ Example near-field usage::
62
62
 
63
63
  """
64
64
 
65
- import os
66
-
67
- USE_JIT = os.environ.get("MIEPYTHON_USE_JIT", "0") == "1"
68
-
69
- if USE_JIT:
70
- # pull in the jit‐compiled backends under the "private" names core.py expects
71
- from .mie_jit import _an_bn_nb as an_bn
72
- from .mie_jit import _cn_dn_nb as cn_dn
73
- from .mie_jit import _single_sphere_nb as single_sphere
74
- from .mie_jit import _small_sphere_nb as small_sphere
75
- from .mie_jit import _small_conducting_sphere_nb as small_conducting_sphere
76
- from .mie_jit import _pi_tau_nb as _pi_tau
77
- from .mie_jit import _D_calc_nb as _D_calc
78
- from .mie_jit import _S1_S2_nb as _S1_S2
79
- from .mie_jit import _Lentz_Dn
80
- from .mie_jit import _D_upwards
81
- from .mie_jit import _D_downwards
82
- else:
83
- from .mie_nojit import _an_bn_py as an_bn
84
- from .mie_nojit import _cn_dn_py as cn_dn
85
- from .mie_nojit import _single_sphere_py as single_sphere
86
- from .mie_nojit import _small_sphere_py as small_sphere
87
- from .mie_nojit import _small_conducting_sphere_py as small_conducting_sphere
88
- from .mie_nojit import _pi_tau_py as _pi_tau
89
- from .mie_nojit import _D_calc_py as _D_calc
90
- from .mie_nojit import _S1_S2_py as _S1_S2
91
- from .mie_nojit import _Lentz_Dn
92
- from .mie_nojit import _D_upwards
93
- from .mie_nojit import _D_downwards
65
+ from ._backend import D_calc, USE_JIT, _D_downwards, _D_upwards, _Lentz_Dn, _S1_S2, an_bn, cn_dn, pi_tau
66
+ from ._backend import single_sphere, small_conducting_sphere, small_sphere
94
67
 
95
68
  from .core import efficiencies, intensities, i_par, i_per, i_unpolarized
96
69
  from .core import efficiencies_mx, S1_S2, phase_matrix, coefficients
97
- from .field import (
98
- e_far,
99
- e_near,
100
- h_near,
101
- eh_near,
102
- e_near_cartesian,
103
- h_near_cartesian,
104
- eh_near_cartesian,
105
- )
70
+
71
+ _FIELD_IMPORT_ERROR = None
72
+
73
+
74
+ def _raise_field_import_error():
75
+ """Raise a consistent error when optional near-field dependencies are missing."""
76
+ raise ModuleNotFoundError(
77
+ "Near-field functions require the optional dependency 'scipy'. "
78
+ "Install miepython with SciPy support to use e_far/e_near/h_near/eh_near."
79
+ ) from _FIELD_IMPORT_ERROR
80
+
81
+
82
+ def _missing_field_function(name):
83
+ """Create a placeholder near-field function when SciPy is unavailable."""
84
+
85
+ def _missing(*_args, **_kwargs):
86
+ _raise_field_import_error()
87
+
88
+ _missing.__name__ = name
89
+ _missing.__qualname__ = name
90
+ _missing.__doc__ = f"{name} requires the optional dependency 'scipy'."
91
+ return _missing
92
+
93
+
94
+ try:
95
+ from . import field as _field
96
+ except ModuleNotFoundError as exc:
97
+ missing_module = str(getattr(exc, "name", "") or "")
98
+ if missing_module.startswith("scipy"):
99
+ _FIELD_IMPORT_ERROR = exc
100
+ e_far = _missing_field_function("e_far")
101
+ e_near = _missing_field_function("e_near")
102
+ h_near = _missing_field_function("h_near")
103
+ eh_near = _missing_field_function("eh_near")
104
+ e_near_cartesian = _missing_field_function("e_near_cartesian")
105
+ h_near_cartesian = _missing_field_function("h_near_cartesian")
106
+ eh_near_cartesian = _missing_field_function("eh_near_cartesian")
107
+ else:
108
+ raise
109
+ else:
110
+ e_far = _field.e_far
111
+ e_near = _field.e_near
112
+ h_near = _field.h_near
113
+ eh_near = _field.eh_near
114
+ e_near_cartesian = _field.e_near_cartesian
115
+ h_near_cartesian = _field.h_near_cartesian
116
+ eh_near_cartesian = _field.eh_near_cartesian
106
117
 
107
118
  # functions exposed to the user
108
119
  __all__ = (
@@ -127,15 +138,16 @@ __all__ = (
127
138
  "single_sphere",
128
139
  "small_sphere",
129
140
  "small_conducting_sphere",
141
+ "USE_JIT",
142
+ "D_calc",
143
+ "pi_tau",
130
144
  "_S1_S2",
131
- "_D_calc",
132
- "_pi_tau",
133
145
  "_Lentz_Dn",
134
146
  "_D_upwards",
135
147
  "_D_downwards",
136
148
  )
137
149
 
138
- __version__ = "3.1.0"
150
+ __version__ = "3.2.0"
139
151
  __author__ = "Scott Prahl"
140
152
  __email__ = "scott.prahl@oit.edu"
141
153
  __copyright__ = "2017-2026, Scott Prahl"
@@ -0,0 +1,49 @@
1
+ """Select and expose the active low-level backend implementation.
2
+
3
+ This module centralizes JIT vs no-JIT backend selection so other modules can
4
+ import backend callables without importing ``miepython`` package root.
5
+ """
6
+
7
+ import os
8
+
9
+ USE_JIT = os.environ.get("MIEPYTHON_USE_JIT", "0") == "1"
10
+
11
+ if USE_JIT:
12
+ from .mie_jit import _D_calc_nb as D_calc
13
+ from .mie_jit import _D_downwards
14
+ from .mie_jit import _D_upwards
15
+ from .mie_jit import _Lentz_Dn
16
+ from .mie_jit import _S1_S2_nb as _S1_S2
17
+ from .mie_jit import _an_bn_nb as an_bn
18
+ from .mie_jit import _cn_dn_nb as cn_dn
19
+ from .mie_jit import _pi_tau_nb as pi_tau
20
+ from .mie_jit import _single_sphere_nb as single_sphere
21
+ from .mie_jit import _small_conducting_sphere_nb as small_conducting_sphere
22
+ from .mie_jit import _small_sphere_nb as small_sphere
23
+ else:
24
+ from .mie_nojit import _D_calc_py as D_calc
25
+ from .mie_nojit import _D_downwards
26
+ from .mie_nojit import _D_upwards
27
+ from .mie_nojit import _Lentz_Dn
28
+ from .mie_nojit import _S1_S2_py as _S1_S2
29
+ from .mie_nojit import _an_bn_py as an_bn
30
+ from .mie_nojit import _cn_dn_py as cn_dn
31
+ from .mie_nojit import _pi_tau_py as pi_tau
32
+ from .mie_nojit import _single_sphere_py as single_sphere
33
+ from .mie_nojit import _small_conducting_sphere_py as small_conducting_sphere
34
+ from .mie_nojit import _small_sphere_py as small_sphere
35
+
36
+ __all__ = (
37
+ "USE_JIT",
38
+ "D_calc",
39
+ "an_bn",
40
+ "cn_dn",
41
+ "pi_tau",
42
+ "single_sphere",
43
+ "small_sphere",
44
+ "small_conducting_sphere",
45
+ "_S1_S2",
46
+ "_Lentz_Dn",
47
+ "_D_upwards",
48
+ "_D_downwards",
49
+ )
@@ -34,8 +34,7 @@ Normalized Mie scattering intensities for angles mu=cos(theta)::
34
34
  """
35
35
 
36
36
  import numpy as np
37
- from miepython import an_bn, cn_dn, single_sphere, _S1_S2
38
-
37
+ from ._backend import _S1_S2, an_bn, cn_dn, single_sphere
39
38
 
40
39
  # not really needed but included for clarity
41
40
  __all__ = (
@@ -79,10 +79,11 @@ Conventions
79
79
  """
80
80
 
81
81
  import numpy as np
82
- import miepython as mie
83
- from scipy.special import spherical_jn, factorial2
84
- from miepython.bessel import spherical_h1, d_riccati_bessel_h1
85
- from miepython.util import spherical_vector_to_cartesian
82
+ from scipy.special import factorial2, spherical_jn
83
+ from ._backend import D_calc, pi_tau
84
+ from .bessel import d_riccati_bessel_h1, spherical_h1
85
+ from .core import S1_S2, coefficients
86
+ from .util import spherical_vector_to_cartesian
86
87
 
87
88
  __all__ = (
88
89
  "e_near",
@@ -124,7 +125,7 @@ def _vsh_components_base(n_terms, lambda0, d_sphere, m_index, r, theta):
124
125
 
125
126
  pi = np.empty(n_terms)
126
127
  tau = np.empty(n_terms)
127
- mie._pi_tau(mu, pi, tau)
128
+ pi_tau(mu, pi, tau)
128
129
 
129
130
  n_int = np.arange(1, n_terms + 1, dtype=np.int64)
130
131
  n_arr = n_int.astype(np.float64)
@@ -142,7 +143,7 @@ def _vsh_components_base(n_terms, lambda0, d_sphere, m_index, r, theta):
142
143
  n_factor1 = rho_pow / denom
143
144
  n_factor2 = (n_arr + 1.0) * rho_pow / denom
144
145
  else:
145
- d_vals = mie._D_calc(np.complex128(m_index), float(kr), n_terms + 1)[:n_terms]
146
+ d_vals = D_calc(np.complex128(m_index), float(kr), n_terms + 1)[:n_terms]
146
147
  n_factor1 = jn / rho
147
148
  n_factor2 = jn * d_vals
148
149
  else:
@@ -181,7 +182,7 @@ def e_far(lambda0, d_sphere, m_sphere, n_env, r, theta, phi):
181
182
  jkr = 1j * 2 * np.pi * n_env * r / lambda0
182
183
  amp = np.exp(jkr) / (-jkr)
183
184
 
184
- S1, S2 = mie.S1_S2(m_rel, x, np.cos(theta), norm="wiscombe")
185
+ S1, S2 = S1_S2(m_rel, x, np.cos(theta), norm="wiscombe")
185
186
 
186
187
  E_r = np.zeros_like(S1, dtype=complex)
187
188
  E_theta = S2 * amp * np.cos(phi)
@@ -254,7 +255,7 @@ def _coefficients_abcd(lambda0, d_sphere, m_sphere, n_env, n_pole):
254
255
  """
255
256
  x = np.pi * d_sphere * n_env / lambda0
256
257
  m_rel = m_sphere / n_env
257
- a, b, c, d = mie.coefficients(m_rel, x, n_pole=n_pole, internal=True)
258
+ a, b, c, d = coefficients(m_rel, x, n_pole=n_pole, internal=True)
258
259
  return np.array([a, b, c, d])
259
260
 
260
261
 
@@ -402,7 +403,7 @@ def _e_near_abcd(abcd, lambda0, d_sphere, m_sphere, n_env, r, theta, phi, includ
402
403
  E_the += Ei_the
403
404
  E_phi += Ei_phi
404
405
 
405
- return np.array([E_rad, E_the, -E_phi])
406
+ return np.array([E_rad, E_the, E_phi])
406
407
 
407
408
 
408
409
  def _h_near_abcd(abcd, lambda0, d_sphere, m_sphere, n_env, r, theta, phi, include_incident):
@@ -459,7 +460,7 @@ def _h_near_abcd(abcd, lambda0, d_sphere, m_sphere, n_env, r, theta, phi, includ
459
460
  H_the += Hi_the
460
461
  H_phi += Hi_phi
461
462
 
462
- return np.array([H_rad, H_the, -H_phi])
463
+ return np.array([H_rad, H_the, H_phi])
463
464
 
464
465
 
465
466
  def _eh_near_abcd(abcd, lambda0, d_sphere, m_sphere, n_env, r, theta, phi, include_incident):
@@ -519,7 +520,7 @@ def _eh_near_abcd(abcd, lambda0, d_sphere, m_sphere, n_env, r, theta, phi, inclu
519
520
  h_the += h_i[1]
520
521
  h_phi += h_i[2]
521
522
 
522
- return np.array([e_rad, e_the, -e_phi]), np.array([h_rad, h_the, -h_phi])
523
+ return np.array([e_rad, e_the, e_phi]), np.array([h_rad, h_the, h_phi])
523
524
 
524
525
 
525
526
  def e_near(lambda0, d_sphere, m_sphere, n_env, r, theta, phi, include_incident=True, n_pole=0, abcd=None):
@@ -545,9 +546,9 @@ def e_near(lambda0, d_sphere, m_sphere, n_env, r, theta, phi, include_incident=T
545
546
  if abcd is None:
546
547
  abcd = _coefficients_abcd(lambda0, d_sphere, m_sphere, n_env, n_pole)
547
548
 
548
- evaluator = lambda rr, tt, pp: _e_near_abcd( # noqa: E731
549
- abcd, lambda0, d_sphere, m_sphere, n_env, rr, tt, pp, include_incident
550
- )
549
+ def evaluator(rr, tt, pp):
550
+ return _e_near_abcd(abcd, lambda0, d_sphere, m_sphere, n_env, rr, tt, pp, include_incident)
551
+
551
552
  return _vectorized_field_eval(evaluator, r, theta, phi)
552
553
 
553
554
 
@@ -574,9 +575,9 @@ def h_near(lambda0, d_sphere, m_sphere, n_env, r, theta, phi, include_incident=T
574
575
  if abcd is None:
575
576
  abcd = _coefficients_abcd(lambda0, d_sphere, m_sphere, n_env, n_pole)
576
577
 
577
- evaluator = lambda rr, tt, pp: _h_near_abcd( # noqa: E731
578
- abcd, lambda0, d_sphere, m_sphere, n_env, rr, tt, pp, include_incident
579
- )
578
+ def evaluator(rr, tt, pp):
579
+ return _h_near_abcd(abcd, lambda0, d_sphere, m_sphere, n_env, rr, tt, pp, include_incident)
580
+
580
581
  return _vectorized_field_eval(evaluator, r, theta, phi)
581
582
 
582
583
 
@@ -614,9 +615,9 @@ def eh_near(
614
615
  if abcd is None:
615
616
  abcd = _coefficients_abcd(lambda0, d_sphere, m_sphere, n_env, n_pole)
616
617
 
617
- evaluator = lambda rr, tt, pp: _eh_near_abcd( # noqa: E731
618
- abcd, lambda0, d_sphere, m_sphere, n_env, rr, tt, pp, include_incident
619
- )
618
+ def evaluator(rr, tt, pp):
619
+ return _eh_near_abcd(abcd, lambda0, d_sphere, m_sphere, n_env, rr, tt, pp, include_incident)
620
+
620
621
  return _vectorized_field_pair_eval(evaluator, r, theta, phi)
621
622
 
622
623
 
@@ -311,8 +311,8 @@ def N_base(n, m_index, kr, theta, inside):
311
311
  factor1 = rho ** (n - 1) / factorial2(2 * n + 1)
312
312
  else:
313
313
  factor1 = spherical_jn(n, rho)
314
- # _D_calc returns D_1..D_N; request one extra term and take D_n.
315
- Dn = mie._D_calc(m_index, kr, n + 1)[n - 1]
314
+ # D_calc returns D_1..D_N; request one extra term and take D_n.
315
+ Dn = mie.D_calc(m_index, kr, n + 1)[n - 1]
316
316
  factor2 = factor1 * Dn
317
317
  factor1 /= rho
318
318
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: miepython
3
- Version: 3.1.0
3
+ Version: 3.2.0
4
4
  Summary: Mie scattering of a plane wave by a sphere
5
5
  Author-email: Scott Prahl <scott.prahl@oit.edu>
6
6
  License-Expression: MIT
@@ -14,11 +14,12 @@ Classifier: Development Status :: 5 - Production/Stable
14
14
  Classifier: Intended Audience :: Science/Research
15
15
  Classifier: Programming Language :: Python
16
16
  Classifier: Programming Language :: Python :: 3
17
- Classifier: Programming Language :: Python :: 3.9
18
17
  Classifier: Programming Language :: Python :: 3.10
19
18
  Classifier: Programming Language :: Python :: 3.11
20
19
  Classifier: Programming Language :: Python :: 3.12
21
- Requires-Python: >=3.9
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Requires-Python: >=3.10
22
23
  Description-Content-Type: text/x-rst
23
24
  License-File: LICENSE.txt
24
25
  Requires-Dist: numpy
@@ -26,8 +27,9 @@ Requires-Dist: matplotlib
26
27
  Requires-Dist: scipy
27
28
  Requires-Dist: numba; sys_platform != "emscripten"
28
29
  Provides-Extra: docs
29
- Requires-Dist: Sphinx==8.1.3; extra == "docs"
30
- Requires-Dist: nbsphinx==0.9.7; extra == "docs"
30
+ Requires-Dist: Sphinx>=8.1.3; extra == "docs"
31
+ Requires-Dist: nbsphinx>=0.9.7; extra == "docs"
32
+ Requires-Dist: ipython; extra == "docs"
31
33
  Requires-Dist: sphinx_automodapi; extra == "docs"
32
34
  Requires-Dist: sphinx_rtd_theme; extra == "docs"
33
35
  Provides-Extra: dev
@@ -36,7 +38,7 @@ Requires-Dist: imageio; extra == "dev"
36
38
  Requires-Dist: requests; extra == "dev"
37
39
  Requires-Dist: ruff; extra == "dev"
38
40
  Requires-Dist: yamllint; extra == "dev"
39
- Requires-Dist: rstcheck; extra == "dev"
41
+ Requires-Dist: rstcheck[sphinx,toml]; extra == "dev"
40
42
  Requires-Dist: pylint; extra == "dev"
41
43
  Requires-Dist: pyroma; extra == "dev"
42
44
  Requires-Dist: black[jupyter]; extra == "dev"
@@ -49,8 +51,8 @@ Requires-Dist: jupyterlab; extra == "dev"
49
51
  Requires-Dist: nbconvert; extra == "dev"
50
52
  Requires-Dist: nbformat; extra == "dev"
51
53
  Provides-Extra: lite
52
- Requires-Dist: jupyterlite-core==0.6.4; extra == "lite"
53
- Requires-Dist: jupyterlite-pyodide-kernel==0.6.1; extra == "lite"
54
+ Requires-Dist: jupyterlite-core<0.8,>=0.7; extra == "lite"
55
+ Requires-Dist: jupyterlite-pyodide-kernel<0.8,>=0.7; extra == "lite"
54
56
  Dynamic: license-file
55
57
 
56
58
  .. |pypi| image:: https://img.shields.io/pypi/v/miepython?color=68CA66
@@ -208,19 +210,19 @@ Citation
208
210
 
209
211
  If you use ``laserbeamsize`` in academic, instructional, or applied technical work, please cite:
210
212
 
211
- Prahl, S. (2026). *miepython: Pure python calculation of Mie scattering* (Version 3.0.5) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.7949263
213
+ Prahl, S. (2026). *miepython: Pure python calculation of Mie scattering* (Version 3.1.0) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.7949263
212
214
 
213
215
  BibTeX:
214
216
 
215
217
  .. code-block:: bibtex
216
218
 
217
- @software{prahl_miepython_2025,
219
+ @software{prahl_miepython_2026,
218
220
  author = {Scott Prahl},
219
221
  title = {{miepython}: A Python library for Mie scattering calculations},
220
222
  url = {https://github.com/scottprahl/miepython},
221
223
  doi = {10.5281/zenodo.7949263},
222
224
  year = {2026},
223
- version = {3.0.5},
225
+ version = {3.1.0},
224
226
  publisher = {Zenodo}
225
227
  }
226
228
 
@@ -5,6 +5,7 @@ MANIFEST.in
5
5
  README.rst
6
6
  pyproject.toml
7
7
  miepython/__init__.py
8
+ miepython/_backend.py
8
9
  miepython/bessel.py
9
10
  miepython/core.py
10
11
  miepython/field.py
@@ -11,7 +11,7 @@ imageio
11
11
  requests
12
12
  ruff
13
13
  yamllint
14
- rstcheck
14
+ rstcheck[sphinx,toml]
15
15
  pylint
16
16
  pyroma
17
17
  black[jupyter]
@@ -25,11 +25,12 @@ nbconvert
25
25
  nbformat
26
26
 
27
27
  [docs]
28
- Sphinx==8.1.3
29
- nbsphinx==0.9.7
28
+ Sphinx>=8.1.3
29
+ nbsphinx>=0.9.7
30
+ ipython
30
31
  sphinx_automodapi
31
32
  sphinx_rtd_theme
32
33
 
33
34
  [lite]
34
- jupyterlite-core==0.6.4
35
- jupyterlite-pyodide-kernel==0.6.1
35
+ jupyterlite-core<0.8,>=0.7
36
+ jupyterlite-pyodide-kernel<0.8,>=0.7
@@ -13,7 +13,7 @@ miepython = ["data/*.txt"]
13
13
  name = "miepython"
14
14
  authors = [{name = "Scott Prahl", email = "scott.prahl@oit.edu"}]
15
15
  description = "Mie scattering of a plane wave by a sphere"
16
- requires-python = ">=3.9"
16
+ requires-python = ">=3.10"
17
17
  readme = "README.rst"
18
18
  license = "MIT"
19
19
  keywords = [
@@ -35,10 +35,11 @@ classifiers = [
35
35
  "Intended Audience :: Science/Research",
36
36
  "Programming Language :: Python",
37
37
  "Programming Language :: Python :: 3",
38
- "Programming Language :: Python :: 3.9",
39
38
  "Programming Language :: Python :: 3.10",
40
39
  "Programming Language :: Python :: 3.11",
41
40
  "Programming Language :: Python :: 3.12",
41
+ "Programming Language :: Python :: 3.13",
42
+ "Programming Language :: Python :: 3.14",
42
43
  ]
43
44
  dynamic = ["version"]
44
45
  dependencies = [
@@ -50,8 +51,9 @@ dependencies = [
50
51
 
51
52
  [project.optional-dependencies]
52
53
  docs = [
53
- "Sphinx==8.1.3",
54
- "nbsphinx==0.9.7",
54
+ "Sphinx>=8.1.3",
55
+ "nbsphinx>=0.9.7",
56
+ "ipython",
55
57
  "sphinx_automodapi",
56
58
  "sphinx_rtd_theme",
57
59
  ]
@@ -65,7 +67,7 @@ dev = [
65
67
  # Lint / QA
66
68
  "ruff",
67
69
  "yamllint",
68
- "rstcheck",
70
+ "rstcheck[sphinx,toml]",
69
71
  "pylint",
70
72
  "pyroma",
71
73
  "black[jupyter]",
@@ -84,8 +86,8 @@ dev = [
84
86
  ]
85
87
 
86
88
  lite = [
87
- "jupyterlite-core==0.6.4",
88
- "jupyterlite-pyodide-kernel==0.6.1",
89
+ "jupyterlite-core>=0.7,<0.8",
90
+ "jupyterlite-pyodide-kernel>=0.7,<0.8",
89
91
  ]
90
92
 
91
93
  [project.urls]
@@ -100,7 +102,7 @@ version = {attr = "miepython.__version__"}
100
102
 
101
103
  [tool.black]
102
104
  line-length = 120
103
- target-version = ["py39", "py310", "py311", "py312"]
105
+ target-version = ["py310", "py311", "py312", "py313", "py314"]
104
106
 
105
107
  [tool.pytest.ini_options]
106
108
  testpaths = ["tests"]
@@ -111,10 +113,12 @@ addopts = [
111
113
  [tool.pylint]
112
114
  disable = [
113
115
  "C0103", "C0209", "C0302", "C0413", "E0611",
114
- "R0902", "R0904", "R0912", "R0913", "R0914", "R0915", "R0917", "R1702",
116
+ "R0801", "R0902", "R0903", "R0904", "R0912",
117
+ "R0913", "R0914", "R0915", "R0917", "R1702",
115
118
  "W0511"
116
119
  ]
117
120
  max-line-length = 120
121
+ exclude-protected = ["_single_sphere_py", "_single_sphere_nb", "_S1_S2_py", "_S1_S2_nb"]
118
122
 
119
123
  [tool.ruff]
120
124
  line-length = 120
@@ -1,5 +1,5 @@
1
- # pylint: disable=invalid-name
2
1
  """Run all the scripts in the examples directory."""
2
+
3
3
  import importlib
4
4
  import time
5
5
  import sys