# Source code for irfpy.util.isotropy

""" Functions for isotropic distribution

.. autosummary::

getRandom
getRandom_tp
"""
import numpy as np

[docs]def getRandom_tp(size=None):
r""" Return the randomly generated distribution in |theta| and |phi|

:param size: The size of the array.
:return: Randomly generated isotropic distribution.
(theta, phi) = retval in radians
:rtype: (2, size[0], size[1], ...)-shaped numpy array.

>>> rnd = getRandom_tp()
>>> rnd.shape
(2,)

>>> rnd = getRandom_tp(size=(2, 4))   # (3, 2, 4) array will be returned.
>>> rnd.shape
(2, 2, 4)

*Way of generation*

The polar angle, |theta|, should distribute considering the area at spherical surface,
namely depending on :math:\sin\theta.
To realize this, one can start from uniform random, convert it according to

.. math::

\theta = \cos^{-1} (1-2P)

where P is the (array of) value generated from random distribution.

The azimuth angle, |phi|, is random in the range of (0, 2 |pi|).

"""

if size is None:
size = 1

theta_random = np.random.uniform(low=0, high=1, size=size)
theta_random = 1 - 2 * theta_random
theta_random = np.clip(theta_random, -1, 1)   # To confirm the range.
theta_random = np.arccos(theta_random)

phi_random = np.random.uniform(low=0, high=np.pi * 2, size=size)

values = np.array([theta_random, phi_random])
if size == 1:
values = np.squeeze(values)

return values

[docs]def getRandom(size=None):
r""" Return the randomly generated distribution

:param size: The size of the array.
:return: Randomly generated isotropic distribution.
Unit vector.
:rtype: (3, size[0], size[1], ...)-shaped numpy array.

>>> np.random.seed(0)   # Set the random seed.
>>> rnd = getRandom()
>>> rnd.shape
(3,)

>>> print('{:.3f}'.format((rnd ** 2).sum()))
1.000

>>> rnd = getRandom(size=(2, 4))   # (3, 2, 4) array will be returned.
>>> rnd.shape
(3, 2, 4)

*Way of generation*

The polar angle, |theta|, should distribute considering the area at spherical surface,
namely depending on :math:\sin\theta.
To realize this, one can start from uniform random, convert it according to

.. math::

\theta = \cos^{-1} (1-2P)

where P is the (array of) value generated from random distribution.

The azimuth angle, |phi|, is random in the range of (0, 2 |pi|).

"""
theta_random, phi_random = getRandom_tp(size)

x = np.sin(theta_random) * np.cos(phi_random)
y = np.sin(theta_random) * np.sin(phi_random)
z = np.cos(theta_random)

return np.array([x, y, z])