Skip to content

Commit e8b588b

Browse files
committed
changed _scatcoeffs() and all related funcs to not squeeze wavelen axis
1 parent abc83f7 commit e8b588b

File tree

4 files changed

+65
-70
lines changed

4 files changed

+65
-70
lines changed

pymie/mie.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,8 @@ def _scatcoeffs(m, x, nstop, eps1 = DEFAULT_EPS1, eps2 = DEFAULT_EPS2):
437437
# index ratio should be specified as a 2D array with shape
438438
# [num_values, num_layers] to calculate over a set of different values,
439439
# such as wavelengths. If specified as a 1D array, shape is [num_layers].
440-
if np.atleast_2d(m).shape[-1] > 1:
440+
num_layers = np.atleast_2d(m).shape[-1]
441+
if num_layers > 1:
441442
return _scatcoeffs_multi(m, x)
442443

443444
# Scattering coefficients for single-layer particles.
@@ -446,12 +447,17 @@ def _scatcoeffs(m, x, nstop, eps1 = DEFAULT_EPS1, eps2 = DEFAULT_EPS2):
446447
# nmx = np.array([nstop, np.round(np.absolute(m*x))]).max() + 20
447448
# Dnmx = mie_specfuncs.log_der_1(m*x, nmx, nstop)
448449
# above replaced with Lentz algorithm
449-
z = np.atleast_1d(m * x).squeeze()
450+
z = np.atleast_1d(m * x)
451+
# remove unneded layer axis
452+
z = z[..., 0]
450453
Dnmx = mie_specfuncs.dn_1_down(z, nstop + 1, nstop,
451454
mie_specfuncs.lentz_dn1(z, nstop + 1,
452455
eps1, eps2))
456+
453457
n = np.arange(nstop+1)
454-
x = np.atleast_2d(x)
458+
if np.ndim(x) <= 1:
459+
x = np.atleast_1d(x)[..., np.newaxis]
460+
455461
psi, xi = mie_specfuncs.riccati_psi_xi(x, nstop)
456462

457463
# insert zeroes at the beginning of second axis (order axis)
@@ -460,9 +466,8 @@ def _scatcoeffs(m, x, nstop, eps1 = DEFAULT_EPS1, eps2 = DEFAULT_EPS2):
460466
an = ( (Dnmx/m + n/x)*psi - psishift ) / ( (Dnmx/m + n/x)*xi - xishift )
461467
bn = ( (Dnmx*m + n/x)*psi - psishift ) / ( (Dnmx*m + n/x)*xi - xishift )
462468

463-
# coefficient array has shape [2, num_values, nstop] or [2, nstop] if
464-
# only one value (only one wavelength, for example)
465-
return np.array([an[..., 1:nstop+1], bn[..., 1:nstop+1]]).squeeze()
469+
# coefficient array has shape [2, num_values, nstop]
470+
return np.array([an[..., 1:nstop+1], bn[..., 1:nstop+1]])
466471

467472
def _scatcoeffs_multi(marray, xarray, nstop=None, eps1 = 1e-3, eps2 = 1e-16):
468473
'''Calculate scattered field expansion coefficients (in the Mie formalism)
@@ -578,7 +583,7 @@ def _scatcoeffs_multi(marray, xarray, nstop=None, eps1 = 1e-3, eps2 = 1e-16):
578583
/ ((hbns*mlast + n/xlast)*xi - xishift))
579584

580585
# output begins at n=1
581-
return np.array([an[..., 1:nstop+1], bn[..., 1:nstop+1]]).squeeze()
586+
return np.array([an[..., 1:nstop+1], bn[..., 1:nstop+1]])
582587

583588
def _internal_coeffs(m, x, n_max, eps1 = DEFAULT_EPS1, eps2 = DEFAULT_EPS2):
584589
'''

pymie/tests/test_mie.py

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,9 @@ def test_form_factor():
8888
93.5508557840006])
8989

9090
iparperp = mie.calc_ang_scat(m, x, angles)
91-
assert_array_almost_equal(iparperp[0], ipar_bhmie)
92-
assert_array_almost_equal(iparperp[1], iperp_bhmie)
91+
# squeeze to remove singlet wavelength dimension
92+
assert_array_almost_equal(iparperp[0].squeeze(), ipar_bhmie)
93+
assert_array_almost_equal(iparperp[1].squeeze(), iperp_bhmie)
9394

9495
def test_efficiencies():
9596
x = np.array([0.01, 0.01778279, 0.03162278, 0.05623413, 0.1, 0.17782794,
@@ -170,8 +171,9 @@ def test_absorbing_materials():
170171
8.26505988320951, 47.4736966179677])
171172

172173
iparperp = mie.calc_ang_scat(m, x, angles)
173-
assert_array_almost_equal(iparperp[0], ipar_bhmie)
174-
assert_array_almost_equal(iparperp[1], iperp_bhmie)
174+
# squeeze to remove singlet wavelen axis before comparison
175+
assert_array_almost_equal(iparperp[0].squeeze(), ipar_bhmie)
176+
assert_array_almost_equal(iparperp[1].squeeze(), iperp_bhmie)
175177

