Skip to content

Commit f4d6868

Browse files
committed
simplified reshaping in amplitude_scattering_matrix()
We now assume thetas has shape (..., num_thetas) and that we broadcast over phi if specified. This change will affect downstream code that calculates the amplitude scattering matrix for all pairs (theta_ij, phi_ij) but is needed to allow theta and phi to be specified as a function of wavelength.
1 parent 8b0d2eb commit f4d6868

File tree

2 files changed

+30
-50
lines changed

2 files changed

+30
-50
lines changed

pymie/mie.py

Lines changed: 23 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,7 @@ def _scatcoeffs(m, x, nstop, eps1 = DEFAULT_EPS1, eps2 = DEFAULT_EPS2):
503503
an = ( (Dnmx/m + n/x)*psi - psishift ) / ( (Dnmx/m + n/x)*xi - xishift )
504504
bn = ( (Dnmx*m + n/x)*psi - psishift ) / ( (Dnmx*m + n/x)*xi - xishift )
505505

506-
# coefficient array has shape [2, num_values, nstop]
506+
# coefficient array has shape (2, ..., nstop)
507507
return np.array([an[..., 1:nstop+1], bn[..., 1:nstop+1]])
508508

509509

@@ -1441,8 +1441,7 @@ def diff_abs_intensity_complex_medium(m, x, thetas, ktd):
14411441
return I_par.real, I_perp.real
14421442

14431443

1444-
def amplitude_scattering_matrix(m, x, thetas,
1445-
phis = None):
1444+
def amplitude_scattering_matrix(m, x, thetas, phis=None):
14461445
"""
14471446
Calculates the amplitude scattering matrix for an n-dim array of thetas
14481447
(and phis if in cartesian coordinate system)
@@ -1506,32 +1505,27 @@ def amplitude_scattering_matrix(m, x, thetas,
15061505
# calculate mie coefficients
15071506
coeffs = _scatcoeffs(m, x, nstop)
15081507

1509-
# expand dims to allow broadcasting over phi
1510-
if phis is not None:
1511-
if np.ndim(phis) < 2:
1512-
thetas = thetas[..., np.newaxis]
1513-
phis = phis[..., np.newaxis, :]
1514-
1515-
# calculate amplitude scattering matrix in 'scattering plane' coordinate
1516-
# system
1517-
S2_sp, S1_sp = _amplitude_scattering_matrix(nstop, prefactor,
1518-
coeffs, thetas)
1519-
S3_sp = np.zeros_like(S1_sp)
1520-
S4_sp = np.zeros_like(S1_sp)
1508+
# calculate amplitude scattering matrix in scattering plane basis
1509+
S2, S1 = _amplitude_scattering_matrix(nstop, prefactor, coeffs, thetas)
15211510

15221511
if phis is not None:
1523-
# calculate sines and cosines
1524-
cosphi = np.cos(phis)
1525-
sinphi = np.sin(phis)
1512+
# expand dims to allow broadcasting over phi
1513+
S1 = S1[..., np.newaxis]
1514+
S2 = S2[..., np.newaxis]
1515+
phis = phis[..., np.newaxis, :]
15261516

15271517
# calculate elements of scattering matrix
1528-
S1_xy = S2_sp*(sinphi)**2 + S1_sp*(cosphi)**2
1529-
S2_xy = S2_sp*(cosphi)**2 + S1_sp*(sinphi)**2
1530-
S3_xy = S2_sp*sinphi*cosphi - S1_sp*sinphi*cosphi
1531-
S4_xy = S2_sp*cosphi*sinphi - S1_sp*cosphi*sinphi
1518+
cosphi = np.cos(phis)
1519+
sinphi = np.sin(phis)
1520+
S1_xy = S2*(sinphi)**2 + S1*(cosphi)**2
1521+
S2_xy = S2*(cosphi)**2 + S1*(sinphi)**2
1522+
S3_xy = S2*sinphi*cosphi - S1*sinphi*cosphi
1523+
S4_xy = S2*cosphi*sinphi - S1*cosphi*sinphi
15321524
return S1_xy, S2_xy, S3_xy, S4_xy
15331525
else:
1534-
return S1_sp, S2_sp, S3_sp, S4_sp
1526+
S3 = np.zeros_like(S1)
1527+
S4 = np.zeros_like(S1)
1528+
return S1, S2, S3, S4
15351529

15361530

15371531
def vector_scattering_amplitude(m, x, thetas,
@@ -1639,26 +1633,15 @@ def _amplitude_scattering_matrix(n_stop, prefactor, coeffs, thetas):
16391633
"""
16401634
pis, taus = _pis_and_taus(n_stop, thetas)
16411635

