Source code for miepython.monte_carlo
"""
Monte Carlo and Mie scattering.
"""
import numpy as np
import miepython as mie
__all__ = (
"cdf",
"mu_with_uniform_cdf",
"generate_mie_costheta",
)
[docs]
def cdf(m, x, num):
"""
Create a CDF for unpolarized scattering uniformly spaced in cos(theta).
The CDF covers scattered (exit) angles ranging from 180 to 0 degrees.
(The cosines are uniformly distributed over -1 to 1.) Because the angles
are uniformly distributed in cos(theta), the scattering function is not
sampled uniformly and therefore huge array sizes are needed to adequately
sample highly anisotropic phase functions.
Since this is a cumulative distribution function, the maximum value
should be 1.
Args:
m: the complex index of refraction of the sphere
x: the size parameter of the sphere
num: length of desired CDF array
Returns:
mu: array of cosines of angles
cdf: array of cumulative distribution function values
"""
mu = np.linspace(-1, 1, num)
intensity_per_mu = mie.i_unpolarized(m, x, mu, norm="4pi") / num
cdf_values = np.cumsum(intensity_per_mu)
return mu, cdf_values
[docs]
def mu_with_uniform_cdf(m, x, num):
"""
Create a CDF for unpolarized scattering for uniform CDF.
The CDF covers scattered (exit) angles ranging from 180 to 0 degrees.
(The cosines are uniformly distributed over -1 to 1.) These angles mu
correspond to uniform spacing of the cumulative distribution function
for unpolarized Mie scattering where cdf[i] = i/(num-1).
This is a brute force implementation that solves the problem by
calculating the CDF at many points and then scanning to find the
specific angles that correspond to uniform interval of the CDF.
Since this is a cumulative distribution function, the maximum value
should be 1.
Args:
m: the complex index of refraction of the sphere
x: the size parameter of the sphere
num: length of desired CDF array
Returns:
mu: array of cosines of angles (irregularly spaced)
cdf: array of cumulative distribution function values
"""
big_num = 2000 # large to work with x up to 10
big_mu, big_cdf = cdf(m, x, big_num)
mu = np.empty(num)
cdf_values = np.empty(num)
mu[0] = -1 # cos[180 degrees] is -1
cdf_values[0] = 0 # initial cdf is zero
big_k = 0 # index into big_cdf
for k in range(1, num - 1):
target = k / (num - 1)
while big_cdf[big_k] < target:
big_k += 1
delta = big_cdf[big_k] - target
delta_cdf = big_cdf[big_k] - big_cdf[big_k - 1]
delta_mu = big_mu[big_k] - big_mu[big_k - 1]
mu[k] = big_mu[big_k] - delta / delta_cdf * delta_mu # interpolate
cdf_values[k] = target
# print(' mu[',k,']=% .5f'%mu[k],' cdf[',k,']=% .5f'%cdf[k],
# 'cdf=',big_cdf[big_k], fraction)
mu[num - 1] = 1 # cos[0 degrees] is 1
cdf_values[num - 1] = 1 # last cdf is one
return [mu, cdf_values]
[docs]
def generate_mie_costheta(mu_cdf):
"""
Generate a new scattering angle using a cdf.
A uniformly spaced cumulative distribution function (CDF) is needed.
New random angles are generated by selecting a random interval
mu[i] to mu[i+1] and choosing an angle uniformly distributed over
the interval.
Args:
mu_cdf: a cumulative distribution function
Returns:
The cosine of the scattering angle
"""
# the following should be equivalent to these four lines
# index = np.random.randint(0, high=len(mu_cdf))
num = len(mu_cdf) - 1
index = int(np.random.random() * num)
if index >= num:
index = num - 1
x = mu_cdf[index]
x += (mu_cdf[index + 1] - mu_cdf[index]) * np.random.random()
return x