miepython 3.0.5__py3-none-any.whl → 3.2.0__py3-none-any.whl

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.
miepython/mie_jit.py CHANGED
@@ -321,8 +321,8 @@ def _S1_S2_nb(m, x, mu, n_pole):
321
321
  N = len(a)
322
322
  pi = np.zeros(N)
323
323
  tau = np.zeros(N)
324
- n = np.arange(1, N + 1)
325
- scale = (2 * n + 1) / ((n + 1) * n)
324
+ n = np.arange(1, N + 1, dtype=np.float64)
325
+ scale = (2.0 * n + 1.0) / ((n + 1.0) * n)
326
326
 
327
327
  nangles = len(mu)
328
328
  S1 = np.zeros(nangles, dtype=np.complex128)
@@ -331,8 +331,14 @@ def _S1_S2_nb(m, x, mu, n_pole):
331
331
  for k in range(nangles):
332
332
  _pi_tau_nb(mu[k], pi, tau)
333
333
  if n_pole == 0:
334
- S1[k] = np.sum(scale * (pi * a + tau * b))
335
- S2[k] = np.sum(scale * (tau * a + pi * b))
334
+ s1 = 0.0 + 0.0j
335
+ s2 = 0.0 + 0.0j
336
+ for i in range(N):
337
+ si = scale[i]
338
+ s1 += si * (pi[i] * a[i] + tau[i] * b[i])
339
+ s2 += si * (tau[i] * a[i] + pi[i] * b[i])
340
+ S1[k] = s1
341
+ S2[k] = s2
336
342
  else:
337
343
  S1[k] = scale[n_pole] * (pi[n_pole] * a[n_pole] + tau[n_pole] * b[n_pole])
338
344
  S2[k] = scale[n_pole] * (tau[n_pole] * a[n_pole] + pi[n_pole] * b[n_pole])
@@ -443,7 +449,7 @@ def _single_sphere_nb(m, x, n_pole, e_field):
443
449
  qback: the backscatter efficiency
444
450
  g: the average cosine of the scattering phase function