176178
def test_multilayer_spheres():
177179
# test that form factors and cross sections are the same for a
@@ -485,8 +487,6 @@ def test_differential_cross_section():
485487

486488
# With far-field Mie solutions
487489
I_parperp_cad = mie.calc_ang_scat(m, x, theta)
488-
# temp fix to re-insert wavelength dimension (which gets squeezed out)
489-
I_parperp_cad = I_parperp_cad[:, np.newaxis, :]
490490

491491
# With Mie solutions at surface of particle (but neglecting near-fields)
492492
kd = (k*distance).to("").magnitude
@@ -748,11 +748,7 @@ def test_diff_scat_intensity_complex_medium_cartesian():
748748
I_par_perp_mag = np.sqrt((I_parperp**2).sum(axis=0))
749749

750750
# check that the magnitudes are equal
751-
#
752-
# Because of the way this test is done (using a 2D array of angles
753-
# for theta), we don't end up with a wavelength axis for I_parperp. Have
754-
# to squeeze to compare.
755-
assert_allclose(I_xy_mag.squeeze(), I_par_perp_mag, rtol=1e-15)
751+
assert_allclose(I_xy_mag, I_par_perp_mag, rtol=1e-15)
756752

757753
def test_integrate_intensity_complex_medium_cartesian():
758754
'''
@@ -901,7 +897,12 @@ def test_dwell_time_and_energy():
901897
distance_calc = dwell_time*c
902898
distance_calc = distance_calc.to('um')
903899

904-
assert_approx_equal(cscat_reported.magnitude, cscat_calc.magnitude, significant=2)
905-
assert_approx_equal(W_star_reported, np.real(W_star_calc), significant=2)
906-
assert_approx_equal(W_reported.magnitude, np.real(W_calc.magnitude), significant=2)
907-
assert_approx_equal(distance_reported.magnitude, np.real(distance_calc.magnitude), significant=2)
900+
# squeeze to remove singlet wavelength dimension on calculated values
901+
assert_allclose(cscat_calc.squeeze().magnitude,
902+
cscat_reported.magnitude, rtol=1e-2)
903+
assert_allclose(np.real(W_star_calc).squeeze(), W_star_reported, rtol=1e-1)
904+
assert_allclose(np.real(W_calc.magnitude).squeeze(),
905+
W_reported.magnitude, rtol=1e-1)
906+
assert_allclose(np.real(distance_calc.magnitude).squeeze(),
907+
distance_reported.magnitude, rtol=1e-1)
908+

pymie/tests/test_mie_vectorized.py

Lines changed: 35 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -430,27 +430,23 @@ def test_vectorized_scatcoeffs(self, num_wavelen, num_layer):
430430
assert_equal(coeffs, coeffs_direct)
431431

432432
# make sure shape is correct
433-
if num_wavelen == 1:
434-
expected_shape = (2, nstop)
435-
assert coeffs.shape == expected_shape
436-
# no further test since no loop required in this case
437-
else:
438-
expected_shape = (2, num_wavelen, nstop)
439-
assert coeffs.shape == expected_shape
440-
441-
# we should get same value from loop
442-
coeffs_loop = np.zeros(expected_shape, dtype=complex)
443-
for i in range(m.shape[0]):
444-
if num_layer == 1:
445-
c = mie._scatcoeffs(m[i], x[i], nstop)
446-
else:
447-
# need to specify nstop here; otherwise we will get a
448-
# different number of scattering coefficients for each
449-
# wavelength, since _scatcoeffs_multi() picks the largest x
450-
# for each wavelength.
451-
c = mie._scatcoeffs_multi(m[i], x[i], nstop)
452-
coeffs_loop[:, i] = c
453-
assert_equal(coeffs, coeffs_loop)
433+
expected_shape = (2, num_wavelen, nstop)
434+
assert coeffs.shape == expected_shape
435+
436+
# we should get same value from loop
437+
coeffs_loop = []
438+
for i in range(m.shape[0]):
439+
if num_layer == 1:
440+
coeffs_loop.append(mie._scatcoeffs(m[i], x[i], nstop))
441+
else:
442+
# need to specify nstop here; otherwise we will get a
443+
# different number of scattering coefficients for each
444+
# wavelength, since _scatcoeffs_multi() picks the largest x
445+
# for each wavelength.
446+
coeffs_loop.append(mie._scatcoeffs_multi(m[i], x[i], nstop))
447+
# concatenate along wavelength axis
448+
coeffs_loop = np.concatenate(coeffs_loop, axis=1)
449+
assert_equal(coeffs, coeffs_loop)
454450

455451
@pytest.mark.parametrize("num_wavelen,num_layer",
456452
[(1, 1), (10, 1), (1, 5), (10, 5)])
@@ -631,10 +627,7 @@ def test_vectorized_angular_functions(self, num_wavelen,
631627

632628
# check that shapes of all the computed quantities are correct
633629
for element in vsa + mat:
634-
if num_wavelen > 1:
635-
assert element.shape == (num_wavelen, ) + thetas.shape
636-
else:
637-
assert element.shape == thetas.shape
630+
assert element.shape == (num_wavelen, ) + thetas.shape
638631
assert i12.shape == (2, num_wavelen) + thetas.shape
639632

640633
# check that vectorized calculations match looped calculations over
@@ -691,13 +684,13 @@ def test_vectorized_angular_functions(self, num_wavelen,
691684
dsigma_1[i] = integral_loop[3].squeeze()
692685
dsigma_2[i] = integral_loop[4].squeeze()
693686

694-
assert_equal(mat[0], S1.squeeze())
695-
assert_equal(mat[1], S2.squeeze())
696-
assert_equal(mat[2], S3.squeeze())
697-
assert_equal(mat[3], S4.squeeze())
687+
assert_equal(mat[0], S1)
688+
assert_equal(mat[1], S2)
689+
assert_equal(mat[2], S3)
690+
assert_equal(mat[3], S4)
698691

699-
assert_equal(vsa[0], amp0.squeeze())
700-
assert_equal(vsa[1], amp1.squeeze())
692+
assert_equal(vsa[0], amp0)
693+
assert_equal(vsa[1], amp1)
701694

702695
assert_equal(i12[0], i1)
703696
assert_equal(i12[1], i2)
@@ -763,10 +756,11 @@ def test_vectorized_asymmetry_parameter(self, num_wavelen, num_layer):
763756

764757
# we should get same values from loop. Need to set nstop to the
765758
# same value as used in the vectorized calculation.
766-
g_loop = np.zeros(expected_shape, dtype=float)
759+
g_loop = []
767760
nstop = mie._nstop(x.max())
768761
for i in range(num_wavelen):
769-
g_loop[i] = mie.calc_g(m[i], x[i], nstop=nstop)
762+
g_loop.append(mie.calc_g(m[i], x[i], nstop=nstop))
763+
g_loop = np.concatenate(g_loop, axis=0)
770764
assert_equal(g, g_loop)
771765

772766
@pytest.mark.parametrize("num_wavelen, num_layer",
@@ -782,10 +776,7 @@ def test_vectorized_cross_sections(self, num_wavelen, num_layer):
782776
cscat, cext, cback, cabs, asym = mie.calc_cross_sections(m, x)
783777

784778
# test shape
785-
if num_wavelen > 1:
786-
expected_shape = (num_wavelen,)
787-
else:
788-
expected_shape = ()
779+
expected_shape = (num_wavelen,)
789780
for cs in [cscat, cext, cback, cabs, asym]:
790781
assert cs.shape == expected_shape
791782

@@ -798,7 +789,7 @@ def test_vectorized_cross_sections(self, num_wavelen, num_layer):
798789
for i in range(num_wavelen):
799790
cs = mie.calc_cross_sections(m[i], x[i])
800791
cscat_loop[i], cext_loop[i], cback_loop[i], \
801-
cabs_loop[i], asym_loop[i] = (c for c in cs)
792+
cabs_loop[i], asym_loop[i] = (c.squeeze() for c in cs)
802793
assert_equal(cscat, cscat_loop)
803794
assert_equal(cext, cext_loop)
804795
assert_equal(cback, cback_loop)
@@ -855,20 +846,16 @@ def test_vectorized_calc_ang_scat(self, num_wavelen, num_layer):
855846
"""
856847
m, x = mx(num_wavelen, num_layer, **self.mxargs)
857848
form_factor = mie.calc_ang_scat(m, x, self.angles)
858-
if num_wavelen == 1:
859-
expected_shape = (2, self.num_angle,)
860-
assert form_factor.shape == expected_shape
861-
# no further test required since there is only one wavelength
862-
return
863-
else:
864-
expected_shape = (2, num_wavelen, self.num_angle)
865-
assert form_factor.shape == expected_shape
849+
expected_shape = (2, num_wavelen, self.num_angle)
850+
assert form_factor.shape == expected_shape
866851

867852
# we should get same values from loop
868-
iparperp_loop = np.zeros(expected_shape, dtype=float)
853+
iparperp_loop = []
869854
for i in range(num_wavelen):
870855
iparperp = mie.calc_ang_scat(m[i], x[i], self.angles)
871-
iparperp_loop[:, i] = iparperp
856+
iparperp_loop.append(iparperp)
857+
# concatenate along wavelength axis
858+
iparperp_loop = np.concatenate(iparperp_loop, axis=1)
872859
assert_equal(form_factor, iparperp_loop)
873860

874861
# check vectorization for Rayleigh-Gans approximation

pymie/tests/test_multilayer.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ def test_sooty_particles():
9292
def efficiencies_from_scat_units(m, x):
9393
asbs = mie._scatcoeffs_multi(m, x)
9494
qs = np.array(mie._cross_sections(*asbs)) * 2 / x_L**2
95+
# squeeze out singleton wavelen dimension
96+
qs = qs.squeeze()
9597
# there is a factor of 2 conventional difference between
9698
# "backscattering" and "radar backscattering" efficiencies.
9799
return np.array([qs[1], qs[0], qs[2]*4*np.pi/2.])

0 commit comments

Comments
 (0)