1642-
# to broadcast correctly over the dimensions of coeffs (which may be
1643-
# wavelength or other variable), we need to add leading dimensions to the
1644-
# pis and taus, which have shape [num_angles, order]. Result should have
1645-
# pis, taus shape: [1, ..., 1, num_angles, order]
1646-
# Similarly, we need to insert dimensions in coeffs corresponding to the
1647-
# angles in pis and taus. Result should have
1648-
# coeffs[0].shape: [num_values, ..., 1, order]
1649-
num_leading_dims = len(coeffs[0].shape[:-1])
1650-
num_insert_dims = len(pis.shape[:-1])
1651-
new_coeffs_shape = (coeffs.shape[:-1] + num_insert_dims*(1,)
1652-
+ (coeffs.shape[-1],))
1653-
pis = pis.reshape(num_leading_dims*(1,) + pis.shape)
1654-
taus = taus.reshape(num_leading_dims*(1,) + taus.shape)
1655-
coeffs = coeffs.reshape(new_coeffs_shape)
1656-
1657-
# result should have shape [num_values, ..., num_angles]
1636+
# For broadcasting over theta, set shape to (..., 1, order)
1637+
coeffs = coeffs[..., np.newaxis, :]
1638+
1639+
# result should have shape (..., num_thetas)
16581640
S1 = np.sum(prefactor*(coeffs[0]*pis + coeffs[1]*taus), axis=-1)
16591641
S2 = np.sum(prefactor*(coeffs[0]*taus + coeffs[1]*pis), axis=-1)
16601642
return S2, S1
16611643

1644+
16621645
def _amplitude_scattering_matrix_RG(prefactor, x, thetas):
16631646
"""Amplitude scattering matrix from Rayleigh-Gans approximation
16641647
@@ -1674,7 +1657,7 @@ def _amplitude_scattering_matrix_RG(prefactor, x, thetas):
16741657
p = np.divide(np.sin(u) - u*np.cos(u), u**3, out=np.ones_like(u),
16751658
where=u!=0)
16761659

1677-
# result should have shape [num_values, ..., num_angles]
1660+
# result should have shape (..., num_angles)
16781661
S1 = prefactor * 3 * p
16791662
S2 = S1 * np.cos(thetas)
16801663
return S2, S1

pymie/tests/test_mie.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -703,12 +703,11 @@ def test_vector_scattering_amplitude_2d_theta_cartesian():
703703
as_vec_x0, as_vec_y0 = mie.vector_scattering_amplitude(m, x, thetas,
704704
phis=phis)
705705

706-
# calculate the amplitude scattering matrix in par/perp basis. Need to
707-
# setup for broadcasting first:
708-
thetas = thetas[:, np.newaxis]
709-
phis = phis[np.newaxis, :]
706+
# calculate the amplitude scattering matrix in par/perp basis.
710707
S1_sp, S2_sp, _, _ = mie.amplitude_scattering_matrix(m, x, thetas)
711708

709+
S1_sp = S1_sp[..., np.newaxis]
710+
S2_sp = S2_sp[..., np.newaxis]
712711
cosphi = np.cos(phis)
713712
sinphi = np.sin(phis)
714713
as_vec_x = S2_sp * cosphi**2 + S1_sp * sinphi**2
@@ -735,19 +734,17 @@ def test_diff_scat_intensity_complex_medium_cartesian():
735734
thetas = np.linspace(np.pi/2, np.pi, 4)
736735
phis = np.linspace(0, 2*np.pi, 3)
737736

738-
# allow broadcasting over phi
739-
thetas_2d = np.repeat(thetas[:, np.newaxis], phis.shape, axis=1)
740-
741737
# parameters for calculating scattering
742738
m = index_ratio(n_particle, n_matrix)
743739
x = size_parameter(wavelen, n_matrix, radius)
744740
kd = (2*np.pi*n_matrix/wavelen*Quantity(10000.0, "nm")).to("").magnitude
745741

746742
# calculate differential scattered intensity in par/perp basis
747-
# use of theta_2d here (instead of theta) broadcasts over the phi
748-
# dimension, allowing us to compare to cartesian calculation
749-
I_parperp = mie.diff_scat_intensity_complex_medium(m, x, thetas_2d, kd,
743+
I_parperp = mie.diff_scat_intensity_complex_medium(m, x, thetas, kd,
750744
near_field=False)
745+
# broadcast over the phi dimension, allowing us to compare to cartesian
746+
# calculation
747+
I_parperp = np.repeat(I_parperp[..., np.newaxis], phis.shape, axis=3)
751748

752749
# calculate differential scattered intensity in xy basis
753750
# if incident vector is unpolarized (1,1), then the resulting differential

0 commit comments

Comments
 (0)