445
451
  """
446
- e_field = not e_field # unused
452
+ _ = e_field # unused
447
453
 
448
454
  # case when sphere matches its environment
449
455
  if abs(m.real - 1) <= 1e-8 and abs(m.imag) < 1e-8:
@@ -461,33 +467,60 @@ def _single_sphere_nb(m, x, n_pole, e_field):
461
467
  m = 1 - 10000j
462
468
 
463
469
  a, b = _an_bn_nb(m, x, n_pole)
470
+ x2 = x * x
464
471
 
465
472
  if n_pole == 0:
466
- n = np.arange(1, len(a) + 1)
467
- cn = 2.0 * n + 1.0
468
-
469
- qext = 2 * np.sum(cn * (a.real + b.real)) / x**2
470
-
471
- if m.imag == 0:
473
+ n_terms = len(a)
474
+ qext_acc = 0.0
475
+ qsca_acc = 0.0
476
+ qback_acc = 0.0 + 0.0j
477
+ g_acc = 0.0
478
+
479
+ for i in range(n_terms):
480
+ ni = i + 1
481
+ cn = 2.0 * ni + 1.0
482
+
483
+ ai = a[i]
484
+ bi = b[i]
485
+ ai_re = ai.real
486
+ ai_im = ai.imag
487
+ bi_re = bi.real
488
+ bi_im = bi.imag
489
+
490
+ qext_acc += cn * (ai_re + bi_re)
491
+
492
+ if m.imag != 0.0:
493
+ qsca_acc += cn * (ai_re * ai_re + ai_im * ai_im + bi_re * bi_re + bi_im * bi_im)
494
+
495
+ # (-1)^n with n starting from 1.
496
+ sign = -1.0 if (ni % 2) == 1 else 1.0
497
+ qback_acc += sign * cn * (ai - bi)
498
+
499
+ if i < n_terms - 1:
500
+ aip1 = a[i + 1]
501
+ bip1 = b[i + 1]
502
+ c1n = ni * (ni + 2.0) / (ni + 1.0)
503
+ c2n = cn / ni / (ni + 1.0)
504
+ g_acc += c1n * (ai * np.conjugate(aip1) + bi * np.conjugate(bip1)).real
505
+ g_acc += c2n * (ai * np.conjugate(bi)).real
506
+
507
+ qext = 2.0 * qext_acc / x2
508
+
509
+ if m.imag == 0.0:
472
510
  qsca = qext
473
511
  else:
474
- qsca = 2 * np.sum(cn * (np.abs(a) ** 2 + np.abs(b) ** 2)) / x**2
475
-
476
- qback = np.abs(np.sum((-1) ** n * cn * (a - b))) ** 2 / x**2
512
+ qsca = 2.0 * qsca_acc / x2
477
513
 
478
- c1n = n * (n + 2) / (n + 1)
479
- c2n = cn / n / (n + 1)
480
- asy1 = c1n[:-1] * (a[:-1] * a[1:].conjugate() + b[:-1] * b[1:].conjugate()).real
481
- asy2 = c2n[:-1] * (a[:-1] * b[:-1].conjugate()).real
482
- g = 4 * np.sum(asy1 + asy2) / qsca / x**2
514
+ qback = np.abs(qback_acc) ** 2 / x2
515
+ g = 4.0 * g_acc / qsca / x2
483
516
 
484
517
  else:
485
518
  cn = 2.0 * n_pole + 1
486
- qext = 2 * cn * (a[-1].real + b[-1].real) / x**2
487
- qback = np.abs((-1) ** n_pole * cn * (a[-1] - b[-1])) ** 2 / x**2
519
+ qext = 2.0 * cn * (a[-1].real + b[-1].real) / x2
520
+ qback = np.abs((-1) ** n_pole * cn * (a[-1] - b[-1])) ** 2 / x2
488
521
  qsca = qext
489
522
  if m.imag < 0:
490
- qsca = 2 * cn * (np.abs(a[-1]) ** 2 + np.abs(b[-1]) ** 2) / x**2
523
+ qsca = 2.0 * cn * (np.abs(a[-1]) ** 2 + np.abs(b[-1]) ** 2) / x2
491
524
  g = 0
492
525
 
493
526
  return qext, qsca, qback, g
miepython/mie_nojit.py CHANGED
@@ -2,6 +2,8 @@
2
2
  Low-level Mie calculations that do not use numba.
3
3
  """
4
4
 
5
+ from functools import lru_cache
6
+
5
7
  import numpy as np
6
8
 
