Skip to content

Commit 7c666ac

Browse files
committed
removed dimension checks and many reshaping operations throughout
assumption is that m and x are specified as arrays with at least 2 dimensions
1 parent ee344f0 commit 7c666ac

File tree

4 files changed

+92
-64
lines changed

4 files changed

+92
-64
lines changed

pymie/mie.py

Lines changed: 49 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ def calc_ang_scat(m, x, angles, mie = True, check = False):
8787
"""
8888
if mie:
8989
# Mie scattering preliminaries
90-
nstop = _nstop(np.array(x).max())
90+
nstop = _nstop(x.max())
9191

9292
coeffs = _scatcoeffs(m, x, nstop)
9393
n = np.arange(nstop)+1.
@@ -118,6 +118,7 @@ def calc_ang_scat(m, x, angles, mie = True, check = False):
118118

119119
return np.array([ipar, iperp])
120120

121+
121122
def calc_cross_sections(m, x, eps1 = DEFAULT_EPS1,
122123
eps2 = DEFAULT_EPS2):
123124
"""
@@ -156,7 +157,7 @@ def calc_cross_sections(m, x, eps1 = DEFAULT_EPS1,
156157
157158
where I_0 is the incident intensity. See van de Hulst, p. 14.
158159
"""
159-
lmax = _nstop(np.array(x).max())
160+
lmax = _nstop(x.max())
160161
albl = _scatcoeffs(m, x, lmax, eps1=eps1, eps2=eps2)
161162

162163
cscat, cext, cback = tuple(c*2*np.pi for c in
@@ -172,6 +173,7 @@ def calc_cross_sections(m, x, eps1 = DEFAULT_EPS1,
172173

173174
return cscat, cext, cabs, cback, asym
174175

176+
175177
def calc_efficiencies(m, x):
176178
"""
177179
Scattering, extinction, backscattering efficiencies
@@ -181,36 +183,38 @@ def calc_efficiencies(m, x):
181183
scattering cross-section in the backscattering direction, divided by the
182184
geometrical cross-section
183185
"""
184-
nstop = _nstop(np.array(x).max())
186+
nstop = _nstop(x.max())
185187
coeffs = _scatcoeffs(m, x, nstop)
186188

187189
cscat, cext, cback = _cross_sections(coeffs[0], coeffs[1])
188190

189191
# for multilayer spheres, scale by the size parameter corresponding to
190192
# outermost radius
191-
x_outer = np.atleast_2d(x).max(axis=-1)
193+
x_outer = x.max(axis=-1)
192194
qscat = cscat * 2./np.abs(x_outer)**2
193195
qext = cext * 2./np.abs(x_outer)**2
194196
qback = cback * 1./np.abs(x_outer)**2
195197

196198
# in order: scattering, extinction and backscattering efficiency
197199
return qscat, qext, qback
198200

201+
199202
def calc_g(m, x, nstop=None):
200203
"""
201204
Asymmetry parameter
202205
"""
203206
if nstop is None:
204-
nstop = _nstop(np.array(x).max())
207+
nstop = _nstop(x.max())
205208
coeffs = _scatcoeffs(m, x, nstop)
206209

207210
# for multilayer particle, need to scale by the x of the outermost layer
208-
outer_x = np.array(x).max(axis=-1)
211+
outer_x = x.max(axis=-1)
209212
cscat = _cross_sections(coeffs[0], coeffs[1])[0] * 2./outer_x**2
210213
g = ((4./(outer_x**2 * cscat))
211214
* _asymmetry_parameter(coeffs[0], coeffs[1]))
212215
return g
213216

217+
214218
def calc_integrated_cross_section(m, x, thetas):
215219
"""
216220
Calculate (dimensionless) integrated cross section using quadrature
@@ -234,13 +238,12 @@ def calc_integrated_cross_section(m, x, thetas):
234238
"""
235239
form_factor = calc_ang_scat(m, x, thetas)
236240

237-
integrand_par = form_factor[0]*np.sin(thetas)
238-
integrand_perp = form_factor[1]*np.sin(thetas)
241+
integrand = form_factor * np.sin(thetas)
242+
integral = 2 * np.pi * np.trapezoid(integrand, x=thetas)
239243

240-
integral_par = 2 * np.pi * np.trapezoid(integrand_par, x=thetas)
241-
integral_perp = 2 * np.pi * np.trapezoid(integrand_perp, x=thetas)
244+
# average over two polarizations
245+
return (integral.sum(axis=0))/2.0
242246

243-
return (integral_par + integral_perp)/2.0
244247

245248
def calc_energy(radius, n_medium, m, x, nstop,
246249
eps1 = DEFAULT_EPS1, eps2 = DEFAULT_EPS2):
@@ -276,6 +279,7 @@ def calc_energy(radius, n_medium, m, x, nstop,
276279

277280
return W
278281

282+
279283
def calc_dwell_time(radius, n_medium, n_particle, wavelen,
280284
min_angle=0.01, num_angles=200,
281285
eps1 = DEFAULT_EPS1, eps2 = DEFAULT_EPS2):
@@ -339,6 +343,7 @@ def calc_dwell_time(radius, n_medium, n_particle, wavelen,
339343

340344
return dwell_time
341345

346+
342347
# TODO: document and test the correctness of this function, or delete (it is
343348
# not currently used anywhere)
344349
@ureg.check('[length]', None, None, '[length]', None, None)
@@ -375,6 +380,7 @@ def calc_reflectance(radius, n_medium, n_particle, wavelen,
375380

376381
return reflectance
377382

383+
378384
# Mie functions used internally
379385

380386
def _pis_and_taus(nstop, thetas):
@@ -408,6 +414,8 @@ def _pis_and_taus(nstop, thetas):
408414
ang_shape = list(thetas.shape)
409415

410416
# flatten to make calculations easier
417+
# TODO if thetas specified as meshgrid, this will square the number of
418+
# calculations done
411419
thetas = np.ndarray.flatten(thetas)
412420

413421
mu = np.cos(thetas)
@@ -433,12 +441,12 @@ def _pis_and_taus(nstop, thetas):
433441
taus = np.reshape(taus, ang_shape)
434442
return pis[...,1:nstop+1], taus[...,1:nstop+1]
435443

444+
436445
def _scatcoeffs(m, x, nstop, eps1 = DEFAULT_EPS1, eps2 = DEFAULT_EPS2):
437446
# index ratio should be specified as a 2D array with shape
438447
# [num_values, num_layers] to calculate over a set of different values,
439448
# such as wavelengths. If specified as a 1D array, shape is [num_layers].
440-
num_layers = np.atleast_2d(m).shape[-1]
441-
if num_layers > 1:
449+
if m.shape[-1] > 1:
442450
return _scatcoeffs_multi(m, x)
443451

444452
# Scattering coefficients for single-layer particles.
@@ -447,17 +455,13 @@ def _scatcoeffs(m, x, nstop, eps1 = DEFAULT_EPS1, eps2 = DEFAULT_EPS2):
447455
# nmx = np.array([nstop, np.round(np.absolute(m*x))]).max() + 20
448456
# Dnmx = mie_specfuncs.log_der_1(m*x, nmx, nstop)
449457
# above replaced with Lentz algorithm
450-
z = np.atleast_1d(m * x)
451-
# remove unneded layer axis
452-
z = z[..., 0]
458+
z = m * x
459+
z = z[..., 0] # remove unneeded layer axis
453460
Dnmx = mie_specfuncs.dn_1_down(z, nstop + 1, nstop,
454461
mie_specfuncs.lentz_dn1(z, nstop + 1,
455462
eps1, eps2))
456463

457464
n = np.arange(nstop+1)
458-
if np.ndim(x) <= 1:
459-
x = np.atleast_1d(x)[..., np.newaxis]
460-
461465
psi, xi = mie_specfuncs.riccati_psi_xi(x, nstop)
462466

463467
# insert zeroes at the beginning of second axis (order axis)
@@ -469,6 +473,7 @@ def _scatcoeffs(m, x, nstop, eps1 = DEFAULT_EPS1, eps2 = DEFAULT_EPS2):
469473
# coefficient array has shape [2, num_values, nstop]
470474
return np.array([an[..., 1:nstop+1], bn[..., 1:nstop+1]])
471475

476+
472477
def _scatcoeffs_multi(marray, xarray, nstop=None, eps1 = 1e-3, eps2 = 1e-16):
473478
'''Calculate scattered field expansion coefficients (in the Mie formalism)
474479
for a particle with an arbitrary number of spherically symmetric layers
@@ -498,8 +503,8 @@ def _scatcoeffs_multi(marray, xarray, nstop=None, eps1 = 1e-3, eps2 = 1e-16):
498503
499504
'''
500505
# ensure correct data types and shapes
501-
marray = np.atleast_2d(marray).astype(complex)
502-
xarray = np.atleast_2d(xarray).astype(complex)
506+
marray = marray.astype(complex)
507+
xarray = xarray.astype(complex)
503508

504509
# sanity check: marray and xarray must be same size
505510
if marray.size != xarray.size:
@@ -585,6 +590,7 @@ def _scatcoeffs_multi(marray, xarray, nstop=None, eps1 = 1e-3, eps2 = 1e-16):
585590
# output begins at n=1
586591
return np.array([an[..., 1:nstop+1], bn[..., 1:nstop+1]])
587592

593+
588594
def _internal_coeffs(m, x, n_max, eps1 = DEFAULT_EPS1, eps2 = DEFAULT_EPS2):
589595
'''
590596
Calculate internal Mie coefficients c_n and d_n given
@@ -594,13 +600,13 @@ def _internal_coeffs(m, x, n_max, eps1 = DEFAULT_EPS1, eps2 = DEFAULT_EPS2):
594600
have different conventions (labeling of c_n and d_n and factors of m)
595601
for their internal coefficients.
596602
'''
597-
if np.atleast_2d(x).shape[-1] > 1:
603+
if x.shape[-1] > 1:
598604
raise ValueError("Internal Mie coefficients cannot yet be calculated "
599605
"for layered sphere")
600606

601-
m = np.reshape(m, (np.size(m), 1)).astype(complex)
602-
x = np.reshape(x, (np.size(x), 1)).astype(complex)
603-
z = np.array(m * x)
607+
m = m.astype(complex)
608+
x = x.astype(complex)
609+
z = m * x
604610

605611
ratio = mie_specfuncs.R_psi(x, z, n_max, eps1, eps2)
606612
D1x, D3x = mie_specfuncs.log_der_13(x, n_max, eps1, eps2)
@@ -653,6 +659,7 @@ def _trans_coeffs(m, x, n_max, eps1 = DEFAULT_EPS1, eps2 = DEFAULT_EPS2):
653659

654660
return np.array([cn, dn])
655661

662+
656663
def _time_coeffs(m, x, nstop, eps1 = DEFAULT_EPS1, eps2 = DEFAULT_EPS2):
657664
'''
658665
Calculate what we refer to as the time Mie coefficients gamma_n and An,
@@ -681,6 +688,7 @@ def _time_coeffs(m, x, nstop, eps1 = DEFAULT_EPS1, eps2 = DEFAULT_EPS2):
681688

682689
return gamma_n, An
683690

691+
684692
def _W0(radius, n_medium):
685693
'''
686694
Calculates the time-averaged electromagnetic energy of a sphere having the
@@ -707,6 +715,7 @@ def _W0(radius, n_medium):
707715

708716
return W0
709717

718+
710719
def _nstop(x):
711720
# Takes size parameter, outputs order to compute. Previously used
712721
# criterion from Wiscombe, Applied Optics 19, 1505 (1980):
@@ -716,6 +725,7 @@ def _nstop(x):
716725
# (J. R. Allardice and E. C. Le Ru, Applied Optics, Vol. 53, No. 31 (2014).
717726
return (np.round(np.absolute(x+11*x**(1./3.)+1))).squeeze().astype('int')
718727

728+
719729
def _asymmetry_parameter(al, bl):
720730
'''
721731
Inputs: an, bn coefficient arrays from Mie solution
@@ -733,6 +743,7 @@ def _asymmetry_parameter(al, bl):
733743
np.real(al * np.conj(bl))).sum(axis=-1)
734744
return selfterm + crossterm
735745

746+
736747
def _cross_sections(al, bl):
737748
'''
738749
Calculates scattering and extinction cross sections
@@ -760,6 +771,7 @@ def _cross_sections(al, bl):
760771

761772
return cscat, cext, cback
762773

774+
763775
def _cross_sections_complex_medium_fu(al, bl, cl, dl, radius, n_particle,
764776
n_medium, x_scatterer, x_medium,
765777
wavelen):
@@ -797,7 +809,7 @@ def _cross_sections_complex_medium_fu(al, bl, cl, dl, radius, n_particle,
797809
prefactor1 = eta**2 * wavelen / (2*np.pi*radius**2*n_medium.real*
798810
(1+(eta-1)*np.exp(eta)))
799811

800-
lmax = np.atleast_1d(al).shape[-1]
812+
lmax = al.shape[-1]
801813
l = np.arange(lmax) + 1
802814
prefactor2 = (2. * l + 1.)[np.newaxis, ...]
803815

@@ -833,6 +845,7 @@ def _cross_sections_complex_medium_fu(al, bl, cl, dl, radius, n_particle,
833845

834846
return(cscat, cabs, cext)
835847

848+
836849
def _cross_sections_complex_medium_sudiarta(al, bl, x, radius):
837850
'''
838851
Calculates dimensional scattering, absorption, and extinction cross
@@ -852,13 +865,13 @@ def _cross_sections_complex_medium_sudiarta(al, bl, x, radius):
852865
# if multilayer, use outermost radius and size parameter corresponding to
853866
# outermost radius
854867
radius = np.array(radius.magnitude).max() * radius.units
855-
x = np.array(x).max(axis=-1)
868+
x = x.max(axis=-1)
856869
k = x/radius
857870

858871
# add newaxis corresponding to order (l)
859872
x = x[..., np.newaxis]
860873

861-
lmax = np.atleast_1d(al).shape[-1]
874+
lmax = al.shape[-1]
862875
l = np.arange(lmax) + 1
863876
prefactor = (2. * l + 1.)[np.newaxis, ...]
864877

@@ -975,7 +988,7 @@ def _scat_fields_complex_medium(m, x, thetas, kd, near_field=False):
975988
in an absorbing medium". Applied Optics, 40, 9 (2001).
976989
'''
977990
# calculate mie coefficients
978-
nstop = _nstop(np.array(x).max())
991+
nstop = _nstop(x.max())
979992
n = np.arange(nstop)+1.
980993

981994
an, bn = _scatcoeffs(m, x, nstop)
@@ -1058,6 +1071,7 @@ def _scat_fields_complex_medium(m, x, thetas, kd, near_field=False):
10581071

10591072
return Es_theta, Es_phi, Hs_theta, Hs_phi
10601073

1074+
10611075
def diff_scat_intensity_complex_medium(m, x, thetas, kd, phis=None,
10621076
cartesian=False, near_field=False,
10631077
incident_vector=None):
@@ -1229,6 +1243,7 @@ def diff_scat_intensity_complex_medium(m, x, thetas, kd, phis=None,
12291243
# functions)
12301244
return np.array([I_1.real, I_2.real])*np.abs(kd)**2
12311245

1246+
12321247
def integrate_intensity_complex_medium(dscat, thetas, kd,
12331248
phi_min=0.0,
12341249
phi_max=2*np.pi,
@@ -1369,6 +1384,7 @@ def integrate_intensity_complex_medium(dscat, thetas, kd,
13691384
(sigma_2*factor), (dsigma_1*factor.reshape(kd_shape)/2),
13701385
(dsigma_2*factor.reshape(kd_shape)/2))
13711386

1387+
13721388
def diff_abs_intensity_complex_medium(m, x, thetas, ktd):
13731389
'''
13741390
Calculates the differential absorbed intensity as a function of scattering
@@ -1408,7 +1424,7 @@ def diff_abs_intensity_complex_medium(m, x, thetas, ktd):
14081424
14091425
'''
14101426
# calculate mie coefficients
1411-
nstop = _nstop(np.array(x).max())
1427+
nstop = _nstop(x.max())
14121428
n = np.arange(nstop)+1.
14131429
cn, dn = _internal_coeffs(m, x, nstop)
14141430

@@ -1455,6 +1471,7 @@ def diff_abs_intensity_complex_medium(m, x, thetas, ktd):
14551471

14561472
return I_par.real, I_perp.real
14571473

1474+
14581475
def amplitude_scattering_matrix(m, x, thetas,
14591476
cartesian=False,
14601477
phis = None):
@@ -1523,7 +1540,7 @@ def amplitude_scattering_matrix(m, x, thetas,
15231540
and theta. Shapes of all arrays are (num_values, num_theta, [num_phi])
15241541
"""
15251542
# calculate n-array
1526-
nstop = _nstop(np.array(x).max())
1543+
nstop = _nstop(x.max())
15271544
n = np.arange(nstop)+1.
15281545
prefactor = (2*n+1)/(n*(n+1))
15291546

@@ -1696,7 +1713,7 @@ def _amplitude_scattering_matrix_RG(prefactor, x, thetas):
16961713
"""Amplitude scattering matrix from Rayleigh-Gans approximation
16971714
16981715
"""
1699-
if np.atleast_2d(x).shape[-1] > 1:
1716+
if x.shape[-1] > 1:
17001717
raise ValueError("Rayleigh-Gans approximation cannot be used for "
17011718
"layered spheres")
17021719

0 commit comments

Comments
 (0)