miepython 3.1.0__tar.gz → 3.3.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 (63) hide show
  1. miepython-3.3.0/CHANGELOG.rst +628 -0
  2. {miepython-3.1.0 → miepython-3.3.0}/CITATION.cff +3 -3
  3. miepython-3.3.0/MANIFEST.in +12 -0
  4. {miepython-3.1.0 → miepython-3.3.0}/PKG-INFO +16 -13
  5. {miepython-3.1.0 → miepython-3.3.0}/README.rst +5 -5
  6. miepython-3.3.0/miepython/__init__.py +186 -0
  7. miepython-3.3.0/miepython/_backend.py +49 -0
  8. {miepython-3.1.0 → miepython-3.3.0}/miepython/core.py +106 -42
  9. {miepython-3.1.0 → miepython-3.3.0}/miepython/field.py +241 -295
  10. {miepython-3.1.0 → miepython-3.3.0}/miepython/mie_jit.py +202 -97
  11. {miepython-3.1.0 → miepython-3.3.0}/miepython/mie_nojit.py +166 -86
  12. {miepython-3.1.0 → miepython-3.3.0}/miepython/monte_carlo.py +57 -8
  13. {miepython-3.1.0 → miepython-3.3.0}/miepython/rayleigh.py +7 -33
  14. {miepython-3.1.0 → miepython-3.3.0}/miepython/util.py +25 -5
  15. {miepython-3.1.0 → miepython-3.3.0}/miepython/vsh.py +17 -8
  16. {miepython-3.1.0 → miepython-3.3.0}/miepython.egg-info/PKG-INFO +16 -13
  17. {miepython-3.1.0 → miepython-3.3.0}/miepython.egg-info/SOURCES.txt +16 -9
  18. {miepython-3.1.0 → miepython-3.3.0}/miepython.egg-info/requires.txt +7 -5
  19. {miepython-3.1.0 → miepython-3.3.0}/pyproject.toml +33 -9
  20. miepython-3.3.0/tests/benchmark_efficiencies.py +126 -0
  21. miepython-3.3.0/tests/conftest.py +93 -0
  22. miepython-3.3.0/tests/reference.py +239 -0
  23. miepython-3.1.0/tests/test_jit_D.py → miepython-3.3.0/tests/test_D.py +73 -68
  24. miepython-3.3.0/tests/test_abcd.py +495 -0
  25. miepython-3.3.0/tests/test_all_examples.py +23 -0
  26. {miepython-3.1.0 → miepython-3.3.0}/tests/test_all_notebooks.py +5 -4
  27. miepython-3.3.0/tests/test_backend_parity.py +214 -0
  28. miepython-3.3.0/tests/test_bessel.py +201 -0
  29. miepython-3.3.0/tests/test_docstrings.py +76 -0
  30. miepython-3.3.0/tests/test_field.py +604 -0
  31. miepython-3.3.0/tests/test_kernels.py +469 -0
  32. miepython-3.3.0/tests/test_makefile.py +109 -0
  33. miepython-3.3.0/tests/test_mie.py +967 -0
  34. miepython-3.3.0/tests/test_monte_carlo.py +246 -0
  35. miepython-3.3.0/tests/test_optional_scipy.py +155 -0
  36. miepython-3.3.0/tests/test_package_surface.py +161 -0
  37. miepython-3.3.0/tests/test_rayleigh.py +231 -0
  38. miepython-3.3.0/tests/test_scattnlay_reference.py +169 -0
  39. miepython-3.3.0/tests/test_util.py +197 -0
  40. {miepython-3.1.0 → miepython-3.3.0}/tests/test_vsh.py +117 -7
  41. miepython-3.1.0/CHANGELOG.rst +0 -291
  42. miepython-3.1.0/MANIFEST.in +0 -8
  43. miepython-3.1.0/miepython/__init__.py +0 -143
  44. miepython-3.1.0/tests/test_all_examples.py +0 -19
  45. miepython-3.1.0/tests/test_bessel.py +0 -188
  46. miepython-3.1.0/tests/test_field.py +0 -293
  47. miepython-3.1.0/tests/test_jit.py +0 -612
  48. miepython-3.1.0/tests/test_jit_abcd.py +0 -880
  49. miepython-3.1.0/tests/test_jit_speed.py +0 -45
  50. miepython-3.1.0/tests/test_mie_backend_speed.py +0 -82
  51. miepython-3.1.0/tests/test_nojit.py +0 -622
  52. miepython-3.1.0/tests/test_nojit_D.py +0 -243
  53. miepython-3.1.0/tests/test_nojit_abcd.py +0 -879
  54. miepython-3.1.0/tests/test_nojit_speed.py +0 -45
  55. miepython-3.1.0/tests/test_rayleigh.py +0 -87
  56. {miepython-3.1.0 → miepython-3.3.0}/LICENSE.txt +0 -0
  57. {miepython-3.1.0 → miepython-3.3.0}/miepython/bessel.py +0 -0
  58. {miepython-3.1.0 → miepython-3.3.0}/miepython/data/Johnson.txt +0 -0
  59. {miepython-3.1.0 → miepython-3.3.0}/miepython/data/segelstein81_index.txt +0 -0
  60. {miepython-3.1.0 → miepython-3.3.0}/miepython.egg-info/dependency_links.txt +0 -0
  61. {miepython-3.1.0 → miepython-3.3.0}/miepython.egg-info/top_level.txt +0 -0
  62. {miepython-3.1.0 → miepython-3.3.0}/setup.cfg +0 -0
  63. {miepython-3.1.0 → miepython-3.3.0}/tests/test_unpolarized_intensity.py +0 -0