7
9
  __all__ = (
@@ -16,6 +18,31 @@ __all__ = (
16
18
  )
17
19
 
18
20
 
21
+ @lru_cache(maxsize=128)
22
+ def _series_scale_factors(n_terms):
23
+ """Return cached per-order scale factors for Mie series summations."""
24
+ n = np.arange(1, n_terms + 1, dtype=np.float64)
25
+ scale = (2.0 * n + 1.0) / ((n + 1.0) * n)
26
+ scale.setflags(write=False)
27
+ return scale
28
+
29
+
30
+ @lru_cache(maxsize=128)
31
+ def _single_sphere_factors(n_terms):
32
+ """Return cached per-order factors used by ``_single_sphere_py``."""
33
+ n_int = np.arange(1, n_terms + 1, dtype=np.int64)
34
+ n = n_int.astype(np.float64)
35
+ cn = 2.0 * n + 1.0
36
+ alt = np.where((n_int % 2) == 0, 1.0, -1.0) # (-1)^n with n starting at 1
37
+ c1n = n * (n + 2.0) / (n + 1.0)
38
+ c2n = cn / n / (n + 1.0)
39
+ cn.setflags(write=False)
40
+ alt.setflags(write=False)
41
+ c1n.setflags(write=False)
42
+ c2n.setflags(write=False)
43
+ return cn, alt, c1n, c2n
44
+
45
+
19
46
  def _Lentz_Dn(z, N):
20
47
  """
21
48
  Compute the logarithmic derivative of the Ricatti-Bessel function.
@@ -139,7 +166,7 @@ def _an_bn_py(m, x, n_pole=0):
139
166
  Returns:
140
167
  a, b: arrays of Mie coefficents An and Bn
141
168
  """
142
- if np.imag(m) > 0: # ensure imaginary part of refractive index is negative
169
+ if m.imag > 0: # ensure imaginary part of refractive index is negative
143
170
  m = np.conj(m)
144
171
 
145
172
  if n_pole == 0:
@@ -152,21 +179,24 @@ def _an_bn_py(m, x, n_pole=0):
152
179
  if x <= 0:
153
180
  return a, b
154
181
 
182
+ inv_x = 1.0 / x
155
183
  psi_nm1 = np.sin(x) # nm1 = n-1 = 0
156
- psi_n = psi_nm1 / x - np.cos(x)
184
+ psi_n = psi_nm1 * inv_x - np.cos(x)
157
185
  xi_nm1 = np.complex128(psi_nm1 + 1j * np.cos(x))
158
- xi_n = np.complex128(psi_n + 1j * (np.cos(x) / x + np.sin(x)))
186
+ xi_n = np.complex128(psi_n + 1j * (np.cos(x) * inv_x + np.sin(x)))
159
187
 
160
188
  if m.real > 0.0:
161
189
  D = _D_calc_py(m, x, nstop + 1)
162
190
 
163
191
  for n in range(1, nstop):
164
- temp = D[n - 1] / m + n / x
192
+ n_over_x = n * inv_x
193
+ temp = D[n - 1] / m + n_over_x
165
194
  a[n - 1] = (temp * psi_n - psi_nm1) / (temp * xi_n - xi_nm1)
166
- temp = D[n - 1] * m + n / x
195
+ temp = D[n - 1] * m + n_over_x
167
196
  b[n - 1] = (temp * psi_n - psi_nm1) / (temp * xi_n - xi_nm1)
168
- psi = (2 * n + 1) * psi_n / x - psi_nm1
169
- xi = (2 * n + 1) * xi_n / x - xi_nm1
197
+ two_np1_over_x = (2 * n + 1) * inv_x
198
+ psi = two_np1_over_x * psi_n - psi_nm1
199
+ xi = two_np1_over_x * xi_n - xi_nm1
170
200
  xi_nm1 = xi_n
171
201
  xi_n = xi
172
202
  psi_nm1 = psi_n
@@ -174,9 +204,10 @@ def _an_bn_py(m, x, n_pole=0):
174
204
 
175
205
  else:
176
206
  for n in range(1, nstop):
177
- a[n - 1] = (n * psi_n / x - psi_nm1) / (n * xi_n / x - xi_nm1)
207
+ n_over_x = n * inv_x
208
+ a[n - 1] = (n_over_x * psi_n - psi_nm1) / (n_over_x * xi_n - xi_nm1)
178
209
  b[n - 1] = psi_n / xi_n
179
- xi = (2 * n + 1) * xi_n / x - xi_nm1
210
+ xi = (2 * n + 1) * inv_x * xi_n - xi_nm1
180
211
  xi_nm1 = xi_n
181
212
  xi_n = xi
182
213
  psi_nm1 = psi_n
@@ -216,35 +247,39 @@ def _cn_dn_py(m, x, n_pole):
216
247
  if x <= 0:
217
248
  return c, d
218
249
 
250
+ inv_x = 1.0 / x
219
251
  # no need to calculate anything when sphere is perfectly conducting
220
252
  if m.real > 0.0 and not np.isinf(m.real) or not np.isinf(m.imag):
221
253
  psi_nm1 = np.sin(x) # nm1 = n-1 = 0
222
- psi_n = psi_nm1 / x - np.cos(x)
254
+ psi_n = psi_nm1 * inv_x - np.cos(x)
223
255
 
256
+ inv_mx = 1.0 / mx
224
257
  psi_nm1_mx = np.sin(mx) # nm1 = n-1 = 0
225
- psi_n_mx = psi_nm1_mx / mx - np.cos(mx)
258
+ psi_n_mx = psi_nm1_mx * inv_mx - np.cos(mx)
226
259
 
227
260
  xi_nm1 = np.complex128(psi_nm1 + 1j * np.cos(x))
228
- xi_n = np.complex128(psi_n + 1j * (np.cos(x) / x + np.sin(x)))
261
+ xi_n = np.complex128(psi_n + 1j * (np.cos(x) * inv_x + np.sin(x)))
229
262
 
230
263
  Dmx = _D_calc_py(np.complex128(m), x, nstop + 1)
231
264
  Dx = _D_calc_py(np.complex128(1), x, nstop + 1)
232
265
 
233
266
  for n in range(1, nstop + 1):
234
- common = (psi_n / psi_n_mx) * ((Dx[n - 1] + n / x) * xi_n - xi_nm1)
267
+ n_over_x = n * inv_x
268
+ common = (psi_n / psi_n_mx) * ((Dx[n - 1] + n_over_x) * xi_n - xi_nm1)
235
269
 
236
- c[n - 1] = m * common / ((m * Dmx[n - 1] + n / x) * xi_n - xi_nm1)
237
- d[n - 1] = common / ((Dmx[n - 1] / m + n / x) * xi_n - xi_nm1)
270
+ c[n - 1] = m * common / ((m * Dmx[n - 1] + n_over_x) * xi_n - xi_nm1)
271
+ d[n - 1] = common / ((Dmx[n - 1] / m + n_over_x) * xi_n - xi_nm1)
238
272
 
239
- psi = (2 * n + 1) * psi_n / x - psi_nm1
273
+ two_np1 = 2 * n + 1
274
+ psi = two_np1 * inv_x * psi_n - psi_nm1
240
275
  psi_nm1 = psi_n
241
276
  psi_n = psi
242
277
 
243
- psi_mx = (2 * n + 1) * psi_n_mx / mx - psi_nm1_mx
278
+ psi_mx = two_np1 * inv_mx * psi_n_mx - psi_nm1_mx
244
279
  psi_nm1_mx = psi_n_mx
245
280
  psi_n_mx = psi_mx
246
281
 
247
- xi = (2 * n + 1) * xi_n / x - xi_nm1
282
+ xi = two_np1 * inv_x * xi_n - xi_nm1
248
283
  xi_nm1 = xi_n
249
284
  xi_n = xi
250
285
 
@@ -310,8 +345,9 @@ def _S1_S2_py(m, x, mu, n_pole):
310
345
  N = len(a)
311
346
  pi = np.zeros(N)
312
347
  tau = np.zeros(N)
313
- n = np.arange(1, N + 1)
314
- scale = (2 * n + 1) / ((n + 1) * n)
348
+ scale = _series_scale_factors(N)
349
+ scale_a = scale * a
350
+ scale_b = scale * b
315
351
 
316
352
  nangles = len(mu)
317
353
  S1 = np.zeros(nangles, dtype=np.complex128)
@@ -320,8 +356,8 @@ def _S1_S2_py(m, x, mu, n_pole):
320
356
  for k in range(nangles):
321
357
  _pi_tau_py(mu[k], pi, tau)
322
358
  if n_pole == 0:
323
- S1[k] = np.sum(scale * (pi * a + tau * b))
324
- S2[k] = np.sum(scale * (tau * a + pi * b))
359
+ S1[k] = np.dot(pi, scale_a) + np.dot(tau, scale_b)
360
+ S2[k] = np.dot(tau, scale_a) + np.dot(pi, scale_b)
325
361
  else:
326
362
  S1[k] = scale[n_pole] * (pi[n_pole] * a[n_pole] + tau[n_pole] * b[n_pole])
327
363
  S2[k] = scale[n_pole] * (tau[n_pole] * a[n_pole] + pi[n_pole] * b[n_pole])
@@ -429,7 +465,7 @@ def _single_sphere_py(m, x, n_pole, e_field):
429
465
  qback: the backscatter efficiency
430
466
  g: the average cosine of the scattering phase function
431
467
  """
432
- e_field = not e_field # unused
468
+ _ = e_field # currently unused in scalar aggregate efficiencies
433
469
 
434
470
  # case when sphere matches its environment
435
471
  if abs(m.real - 1) <= 1e-8 and abs(m.imag) < 1e-8:
@@ -447,33 +483,36 @@ def _single_sphere_py(m, x, n_pole, e_field):
447
483
  m = 1 - 10000j
448
484
 
449
485
  a, b = _an_bn_py(m, x, n_pole)
486
+ x2 = x * x
450
487
 
451
488
  if n_pole == 0:
452
- n = np.arange(1, len(a) + 1)
453
- cn = 2.0 * n + 1.0
489
+ n_terms = len(a)
490
+ cn, alt, c1n, c2n = _single_sphere_factors(n_terms)
491
+ a_re = a.real
492
+ b_re = b.real
493
+ a_abs2 = a_re * a_re + a.imag * a.imag
494
+ b_abs2 = b_re * b_re + b.imag * b.imag
454
495
 
455
- qext = 2 * np.sum(cn * (a.real + b.real)) / x**2
496
+ qext = 2.0 * np.dot(cn, a_re + b_re) / x2
456
497
 
457
498
  if m.imag == 0:
458
499
  qsca = qext
459
500
  else:
460
- qsca = 2 * np.sum(cn * (np.abs(a) ** 2 + np.abs(b) ** 2)) / x**2
501
+ qsca = 2.0 * np.dot(cn, a_abs2 + b_abs2) / x2
461
502
 
462
- qback = np.abs(np.sum((-1) ** n * cn * (a - b))) ** 2 / x**2
503
+ qback = np.abs(np.dot(alt * cn, a - b)) ** 2 / x2
463
504
 
464
- c1n = n * (n + 2) / (n + 1)
465
- c2n = cn / n / (n + 1)
466
505
  asy1 = c1n[:-1] * (a[:-1] * a[1:].conjugate() + b[:-1] * b[1:].conjugate()).real
467
506
  asy2 = c2n[:-1] * (a[:-1] * b[:-1].conjugate()).real
468
- g = 4 * np.sum(asy1 + asy2) / qsca / x**2
507
+ g = 4.0 * np.sum(asy1 + asy2) / qsca / x2
469
508
 
470
509
  else:
471
510
  cn = 2.0 * n_pole + 1
472
- qback = np.abs((-1) ** n_pole * cn * (a[-1] - b[-1])) ** 2 / x**2
473
- qext = 2 * cn * (a[-1].real + b[-1].real) / x**2
511
+ qback = np.abs((-1) ** n_pole * cn * (a[-1] - b[-1])) ** 2 / x2
512
+ qext = 2.0 * cn * (a[-1].real + b[-1].real) / x2
474
513
  qsca = qext
475
514
  if m.imag < 0:
476
- qsca = 2 * cn * (np.abs(a[-1]) ** 2 + np.abs(b[-1]) ** 2) / x**2
515
+ qsca = 2.0 * cn * (np.abs(a[-1]) ** 2 + np.abs(b[-1]) ** 2) / x2
477
516
  g = None
478
517
 
479
518
  return qext, qsca, qback, g
miepython/vsh.py CHANGED
@@ -311,7 +311,9 @@ 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
- factor2 = factor1 * mie._D_calc(m_index, kr, 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
+ factor2 = factor1 * Dn
315
317
  factor1 /= rho
316
318
  else:
317
319
  factor1 = spherical_h1(n, rho) / rho
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: miepython
3
- Version: 3.0.5
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,17 +14,45 @@ 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
25
26
  Requires-Dist: matplotlib
26
- Requires-Dist: numba
27
27
  Requires-Dist: scipy
28
+ Requires-Dist: numba; sys_platform != "emscripten"
29
+ Provides-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"
33
+ Requires-Dist: sphinx_automodapi; extra == "docs"
34
+ Requires-Dist: sphinx_rtd_theme; extra == "docs"
35
+ Provides-Extra: dev
36
+ Requires-Dist: pytest; extra == "dev"
37
+ Requires-Dist: imageio; extra == "dev"
38
+ Requires-Dist: requests; extra == "dev"
39
+ Requires-Dist: ruff; extra == "dev"
40
+ Requires-Dist: yamllint; extra == "dev"
41
+ Requires-Dist: rstcheck[sphinx,toml]; extra == "dev"
42
+ Requires-Dist: pylint; extra == "dev"
43
+ Requires-Dist: pyroma; extra == "dev"
44
+ Requires-Dist: black[jupyter]; extra == "dev"
45
+ Requires-Dist: build; extra == "dev"
46
+ Requires-Dist: check-manifest; extra == "dev"
47
+ Requires-Dist: libarchive-c; extra == "dev"
48
+ Requires-Dist: pyyaml; extra == "dev"
49
+ Requires-Dist: setuptools; extra == "dev"
50
+ Requires-Dist: jupyterlab; extra == "dev"
51
+ Requires-Dist: nbconvert; extra == "dev"
52
+ Requires-Dist: nbformat; extra == "dev"
53
+ Provides-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"
28
56
  Dynamic: license-file
29
57
 
30
58
  .. |pypi| image:: https://img.shields.io/pypi/v/miepython?color=68CA66
@@ -47,8 +75,8 @@ Dynamic: license-file
47
75
  :target: https://github.com/scottprahl/miepython/blob/master/LICENSE.txt
48
76
  :alt: License
49
77
 
50
- .. |test| image:: https://github.com/scottprahl/miepython/actions/workflows/test.yml/badge.svg
51
- :target: https://github.com/scottprahl/miepython/actions/workflows/test.yml
78
+ .. |test| image:: https://github.com/scottprahl/miepython/actions/workflows/test.yaml/badge.svg
79
+ :target: https://github.com/scottprahl/miepython/actions/workflows/test.yaml
52
80
  :alt: Testing
53
81
 
54
82
  .. |docs| image:: https://readthedocs.org/projects/miepython/badge?color=68CA66
@@ -86,11 +114,13 @@ The library implements the full Mie solution, including:
86
114
  - angle-resolved scattering intensities
87
115
  - Mie expansion coefficients
88
116
  - complex amplitude functions and Mueller matrices
117
+ - E-field and H-field calculations in the near-field around a sphere
89
118
 
90
119
  The implementation is numerically stable for a wide range of size parameters and refractive indices, including lossy materials and high-index contrasts.
91
120
 
92
- Immediate Use in the Browser
93
- ----------------------------
121
+
122
+ JupyterLite Implementation
123
+ --------------------------
94
124
 
95
125
  The entire package can be used **immediately** in a browser — without installation — using the JupyterLite interface:
96
126
 
@@ -138,33 +168,10 @@ A typical calculation is straightforward:
138
168
  Documentation and Examples
139
169
  --------------------------
140
170
 
141
- The full documentation is available as:
171
+ The full documentation is available on ReadTheDocs:
142
172
 
143
- Interactive documentation (JupyterLite):
144
- |lite|
145
-
146
- Static Jupyter notebooks on ReadTheDocs:
147
173
  |docs|
148
174
 
149
- Among other things the documentation discusses:
150
-
151
- - **Mathematical formulation of Mie theory**
152
- https://miepython.readthedocs.io/en/latest/01_theory.html
153
-
154
- - **Normalization conventions and units**
155
- https://miepython.readthedocs.io/en/latest/02_normalization.html
156
-
157
- - **Numerical stability considerations**
158
- https://miepython.readthedocs.io/en/latest/03_stability.html
159
-
160
- - **Validation against MIEV0 and other reference implementations**
161
- https://miepython.readthedocs.io/en/latest/04_validation.html
162
-
163
- - **Guidelines for parameter choices and truncation order**
164
- https://miepython.readthedocs.io/en/latest/05_truncation.html
165
-
166
- - **Physical interpretation, resonances, and comparison plots**
167
- https://miepython.readthedocs.io/en/latest/06_examples.html
168
175
 
169
176
  Representative results simple examples:
170
177
 
@@ -174,6 +181,7 @@ Representative results simple examples:
174
181
  .. image:: https://raw.githubusercontent.com/scottprahl/miepython/main/docs/images/02.svg
175
182
  .. image:: https://raw.githubusercontent.com/scottprahl/miepython/main/docs/images/03.svg
176
183
  .. image:: https://raw.githubusercontent.com/scottprahl/miepython/main/docs/images/04.svg
184
+ .. image:: https://raw.githubusercontent.com/scottprahl/miepython/main/docs/images/05.svg
177
185
 
178
186
  Performance and Acceleration
179
187
  ----------------------------
@@ -202,19 +210,19 @@ Citation
202
210
 
203
211
  If you use ``laserbeamsize`` in academic, instructional, or applied technical work, please cite:
204
212
 
205
- 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
206
214
 
207
215
  BibTeX:
208
216
 
209
217
  .. code-block:: bibtex
210
218
 
211
- @software{prahl_miepython_2025,
219
+ @software{prahl_miepython_2026,
212
220
  author = {Scott Prahl},
213
221
  title = {{miepython}: A Python library for Mie scattering calculations},
214
222
  url = {https://github.com/scottprahl/miepython},
215
223
  doi = {10.5281/zenodo.7949263},
216
224
  year = {2026},
217
- version = {3.0.5},
225
+ version = {3.1.0},
218
226
  publisher = {Zenodo}
219
227
  }
220
228
 
@@ -0,0 +1,18 @@
1
+ miepython/__init__.py,sha256=Hs1MtLbYN1trH8aAIT4uiZASWTtCTu3gki5MNq3HdXs,4516
2
+ miepython/_backend.py,sha256=l_faAa6zanygRZKA9vMVcfcutHPFFkpqZzHqwjU8QcE,1619
3
+ miepython/bessel.py,sha256=U094wY_5RFqrVjXZgMbacmt_yqpP8GBtRZ24-MNzBsU,3647
4
+ miepython/core.py,sha256=_6bceok2AZ5-rxgLw8AZ0qR-x0dPfgjoaY7EUE12FaE,19404
5
+ miepython/field.py,sha256=_kD5lAFJPGhBEjx33wBzqaxho2YBLBImKz5IRmlk8lY,26979
6
+ miepython/mie_jit.py,sha256=wSxuo3Dhhm08tTcWRJeuNxwziM6mnlPwnQHZkaDFILs,16282
7
+ miepython/mie_nojit.py,sha256=Vu5jQLr-wNWjAbNSdNfJynyizn_mWL8kX-KtowYGywU,16216
8
+ miepython/monte_carlo.py,sha256=gemJ2vhUyiTg-hAneGRW3typd3SFM2LL4R2o1UAzLeA,3818
9
+ miepython/rayleigh.py,sha256=P-rpa9SKVH3RaxuUUiZzQYYk6c3cFF6vDAAgo9xChz8,9401
10
+ miepython/util.py,sha256=LQEDkJqz6GHVHYog2JzTE12Ibv2ncLeMI20S8IjZzAc,2908
11
+ miepython/vsh.py,sha256=7uHo2o_cFJ2NzB7QfTI3ABJGEZSaO7ndUdxlY5BG3bo,17532
12
+ miepython/data/Johnson.txt,sha256=PcdEhJ3ZKUnt--86ukEvUt-w-814uOyWXtE-G3qWTNE,1337
13
+ miepython/data/segelstein81_index.txt,sha256=aJLqcPIkw-0WDY37GRcTefLV0WGhyMt6IbOKY8JxGww,36719
14
+ miepython-3.2.0.dist-info/licenses/LICENSE.txt,sha256=g9-BWWRLvMDMQ1Zflxliku7Sleom1_SbXHQHOLGgqqk,1055
15
+ miepython-3.2.0.dist-info/METADATA,sha256=LNH39wbRza2D0DymlOpJ2FBAPsy9k08zwfsxIpYTjKE,7795
16
+ miepython-3.2.0.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
17
+ miepython-3.2.0.dist-info/top_level.txt,sha256=bfkk6YwrKm_aIzInR5y03YbywpwCzUjJEHaFkEiSKxI,10
18
+ miepython-3.2.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (82.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,17 +0,0 @@
1
- miepython/__init__.py,sha256=D6Uc5ZJzlTLwziyzYaE7KCzBwH9N9cStRh2_zlYgiaE,3244
2
- miepython/bessel.py,sha256=U094wY_5RFqrVjXZgMbacmt_yqpP8GBtRZ24-MNzBsU,3647
3
- miepython/core.py,sha256=wMFAqHL252RLXhsh84JNoLmBpvXNKdZl29aSAMGRjNQ,19389
4
- miepython/field.py,sha256=Ts1QZCHsTLkIu6gzCwyuPtHyC_SWW2gggPv2-9RHkmk,6499
5
- miepython/mie_jit.py,sha256=Dr2cbv1Scotp3fxkHsFEg5gSLPVlzF1u_xjySF2F_88,15441
6
- miepython/mie_nojit.py,sha256=yt3iWmjQY5bp1qHPVUCGGhms-qkKnLQufyjI_u8FB8Y,14942
7
- miepython/monte_carlo.py,sha256=gemJ2vhUyiTg-hAneGRW3typd3SFM2LL4R2o1UAzLeA,3818
8
- miepython/rayleigh.py,sha256=P-rpa9SKVH3RaxuUUiZzQYYk6c3cFF6vDAAgo9xChz8,9401
9
- miepython/util.py,sha256=LQEDkJqz6GHVHYog2JzTE12Ibv2ncLeMI20S8IjZzAc,2908
10
- miepython/vsh.py,sha256=f7WBSoZou_w0FtpYitl6cTmjrFpi0hwSOjJ9X8tNFEk,17430
11
- miepython/data/Johnson.txt,sha256=PcdEhJ3ZKUnt--86ukEvUt-w-814uOyWXtE-G3qWTNE,1337
12
- miepython/data/segelstein81_index.txt,sha256=aJLqcPIkw-0WDY37GRcTefLV0WGhyMt6IbOKY8JxGww,36719
13
- miepython-3.0.5.dist-info/licenses/LICENSE.txt,sha256=g9-BWWRLvMDMQ1Zflxliku7Sleom1_SbXHQHOLGgqqk,1055
14
- miepython-3.0.5.dist-info/METADATA,sha256=JtouOwg6loYnfq0Cf77RR4G9de8gCXWHGKzpEcLkiSI,7280
15
- miepython-3.0.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
- miepython-3.0.5.dist-info/top_level.txt,sha256=bfkk6YwrKm_aIzInR5y03YbywpwCzUjJEHaFkEiSKxI,10
17
- miepython-3.0.5.dist-info/RECORD,,