@@ -180,10 +180,8 @@ def calc_ang_scat(m, x, thetas, kd=None, phis=None, incident_vector=None,
180180 & Huffman ch. 3 for details.
181181 """
182182 if (kd is not None ) or (phis is not None ):
183- return diff_scat_intensity_complex_medium (m , x , thetas , kd = kd ,
184- phis = phis ,
185- incident_vector =
186- incident_vector )
183+ return diff_scat_intensity (m , x , thetas , kd = kd , phis = phis ,
184+ incident_vector = incident_vector )
187185
188186 # Mie scattering preliminaries
189187 nstop = _nstop (x .max ())
@@ -458,8 +456,8 @@ def calc_dwell_time(radius, n_medium, n_particle, wavelen,
458456 angles = np .linspace (min_angle , np .pi , num_angles )
459457 distance = radius .max ()
460458 kd = (k * distance ).to ("" ).magnitude
461- diff_cscat = diff_scat_intensity_complex_medium (m , x , angles , kd )
462- cscat = integrate_intensity_complex_medium (diff_cscat , angles , kd )[0 ]
459+ diff_cscat = diff_scat_intensity (m , x , angles , kd )
460+ cscat = integrate_intensity (diff_cscat , angles , kd )[0 ]
463461 else :
464462 cscat = calc_cross_sections (m , x , eps1 = eps1 , eps2 = eps2 )[0 ]
465463 cscat = cscat * 1 / k ** 2
@@ -492,10 +490,8 @@ def calc_reflectance(radius, n_medium, n_particle, wavelen,
492490 distance = rmax
493491 k = np .atleast_1d (2 * np .pi / wavelen_media )
494492 kd = (k * distance ).to ("" ).magnitude
495- diff_cscat = diff_scat_intensity_complex_medium (m , x , thetas ,
496- kd )
497- refl_cscat = integrate_intensity_complex_medium (diff_cscat , thetas ,
498- kd )[0 ]
493+ diff_cscat = diff_scat_intensity (m , x , thetas , kd )
494+ refl_cscat = integrate_intensity (diff_cscat , thetas , kd )[0 ]
499495 refl_cscat = refl_cscat / k ** 2
500496 else :
501497 refl_cscat = calc_integrated_cross_section (m , x , thetas )
@@ -1172,20 +1168,21 @@ def _scat_fields_complex_medium(m, x, thetas, kd, near_field=False):
11721168 # note that these solutions are not currently used anywhere in mie.py.
11731169 # When the fields are multiplied to calculate the intensity, the
11741170 # exponential terms reduce down to a term that depends on kd (see
1175- # diff_scat_intensity_complex_medium (). So these equations lead to
1171+ # diff_scat_intensity (). So these equations lead to
11761172 # intensities that are the same as those calculated with the scattering
1177- # matrix in diff_scat_intensity_complex_medium ().
1173+ # matrix in diff_scat_intensity ().
11781174 # We leave the expressions here in case users ever have a need to know
11791175 # the actual fields, rather than the intensities.
11801176
11811177 return Es_theta , Es_phi , Hs_theta , Hs_phi
11821178
11831179
1184- def diff_scat_intensity_complex_medium (m , x , thetas , kd , phis = None ,
1185- near_field = False ,
1186- incident_vector = None ):
1180+ def diff_scat_intensity (m , x , thetas , kd = None , phis = None ,
1181+ near_field = False ,
1182+ incident_vector = None ):
11871183 """
1188- Calculates the differential scattered intensity in an absorbing medium.
1184+ Calculates the differential scattered intensity for both absorbing and
1185+ non-absorbing medium.
11891186 User can choose whether to include near fields.
11901187
11911188 When phis is None:
@@ -1223,11 +1220,11 @@ def diff_scat_intensity_complex_medium(m, x, thetas, kd, phis=None,
12231220 size parameter, x = ka = 2*pi*n_med/lambda * a (sphere radius a)
12241221 thetas : array-like
12251222 Scattering angles. Must be in radians.
1226- kd : float
1223+ kd : float (default None)
12271224 k * distance, where k = 2*np.pi*n_matrix/wavelen, and distance is the
12281225 distance away from the center of the particle. The standard far-field
12291226 solutions are obtained when distance >> radius in a non-absorbing
1230- medium.
1227+ medium. If kd is None, far-field solutions are used.
12311228 phis : None or ndarray
12321229 Azimuthal angles for which to calculate the diff scat intensity. If not
12331230 provided, scattering calculations will be carried out in the
@@ -1295,25 +1292,29 @@ def diff_scat_intensity_complex_medium(m, x, thetas, kd, phis=None,
12951292 in an absorbing medium". Applied Optics, 40, 9 (2001).
12961293
12971294 """
1298- # ensure that broadcasting will work correctly by adding an axis
1299- # corresponding to theta
1300- kd = np .atleast_1d (kd )[..., np .newaxis ]
1301- if phis is not None :
1302- kd = kd [..., np .newaxis ]
1295+ if kd is not None :
1296+ # ensure that broadcasting will work correctly by adding an axis
1297+ # corresponding to theta
1298+ kd = np .atleast_1d (kd )[..., np .newaxis ]
1299+ if phis is not None :
1300+ kd = kd [..., np .newaxis ]
13031301
13041302 if near_field :
1303+ if kd is None :
1304+ raise ValueError ("Near field calculation requires nondimensional "
1305+ "distance kd to be specified" )
13051306 if phis is None :
13061307 # calculate scattered fields in scattering plane coordinate system
13071308 Es_theta , Es_phi , Hs_theta , Hs_phi = _scat_fields_complex_medium (m ,
13081309 x ,thetas , kd , near_field = near_field )
1309- I_1 = Es_theta * np .conj (Hs_phi ) # I_par
1310- I_2 = - Es_phi * np .conj (Hs_theta ) # I_perp
1310+ # calculate intensity and multiply by |kd|^2 so that resulting
1311+ # values can be dimensionalized by multiplying by 1/|k|^2
1312+ I_1 = Es_theta * np .conj (Hs_phi ) * np .abs (kd )** 2 # I_par
1313+ I_2 = - Es_phi * np .conj (Hs_theta ) * np .abs (kd )** 2 # I_perp
13111314 else :
13121315 raise ValueError ("Near fields have not been implemented for the "
13131316 "Cartesian coordinate system. Set near_field "
13141317 "to False to calculate scattered intensity" )
1315-
1316-
13171318 else :
13181319 # calculate vector scattering amplitude
13191320 vec_scat_amp_1 , vec_scat_amp_2 = vector_scattering_amplitude (m , x ,
@@ -1331,36 +1332,38 @@ def diff_scat_intensity_complex_medium(m, x, thetas, kd, phis=None,
13311332 # down to 1/(kd)^2 when k is real, which is the factor usually used to
13321333 # get the final intensity in a non-absorbing medium (p. 113 of Bohren
13331334 # and Huffman).
1334- factor = np .exp (- 2 * kd .imag ) / ((kd .real )** 2 + (kd .imag )** 2 )
1335+ if kd is not None :
1336+ # The factor is normally e^(-2k_I)/|k|^2 but we simplify to
1337+ # e^(2k_I) so that the resulting non-dimensional cross-sections can
1338+ # be dimensionalized by multiplying by 1/|k|^2 (just as with
1339+ # cross-sections returned by other functions). For far-field, the
1340+ # simplified factor is unity
1341+ factor = np .exp (- 2 * kd .imag )
1342+ else :
1343+ factor = 1
1344+
13351345 I_1 = (np .abs (vec_scat_amp_1 )** 2 )* factor # par or x
13361346 I_2 = (np .abs (vec_scat_amp_2 )** 2 )* factor # perp or y
13371347
1338- # the intensities should be real. We multiply by |kd|^2 so that the
1339- # resulting nondimensional cross-sections can be dimensionalized by
1340- # multiplying by 1/|k|^2 (just as with cross-sections returned by other
1341- # functions)
1342- result = np .stack ([I_1 .real , I_2 .real ], axis = - 1 )
1343- return result * np .abs (kd )[..., np .newaxis ]** 2
1348+ # the intensities should be real
1349+ return np .stack ([I_1 .real , I_2 .real ], axis = - 1 )
13441350
13451351
1346- def integrate_intensity_complex_medium (dscat , thetas , kd ,
1347- phi_min = 0.0 ,
1348- phi_max = 2 * np .pi ,
1349- phis = None ):
1352+ def integrate_intensity (dscat , thetas , kd = None ,
1353+ phi_min = 0.0 , phi_max = 2 * np .pi , phis = None ):
13501354 """
13511355 Calculates the scattering cross section by integrating the differential
1352- scattered intensity at a distance of our choice in an absorbing medium .
1356+ scattered intensity. If the medium is absorbing, kd should be specified .
13531357 Choosing the right distance is essential in an absorbing medium because the
13541358 differential scattering intensities decrease with increasing distance.
13551359 The integration is done over scattering angles theta and azimuthal angles
13561360 phi using the trapezoid rule.
13571361
13581362 Parameters
13591363 ----------
1360- dscat : array-like with shape (2, ..., num_thetas, [num_phis])
1364+ dscat : array-like with shape (..., num_thetas, [num_phis], 2 )
13611365 differential scattered intensities for both polarizations. Can be
1362- functions of theta or of theta and phi. If a function of theta and phi,
1363- the theta dimension MUST come first
1366+ functions of theta or of theta and phi.
13641367 thetas : array-like, shape ([angle_leading_dims], num_thetas)
13651368 scattering angles
13661369 kd : array-like, shape (...)
@@ -1392,7 +1395,6 @@ def integrate_intensity_complex_medium(dscat, thetas, kd,
13921395 """
13931396 # add axis for polarization dimension
13941397 thetas = thetas [..., np .newaxis ]
1395- kd = np .atleast_1d (kd )[..., np .newaxis ]
13961398
13971399 # do phi integral first because it's the next-to-last dimension in dscat
13981400 # when specified (thus phis will broadcast with dscat)
@@ -1407,9 +1409,9 @@ def integrate_intensity_complex_medium(dscat, thetas, kd,
14071409 # This factor is needed to account for polarization, which introduces
14081410 # factors of cos(phi) and sin(phi) for the electric fields.
14091411 factor = np .array ([(phi_max / 2 + np .sin (2 * phi_max )/ 4
1410- - phi_min / 2 - np .sin (2 * phi_min )/ 4 ),
1412+ - phi_min / 2 - np .sin (2 * phi_min )/ 4 ),
14111413 (phi_max / 2 - np .sin (2 * phi_max )/ 4
1412- - phi_min / 2 + np .sin (2 * phi_min )/ 4 )])
1414+ - phi_min / 2 + np .sin (2 * phi_min )/ 4 )])
14131415 integrand = integrand * factor
14141416
14151417 # integrate diff. cross-sections over theta using Jacobian
@@ -1421,20 +1423,27 @@ def integrate_intensity_complex_medium(dscat, thetas, kd,
14211423 # (see Sudiarta and Chylek (2001), eq 10).
14221424 # if the imaginary part of k is close to 0 (because the medium index is
14231425 # close to 0), then use the limit value of factor for the calculations
1424- exponent = np .exp (2 * kd .imag )
1425- factor_limit = 2
1426- # ignore division by zero in case k.imag=0; we'll replace the nans with the
1427- # limit value of factor anyway
1428- with np .errstate (divide = 'ignore' , invalid = 'ignore' ):
1429- factor = np .where (kd .imag <= 1e-6 , factor_limit ,
1430- 1 / (exponent / (2 * kd .imag )
1431- + (1 - exponent ) / (2 * kd .imag )** 2 ))
1426+ if kd is not None :
1427+ # add axis corresponding to polarization
1428+ kd = np .atleast_1d (kd )[..., np .newaxis ]
1429+
1430+ exponent = np .exp (2 * kd .imag )
1431+ factor_limit = 2
1432+ # ignore division by zero in case k.imag=0; we'll replace the nans
1433+ # with the limit value of factor anyway
1434+ with np .errstate (divide = 'ignore' , invalid = 'ignore' ):
1435+ factor = np .where (kd .imag <= 1e-6 , factor_limit ,
1436+ 1 / (exponent / (2 * kd .imag )
1437+ + (1 - exponent ) / (2 * kd .imag )** 2 ))
1438+ else :
1439+ # far field result
1440+ factor = 2
14321441
14331442 # calculate the averaged sigma (polarization axis is last)
14341443 sigma = sigma * factor
14351444 sigma_avg = sigma .sum (axis = - 1 )/ 2
14361445
1437- return (sigma_avg , sigma [..., 0 ], sigma [..., 1 ])
1446+ return (sigma_avg , sigma [..., 0 ], sigma [..., 1 ])
14381447
14391448
14401449def diff_abs_intensity_complex_medium (m , x , thetas , ktd ):
0 commit comments