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