@@ -0,0 +1,628 @@
1
+ Changelog
2
+ =========
3
+
4
+ 3.3.0 (07/28/2026)
5
+ -------------------
6
+ * fix the docstring examples in ``core.py``, which had rotted unnoticed. The
7
+ ``coefficients`` examples called ``mie_coefficients``, a name that never
8
+ existed, showed the imaginary parts with the wrong sign, and still included the
9
+ padding element ``an_bn`` no longer returns; the ``efficiencies_mx`` examples
10
+ used a different function's signature and printed an invented repr. The
11
+ Returns section also described a tuple where an ndarray comes back
12
+ * run the docstring examples as part of the test suite, so they cannot rot again.
13
+ They are written with explicit formatting rather than bare array reprs, which
14
+ keeps them independent of NumPy's print precision
15
+ * keep one copy of the scattering-function normalization rules. ``core`` and
16
+ ``rayleigh`` held the same thirty lines differing only in which efficiencies
17
+ they consulted, so ``core.normalization_factor`` now takes an
18
+ ``efficiency_source`` and ``rayleigh`` passes its own
19
+ * accept an ``rng`` argument in ``generate_mie_costheta``. It drew from the
20
+ process-wide ``numpy.random``, which a caller could only control by reseeding
21
+ the whole program; passing ``numpy.random.default_rng(seed)`` now gives a
22
+ reproducible stream and leaves the global one alone. Omitting it behaves as
23
+ before
24
+ * document the angular-argument convention: a scalar ``mu`` counts as one angle
25
+ and returns a length-one array, except in ``phase_matrix``, which squeezes it
26
+ away. ``docs/01_basics.ipynb`` demonstrates the first half deliberately, so
27
+ both are now stated in the docstrings and pinned by tests
28
+ * fix ``make readme_images``, which had never worked: ``.PHONY`` declared that name
29
+ but the rule below it was written ``readme:``, so the advertised command failed
30
+ with "No rule to make target" while an undocumented ``make readme`` did the job
31
+ * drop ``sync`` from ``make help``. No such target exists; the one that syncs the
32
+ environment is ``venv``, and it was not listed at all. Also corrected
33
+ ``pylint-check``, described as "Same as lint above" when it runs pylint alone, and
34
+ moved the individual check targets out of the packaging section into the lint one
35
+ where they belong
36
+ * check the Makefile's help text against the rules it defines, so a target cannot be
37
+ advertised, or declared phony, without existing. These four bugs were all of the
38
+ same kind -- the help block is a pile of ``@echo`` lines with nothing tying it to
39
+ the rules underneath -- and nothing could have caught them
40
+ * generate the README figures reproducibly. matplotlib stamps each SVG with the
41
+ current time and names clip paths and glyphs from a random salt, so re-running
42
+ ``make readme_images`` produced files differing on a hundred lines even when the
43
+ figures were pixel-for-pixel identical. There was no way to tell "a plot changed"
44
+ from "matplotlib rolled different ids". Fixing ``svg.hashsalt`` and passing
45
+ ``metadata={"Date": None}`` makes repeated runs byte-identical, and the committed
46
+ images are regenerated once against that
47
+ * point the README license badge at ``blob/main`` rather than ``blob/master``. There
48
+ is no master branch; the link worked only because GitHub silently redirects
49
+ * evaluate the near fields for every point at once instead of one point at a time.
50
+ ``field.py`` walked the grid with ``np.ndindex`` and called into
51
+ ``scipy.special`` separately for each point, so a 41x41 slice spent 0.4 s almost
52
+ entirely in scipy's per-call overhead rather than on Bessel functions. The points
53
+ are now split once into those inside the sphere and those outside -- the two sides
54
+ need different radial functions and different media, but within a side every point
55
+ does the same arithmetic -- and each group is evaluated as a
56
+ ``(n_points, n_terms)`` batch. Roughly 18x faster: that slice takes 0.022 s, and
57
+ a 101x101 slice drops from 2.4 s to 0.13 s. Every returned value is bitwise
58
+ identical to before, which is checked point by point against evaluating the same
59
+ coordinates individually
60
+ * evaluate the spherical Hankel functions over orders ``0..n+1`` in one call rather
61
+ than making four separate calls that recompute shared orders. ``xi'_n`` needs
62
+ ``h1`` at ``n-1``, ``n`` and ``n+1``, and the order-``n`` value was being computed
63
+ twice; slicing one array instead cuts that part of the work to a quarter
64
+ * drop the three near-field per-point evaluators and the two loop drivers in favour
65
+ of a single batched routine, 63 fewer statements for the same physics.
66
+ ``_vsh_components_base`` now takes an explicit ``inside`` flag and arrays of
67
+ coordinates, so it no longer re-derives from ``d_sphere`` which side it is on
68
+ * make the submodules reachable, each in the way that suits it.
69
+ ``miepython.rayleigh`` needs only NumPy and is now imported with the package, so
70
+ ``import miepython`` is enough to reach it; ``miepython.vsh`` requires the
71
+ optional ``scipy`` and is imported inside the same guard as ``field``, staying
72
+ absent when scipy is not installed; ``miepython.monte_carlo`` imports
73
+ ``miepython`` itself, so importing it from the package would be circular and it
74
+ remains a separate ``import miepython.monte_carlo``. All three are now described
75
+ in the package docstring, and ``monte_carlo``'s own docstring shows the full
76
+ workflow and says why it is not imported for you
77
+ * have ``vsh`` reach ``D_calc`` through ``._backend`` instead of importing
78
+ ``miepython`` for it. The old import was a cycle that worked only because the
79
+ call is deferred to run time, and importing ``vsh`` from ``__init__`` would have
80
+ depended on that
81
+ * fix the call signatures listed in ``vsh``'s module docstring. All four vector
82
+ spherical harmonics were advertised as ``(n, k, d_sphere, r, theta, phi)``; they
83
+ actually take a wavelength rather than a wavenumber and a refractive index
84
+ besides, ``(n, lambda0, d_sphere, m_index, r, theta, phi)``, so anyone following
85
+ the docstring got a ``TypeError``. The four ``*_array`` variants went unmentioned
86
+ altogether. Signatures written out in prose are now checked against the code
87
+ * fix two stale references in the notebooks: ``mie.mie_S1_S2`` in
88
+ ``docs/03a_normalization.ipynb``, together with the comparison values it quotes,
89
+ and a commented-out ``mie._D_calc`` in ``docs/10_basic_tests.ipynb``
90
+ * fix off-by-one in ``n_pole`` for ``S1_S2`` and everything built on it
91
+ (``i_par``, ``i_per``, ``i_unpolarized``, ``phase_matrix``, ``intensities``).
92
+ ``n_pole=1`` returned the quadrupole instead of the dipole; it now matches
93
+ the convention already used by ``efficiencies_mx``
94
+ * raise ``ValueError`` for an ``n_pole`` beyond the truncated series instead of
95
+ returning zeros or an ``IndexError``
96
+ * add multipole regression tests (series sum, closed form, per-multipole
97
+ optical theorem) and re-execute ``docs/12_multipoles.ipynb``
98
+ * implement the ``e_field`` argument of ``efficiencies_mx``, which was accepted
99
+ and documented but silently ignored. With ``n_pole > 0`` it now selects the
100
+ electric multipole a_n (``e_field=True``, the default) or the magnetic
101
+ multipole b_n (``e_field=False``). **Behavior change:** ``n_pole > 0``
102
+ previously returned a_n and b_n combined; that total is now
103
+ ``e_field=True`` plus ``e_field=False``
104
+ * make the two backends agree on ``g`` when ``n_pole > 0``. The no-JIT backend
105
+ returned ``None`` (which became ``nan`` for array input) while the JIT
106
+ backend returned ``0``. Both now return ``0.0``, which is the exact value:
107
+ an isolated multipole of one parity scatters symmetrically about 90 degrees
108
+ * fix an operator-precedence bug in the perfectly-conducting guard of ``cn_dn``.
109
+ ``a and b or c`` made the test true for every finite index, so ``cn_dn(0j, x)``
110
+ raised ``ZeroDivisionError`` and an infinite index produced ``nan``. The
111
+ internal coefficients are now zero for a perfect conductor, as intended
112
+ * drop ``fastmath`` from the numba ``_cn_dn_nb`` kernel. It implies LLVM's
113
+ ``ninf``, which folded the ``np.isinf`` guard to ``False`` and defeated the
114
+ fix above in the JIT backend. This also improves JIT/no-JIT agreement
115
+ * make the internal-field coefficients ``c_n`` and ``d_n`` numerically stable.
116
+ ``psi_n`` came from the three-term upwards recurrence, which is contaminated
117
+ by the growing ``chi_n`` solution once n exceeds ``|mx|``. That is the normal
118
+ case for a relative index below one, where the coefficients were wrong by up
119
+ to 140%. ``psi_n`` now uses Miller's downwards recurrence seeded above ``|z|``
120
+ and normalised against whichever of ``psi_0`` or ``psi_1`` is larger, and
121
+ the logarithmic derivatives take the always-stable downwards route.
122
+ Worst error against a direct SciPy evaluation over 7320 cases drops from
123
+ 90 to 3e-9, and the JIT and no-JIT backends now agree to 5e-15 where they
124
+ previously differed by up to a factor of 39
125
+ * always use the downwards recurrence for the logarithmic derivative ``D_n``.
126
+ Wiscombe's criterion chose between the two recurrences from the refractive
127
+ index alone, never from the number of terms, so it picked the upwards
128
+ recurrence for small spheres where it is unstable. For a lossless sphere
129
+ ``Re(a_n)`` must equal ``|a_n|**2``; at m=1.05, x=0.1 the real parts of the
130
+ quadrupole and higher coefficients came out with the wrong sign and up to 12
131
+ orders of magnitude too large, which made ``efficiencies_mx(n_pole=3)``
132
+ report a negative extinction efficiency. That identity now holds to 4e-24
133
+ over all lossless cases tested, and ``qext`` and ``qsca`` agree to 5e-16
134
+ where they used to differ by 2e-7. The pure-python backend is about 20%
135
+ slower for a large sweep of size parameters; the numba backend is unchanged
136
+ * remove ``fastmath`` from every numba kernel. Besides defeating the
137
+ perfectly-conducting guard above, its reassociation degraded the exact
138
+ lossless identity by roughly six times relative to the pure-python backend.
139
+ Costs about 10% on a large sweep of size parameters
140
+ * ``an_bn`` no longer pads a zero onto the end of ``a`` and ``b``, and ``cn_dn``
141
+ no longer computes one order more than ``an_bn``. Both now return exactly
142
+ Wiscombe's number of terms with every entry a real coefficient, so the
143
+ internal and external series share one truncation. The retained ``a_n`` and
144
+ ``b_n`` are bit-for-bit unchanged, and the arrays are one element shorter
145
+ * ``an_bn(m, x)`` works on both backends. ``_an_bn_py`` defaulted ``n_pole`` to
146
+ zero but the numba kernel's eager signature could not, so the two-argument
147
+ call raised ``TypeError`` with the JIT on. ``_an_bn_nb`` is now compiled
148
+ lazily, which allows the default
149
+ * group the arithmetic in the numba ``_an_bn_nb`` and ``_cn_dn_nb`` kernels the
150
+ way the pure-python ones do. Multiplying by a precomputed ``1/x`` in one and
151
+ dividing by ``x`` in the other differed by one ulp, and the psi recurrence
152
+ amplified that to 2.6e-7 between the backends on significant coefficients.
153
+ They now agree to 1e-13
154
+ * ``an_bn`` builds ``psi_n`` with Miller's downwards recurrence, the same
155
+ treatment ``cn_dn`` already had, and builds ``xi_n`` as ``psi_n + i*chi_n``
156
+ from an upwards ``chi_n`` recurrence. The upwards ``psi_n`` recurrence was
157
+ accurate enough inside Wiscombe's truncation but lost all relative accuracy
158
+ beyond it -- about 1e-6 at the last kept order, and 1e10 or worse a few orders
159
+ later -- so asking for extra terms returned noise. Requesting more orders now
160
+ converges instead: the near-field boundary mismatch at W+8 terms improves from
161
+ 4.7e-2 to 3.1e-6, and it keeps improving to W+16 rather than diverging.
162
+ Building ``xi_n`` from the same ``psi_n`` preserves the lossless identity
163
+ ``Re(a_n) == |a_n|**2`` at 5.6e-16. Within the standard truncation nothing
164
+ moves by more than 1e-11, so this is headroom rather than a correction. The
165
+ pure-python backend costs about 7% more for a large sweep; numba is unchanged.
166
+ ``xi_n`` itself was already fine: it is dominated by the growing ``chi_n``
167
+ solution, for which the upwards recurrence is the stable direction, and it
168
+ matches SciPy to 1e-15 even twenty orders past the truncation
169
+ * assert the scattnlay comparison instead of only plotting it. The reference
170
+ arrays under ``docs/data`` were used solely by ``docs/15_2D_fields.ipynb``,
171
+ which printed relative errors without checking them, so a near-field
172
+ regression was invisible; the median error improved 19x during this release
173
+ and nothing would have noticed either that or its reversal.
174
+ ``tests/test_scattnlay_reference.py`` now checks the scalar efficiencies,
175
+ which are inlined like the existing MIEV0 values and need no data files, and
176
+ compares both 121x121 field grids point by point. Grid points that land
177
+ exactly on the sphere surface are excluded, since the radial E component is
178
+ discontinuous there and the two codes may put them on opposite sides
179
+ * ``docs/15_2D_fields.ipynb`` reads the reference arrays from ``docs/data``
180
+ rather than downloading them from the published branch, so ``make note-test``
181
+ works offline and checks the working tree instead of the last release. Its
182
+ stored error figures were also stale and have been refreshed
183
+ * collapse the per-backend test files. ``test_jit.py``/``test_nojit.py`` and
184
+ ``test_jit_abcd.py``/``test_nojit_abcd.py`` became ``test_mie.py`` and
185
+ ``test_abcd.py`` for the high-level API, which needs one copy because
186
+ ``core.py`` has no backend-specific code, plus ``test_kernels.py`` for the
187
+ kernel-level tests, which the ``kernels`` fixture runs against both backends
188
+ in one process. All 74 distinct test names are preserved, and the merge picks
189
+ up what had drifted between the two halves: an extra MIEV0 conducting case,
190
+ four extra ``qext`` assertions, and both spellings of a perfectly conducting
191
+ index. Shared reference implementations moved to ``tests/reference.py``
192
+ * drop the ``MIEPYTHON_RUN_MIE_SPEED`` switch and ``test_mie_backend_speed.py``.
193
+ Nothing ever set that variable, so the three real assertions in that file --
194
+ a backend agreement check over 4000 random particles and 361 angles -- had
195
+ never run, while the four assertions it also gated were tautologies
196
+ (``elapsed > 0``). The agreement check moved to ``test_backend_parity.py``
197
+ where it always runs and costs about a tenth of a second, and the speedup
198
+ report it printed became ``benchmark_efficiencies.py --compare``, which
199
+ ``make speed`` now calls
200
+ * the benchmark scripts ``test_jit_speed.py`` and ``test_nojit_speed.py`` became
201
+ one ``tests/benchmark_efficiencies.py``. They timed at import rather than in a
202
+ test function, so pytest spent about seven seconds running benchmarks during
203
+ collection while collecting nothing; a full test pass is now much quicker.
204
+ They also both timed whichever backend happened to be bound, so the JIT-named
205
+ one could report pure-python speed. ``make speed`` runs the single script once
206
+ per backend
207
+ * add ``tests/test_backend_parity.py``, which compares the two kernel sets
208
+ directly -- signatures and values -- in a single process, and merge the
209
+ ``test_*_D.py`` pair into one ``test_D.py`` driven by a ``kernels`` fixture
210
+ that parametrizes over both backends. Tests written this way need only one
211
+ copy instead of a JIT/no-JIT pair
212
+ * test ``monte_carlo.py``, which had no tests at all, and drop the unreachable
213
+ index clamp in ``generate_mie_costheta``: ``numpy.random.random`` draws from
214
+ [0, 1), so the index can never run off the table. A test forces the largest
215
+ float below one to show that. The sampler is checked against physics rather
216
+ than against itself: the mean of the drawn cosines reproduces the asymmetry
217
+ parameter g, computed by a completely separate path, and the empirical
218
+ distribution follows the inverse-transform table. With this the whole package
219
+ is covered, so ``fail_under`` is now 100
220
+ * record that ``cdf`` overshoots one. It sums ``i_unpolarized / num`` instead of
221
+ multiplying by the true spacing 2/(num-1), so the last value is about
222
+ 1 + 1.3/num: at the num=20 of ``docs/06_random_deviates.ipynb`` the cumulative
223
+ distribution reaches 1.067 even though the docstring promises a maximum of 1.
224
+ A test pins the 1/num convergence so the quadrature cannot quietly change
225
+ * test the kernel paths an ordinary Mie call never reaches, taking both
226
+ ``mie_jit.py`` and ``mie_nojit.py`` to 100%. ``_D_upwards`` is kept for
227
+ comparison but ``D_calc`` no longer selects it, so it is now checked against
228
+ SciPy where it is valid and against ``_D_downwards``. Also covered: the
229
+ overflow rescale inside ``_psi_downwards``, which a small argument triggers and
230
+ which the normalisation afterwards has to undo exactly; ``cn_dn`` for a sphere
231
+ of no size; and the ``m=0`` shorthand for a perfect conductor, now shown to be
232
+ identical to passing ``1-10000j``. Every module except ``monte_carlo.py`` is
233
+ fully covered, with no partial branches left anywhere
234
+ * test the scattered-only fields and the explicit term count in ``field.py``,
235
+ taking it to 100%. Nothing had ever called a near-field routine with
236
+ ``include_incident=False`` for H, or passed ``n_pole`` at all. Outside the
237
+ sphere the total field minus the scattered field is now checked against an
238
+ incident plane wave written out independently of miepython, and inside the
239
+ sphere ``include_incident`` is confirmed to make no difference
240
+ * test the array dispatch and error paths in ``core.py``, taking it to 100%.
241
+ The ``RuntimeError`` for mismatched ``m`` and ``x`` lengths, the one for array
242
+ input without ``n_pole``, the ``internal`` variants of ``coefficients`` and the
243
+ mixed scalar-and-array calls were all untested, as was the rejection of an
244
+ unknown normalization. Every array result is now checked against the
245
+ equivalent scalar calls
246
+ * fix ``S1_S2`` rejecting a list or an integer array of angles under numba. The
247
+ kernel is declared for ``float64[:]``, so ``S1_S2(m, x, [0, 1])`` worked with the
248
+ JIT off and raised ``TypeError`` with it on; ``core.py`` now coerces the angles
249
+ once, at the boundary, so both backends take the same arguments
250
+ * test the missing-SciPy path in ``__init__.py`` and the remaining parts of
251
+ ``rayleigh.py``, taking them from 52% and 72% to 100%; the total reaches 95%.
252
+ The SciPy fallback is what keeps the package usable in JupyterLite and had never
253
+ been exercised: the tests re-import the package behind an import hook, check the
254
+ placeholders name themselves and chain the original error, and confirm that a
255
+ failure other than SciPy still propagates. ``rayleigh``'s physical-units
256
+ wrappers, all thirteen normalization spellings and its error paths were also
257
+ untested
258
+ * record that ``rayleigh``'s normalization closes only to O(x**2): ``qsca`` in
259
+ ``efficiencies_mx`` stops at x**4 while the ``a1`` used by ``S1_S2`` carries an
260
+ x**5 term, so the 'one' normalization integrates to 1 + 0.07 x**2 rather than 1.
261
+ A test pins that order so extending one expansion without the other shows up
262
+ * test ``vsh.py`` and ``util.py``, which the new coverage run showed at 60% and
263
+ 55%. Both are now at 100% branch coverage, and the total rises from 86% to
264
+ 91%. The four ``M_*_array``/``N_*_array`` helpers, the ``deg=True`` angle
265
+ paths and the small-argument series in ``N_base`` had no tests at all. One of
266
+ the new tests checks ``vsh.py`` against ``field.py``, which holds a second copy
267
+ of the same vector spherical harmonics, so the two can no longer drift apart
268
+ * measure coverage. ``make coverage`` runs the suite once per backend and
269
+ combines the results, and a CI job publishes the HTML report as an artifact.
270
+ Naive coverage is misleading here: coverage.py cannot see inside numba's
271
+ compiled functions, so ``mie_jit.py`` reports 11% no matter how well it is
272
+ tested. The second pass therefore sets ``NUMBA_DISABLE_JIT=1``, which runs the
273
+ njit bodies as plain Python and lifts that file to 96%. The combined figure is
274
+ 86% with branch coverage, and ``fail_under`` is set to 84 as a floor to ratchet
275
+ upward. Two tests that deliberately assert numba is compiled were rewritten to
276
+ hold in either mode rather than being skipped, so all three modes stay green
277
+ * widen the CI matrix. It covered ubuntu on Python 3.10 and 3.14 only, and
278
+ because it also ran just two test files, most of the suite had never executed
279
+ on the oldest supported Python. Linux now runs every version the classifiers
280
+ promise, 3.10 through 3.14, and macOS and Windows run both ends of that range,
281
+ since numba holds the platform-specific half of the package. numba 0.66 ships
282
+ wheels for all of those combinations
283
+ * discover example scripts and notebooks relative to the test file rather than
284
+ the working directory. ``test_all_examples.py`` globbed a relative path, so a
285
+ run started anywhere but the repository root collected nothing and reported
286
+ success; it now asserts that it found something
287
+ * CI now lints. It only ran pytest, so a pull request with formatting or lint
288
+ errors passed clean and the checks in ``make rcheck`` were enforced on nobody.
289
+ A new job runs ``make lint``, the same target a release check uses, through uv,
290
+ so the list of checks lives in the Makefile alone and cannot drift from CI
291
+ * add a ``make lint`` target covering ruff, black, pylint, rstcheck, yamllint,
292
+ check-manifest and pyroma, and have ``make rcheck`` delegate to it. That also
293
+ closes a gap: ``yaml-check`` existed but was never part of ``rcheck``, so the
294
+ workflow files were only ever checked by hand
295
+ * the ``test_jit*`` files now really exercise the numba backend. ``_backend.py``
296
+ binds its kernels the first time ``miepython`` is imported, so setting
297
+ ``MIEPYTHON_USE_JIT`` inside a test module only worked when that module
298
+ happened to import the package first. In a full ``make test`` run something
299
+ always imported it earlier with the JIT off, so those files silently retested
300
+ the pure-python kernels and passed. ``tests/conftest.py`` now reads the
301
+ backend that actually got bound and skips the files belonging to the other
302
+ one, and ``make test`` runs the suite once per backend. The backend-agnostic
303
+ tests run under both, which is new coverage for them. New ``make test-jit``
304
+ and ``make test-nojit`` targets run a single pass, and CI now runs the whole
305
+ suite per backend rather than one file each
306
+ * raise a clear error instead of returning ``inf`` or ``nan`` when a scattering
307
+ function is asked to normalise against a sphere that does not scatter. A
308
+ sphere whose index matches its surroundings gave ``i_unpolarized`` values of
309
+ ``inf`` for the 'albedo', 'one' and '4pi' normalizations and a bare
310
+ ``ZeroDivisionError`` for 'qext'. The 'wiscombe', 'bohren' and 'qsca'
311
+ choices do not divide by an efficiency and still work
312
+ * ``efficiencies_mx`` returns zero rather than ``nan`` for ``qback`` when the
313
+ size parameter is zero. The small-sphere form of ``qback`` is 0/0 there,
314
+ although its limit is zero, and a single zero in an array of size parameters
315
+ used to leave a ``nan`` behind
316
+ * fix ``util.cartesian_to_spherical``, which raised on array input because of a
317
+ scalar ``if r != 0`` test. It now broadcasts its three arguments, reports
318
+ theta as zero at the origin, and clips ``z/r`` so rounding cannot push the
319
+ arccos argument out of range. ``field.py`` dropped its private duplicate of
320
+ this routine
321
+ * fix ``util`` exporting ``_all_`` instead of ``__all__``, and list
322
+ ``phasor_str_scalar`` alongside the rest
323
+ * the near-field routines keep two more terms than Wiscombe's criterion. That
324
+ criterion truncates the scattered series, which converges faster than the
325
+ field evaluated right at the sphere surface: the tangential E and H mismatch
326
+ across the boundary drops from 1.5e-5 to 3.2e-6, and the median disagreement
327
+ with scattnlay over a 121x121 slice falls from 1.1e-11 to 3.3e-14. Further
328
+ orders gain almost nothing once ``psi_n`` is computed stably, so two is where
329
+ it stops. The scattering quantities are untouched, and ``e_far`` still uses
330
+ the criterion unchanged. ``miepython.core.wiscombe_terms`` names the count
331
+ * ``pi_tau`` now fills ``tau`` for the highest order. It had always left the
332
+ last entry zero, which was hidden by the ``an_bn`` padding above and would
333
+ have silently dropped a term once the padding went away. ``n_pole`` may now
334
+ address the highest order, which the old bounds check rejected
335
+
336
+ 3.2.0 (03/06/2026)
337
+ -------------------
338
+ * fix error in E & H calculations in the near field when y≠0 (thanks @dorianherle)
339
+ * add regression test
340
+
341
+ 3.1.0 (02/07/2026)
342
+ -------------------
343
+ * add near-field E and H field APIs, fix boundary continuity/medium handling, and expand validation tests
344
+ * add field calculation utilities, field module cleanup/help text improvements, and precomputed E/H reference data (via scattnlay)
345
+ * improve performance: ~20% speedups in Mie backends and faster near-field calculations; add speed benchmarks
346
+ * documentation updates: new/updated notebooks (boundary conditions, 2D fields, performance), clarified conventions
347
+ * refresh docs/README visuals and assets; add custom CSS for docs images
348
+ * jupyterlite/RTD updates: config moves, build modernizations, and avoid numba install in JupyterLite
349
+ * packaging/CI: pyproject and requirements cleanup, improved PyPI workflow, updated citation automation/config
350
+ * misc cleanups: remove unused files/images, minor typos, Makefile and docs config tidy-ups, CITATION.cff refresh
351
+
352
+ 3.0.5 (1/2/2026)
353
+ -------------------
354
+ * fix versioning
355
+
356
+ 3.0.4 (1/2/2026)
357
+ -------------------
358
+ * Host jupyterlite instance of github
359
+ * improve citation guidelines
360
+ * add test for unpolarized intensity methods
361
+ * begin work on local electrical and magnetic fields
362
+ * using black now with longer lines
363
+ * improved packaging
364
+ * improve readme
365
+
366
+ 3.0.3
367
+ -------------------
368
+ * skipped
369
+
370
+ 3.0.2 (5/25/2025)
371
+ -------------------
372
+ * fix version number
373
+
374
+ 3.0.1 (5/25/2025)
375
+ -------------------
376
+ * fix JIT regression (thanks @avgeiss)
377
+ * clarify polarization in docstrings
378
+ * improve README.rst
379
+ * fix git branches
380
+ * rename mie.mie_scalar to mie.single_sphere
381
+ * rename small_mie_sphere to small_sphere
382
+ * rename small_conducting_mie to small_conducting_sphere
383
+ * rationalize importing of jit and non-jit code
384
+ * add test_jit_speed.py and test_nojit_speed.py
385
+
386
+ 3.0.0 (3/16/2025)
387
+ -------------------
388
+ * breaking api changes
389
+ * api is more sane: mie.efficiencies() instead of miepython.ez_mie()
390
+ * use core.py to cleanly separate jit and non-jit code
391
+ * new function to calculate mie coefficients inside sphere
392
+ * new function to calculate E-fields near and far from sphere (only works in far-field)
393
+ * new rayleigh.py
394
+ * new vsh.py to calculate vector spherical harmonics
395
+ * new util.py for printing complex numbers
396
+ * new bessel.py for complete spherical bessel function support
397
+ * put Monte Carlo routines into their own file
398
+ * use black for python formatting
399
+ * update all notebooks to use new api
400
+ * add more tests
401
+
402
+
403
+ 2.5.5 (12/01/2025)
404
+ -------------------
405
+ * add support for specific spherical modes
406
+ * only branch is now 'main'
407
+
408
+ 2.5.4 (05/07/2024)
409
+ -------------------
410
+ * add auto-dating in CITATION
411
+ * add python 3.12
412
+ * add ruff, pylint config
413
+ * simplify mie_cdf and fix notebook
414
+ * set 3.7 as earliest python version
415
+
416
+ v2.5.3 (8/5/2023)
417
+ -------------------
418
+ * conda-forge fails because test files are not included
419
+
420
+ v2.5.1 (8/5/2023)
421
+ -------------------
422
+ * change tests to accommodate conda-forge
423
+ * require python>=3.9 to accommodate latest numba
424
+ * get rid of tox
425
+
426
+ v2.5.0 (8/4/2023)
427
+ -------------------
428
+ * fix scattering function for very small spheres
429
+
430
+ v2.4.0 (6/10/2023)
431
+ -------------------
432
+ * add mie_phase_matrix() to calculate scattering (Mueller) matrix
433
+
434
+ v2.3.2
435
+ -------------------
436
+ * fix typo in README.rst that prevented pypi upload
437
+ * add CITATION.cff to base level of miepython repository
438
+
439
+ v2.3.1
440
+ -------------------
441
+ * add DOI for citation purposes
442
+
443
+ v2.3.0
444
+ -------------------
445
+ * add optional argument to change scattering function normalization
446
+ * document normalization in new notebook
447
+ * store data in correct place
448
+ * store version __init__.py so scripts can query it
449
+ * fix typo in header of gold sphere example script
450
+ * remove workaround for older Sphinx version
451
+
452
+ v2.2.3 (1/26/2022)
453
+ ------------------
454
+ * update _mie_An_Bn in miepython_nojit
455
+ * store data in module so github testing passes
456
+ * fix build of API documentation on miepython.readthedocs.io
457
+ * only test back to python 3.9 because importlib.resources
458
+ * 3.9 is only needed for a few of the jupyter notebooks
459
+
460
+ v2.2.2 (1/25/2022)
461
+ ------------------
462
+ * modify _mie_An_Bn to allocate and return An and Bn
463
+ * fix minor packaging issue
464
+ * explicitly define encoding when opening files
465
+ * explicitly use .readthedocs.yaml to build docs
466
+ * use rtd theme for docs
467
+ * add docs/requirements
468
+ * restrict Jinja2 to 2.11.3 in docs/requirements
469
+
470
+ v2.2.1 (9/5/2021)
471
+ -----------------
472
+ * create pure python packages
473
+ * include wheel file
474
+ * package as python3 only
475
+
476
+ 2.1.0 (05/22/21)
477
+ ----------------
478
+ * fix case when scalar angle used with mie_S1_S2()
479
+ * add pypi badge
480
+ * fix notebook testing
481
+ * thanks to @zmoon for the following changes:
482
+ * add requirements-dev.txt
483
+ * add example script testing
484
+ * add workflow testing
485
+ * fix Au/Ag error
486
+ * fix examples that use refractiveindex.info
487
+ * add testing badge
488
+
489
+ 2.0.1 (04/25/21)
490
+ ----------------
491
+ * fix packaging mistake
492
+
493
+ 2.0.0 (04/25/21)
494
+ ----------------
495
+ * use numba for 10-700X speed improvement
496
+ * thanks to @jbecca and @pscicluna for their help
497
+ * add performance notebook
498
+ * add automated notebook checking
499
+ * test more code
500
+
501
+ 1.3.3 (03/21/21)
502
+ ----------------
503
+ * colab badge and link
504
+ * change theme for sphinx documentation
505
+ * add requirements.txt to avoid installing sphinx
506
+ * fix restructured text errors
507
+ * advise everywhere to `pip install --user miepython` to avoid permission problems
508
+
509
+ 1.3.2 (01/13/21)
510
+ ----------------
511
+ * add ez_mie(m, d, lambda0)
512
+ * add ez_intensities(m, d, lambda0, mu)
513
+ * fix formatting
514
+ * fix api autodoc
515
+ * specify newer pythons
516
+ * better install instructions
517
+
518
+ 1.3.1 (03/30/20)
519
+ ----------------
520
+ * improve docstrings
521
+ * use Sphinx documentation
522
+ * host docs on readthedocs.io
523
+ * use tox
524
+
525
+ 1.3.0 (02/19/19)
526
+ ----------------
527
+ * fix calculations for small spheres (x<0.05)
528
+ * added notebook doc/09_backscattering.ipynb
529
+ * general tweaks to documentation throughout
530
+ * improved README.md
531
+
532
+ 1.2.0. (02/08/19)
533
+ -----------------
534
+ * fix bug so that large sphere calculations work correctly
535
+ * add tests for large spheres
536
+ * add tests for backscattering efficiency
537
+ * add documentation notebook for large spheres
538
+ * add direct links to documentation
539
+ * finish fixing fractions in notebooks
540
+ * improve README.md
541
+
542
+ 1.1.1. (06/25/18)
543
+ ------------------
544
+ * fix github rendering of fractions in equations
545
+ * add developer instructions
546
+ * fix fractions for github
547
+ * add missing doc files found my check-manifest
548
+ * setup.py fixes suggested by pyroma
549
+ * pep8 compliance and delinting using pylint
550
+ * add missing doc files found my check-manifest
551
+ * setup.py fixes suggested by pyroma
552
+ * pep8 compliance and delinting using pylint
553
+ * update version
554
+ * add notebook doc/08_large_spheres.ipynb
555
+
556
+ 1.1.0 (03/02/2018)
557
+ ------------------
558
+ * update version
559
+ * initial commit of 04_rayleigh.ipynb
560
+ * renamed doc files
561
+ * use new functions from miepython
562
+ * omit low level tests
563
+ * add __author__ and __version__
564
+ * add i_par, i_per, i_unpolarized, and hide private functions
565
+ * rename doc files
566
+ * add quantitative comparisons of angular scattering
567
+ * tweak verbiage
568
+ * ignore more
569
+ * initial commit
570
+ * more cleanup
571
+ * ignore dist files
572
+ * minor reorg of contents
573
+ * fix typos, add more refraction stuff
574
+ * Changes to match PEP8 style
575
+ * add minor comments, fix typos
576
+
577
+ 1.0.0 (08/27/2017)
578
+ ------------------
579
+ * Added docs in form of Jupyter notebooks
580
+
581
+ 0.4.2 (08/26/2017)
582
+ ------------------
583
+ * messed up github release 0.4.1
584
+
585
+ 0.4.1 (08/26/2017)
586
+ ------------------
587
+ * fix typo
588
+
589
+ 0.4.0 (08/26/2017)
590
+ ------------------
591
+ * update README to include basic testing
592
+ * mie(m,x) work automatically with arrays
593
+ * adding MANIFEST.in so examples get included
594
+
595
+ 0.3.2 (07/07/2017)
596
+ ------------------
597
+ * update README, bump to 0.3.2
598
+ * Fix examples so they work.
599
+
600
+ 0.3.1 (07/07/2017)
601
+ ------------------
602
+ * Bump version.
603
+ * Add functions to __init__.py.
604
+
605
+ 0.3.0 (07/07/2017)
606
+ ------------------
607
+ * Update README again.
608
+ * Update README.
609
+ * More packaging issues.
610
+ * Only include normalized scattering functions.
611
+ * Tweak setup.py and add .gitignore.
612
+ * Rename README.
613
+ * Add small sphere calc for S1 and S2.
614
+ * Label tests with MIEV0 cases.
615
+ * Rename example.
616
+ * Add gold sphere example.
617
+ * Add a few example programs.
618
+ * Remove unused tests.
619
+ * Remove extraneous ; simplify test.py, add tests.
620
+ * Simplify test suite management.
621
+ * Rename awkward test_miepython to just test.
622
+ * Reorganize tests, add S1 & S2 test.
623
+ * Added capabilities. Barely working test suite.
624
+ * Add more tests that fail.
625
+ * Move files around.
626
+ * Add boilerplate files and start adding unit tests.
627
+ * Rename to miepython.
628
+ * Initial check in.
@@ -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.2.0
6
6
  doi: 10.5281/zenodo.7949264
7
- date-released: '2026-01-03'
7
+ date-released: '2026-03-06'
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.2.0
37
37
  doi: 10.5281/zenodo.7949264
38
38
  url: https://doi.org/10.5281/zenodo.7949264
39
39
  authors:
@@ -0,0 +1,12 @@
1
+ # Core package files
2
+ include LICENSE.txt
3
+ include README.rst
4
+ include CHANGELOG.rst
5
+ include CITATION.cff
6
+
7
+ # Package data needed at runtime
8
+ include miepython/data/*.txt
9
+
10
+ # setuptools already picks up tests/test*.py, but conftest.py selects the backend
11
+ # each test file needs, so the shipped tests are wrong without it
12
+ recursive-include tests *.py