From c9834b8b3d6209a9f41f3d56e1ca85828327fd9d Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner <inform@tiker.net> Date: Fri, 17 Sep 2021 12:55:16 -0500 Subject: [PATCH] Add Matt's sphere_sample --- pytools/__init__.py | 51 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/pytools/__init__.py b/pytools/__init__.py index ad21b25..64a896b 100644 --- a/pytools/__init__.py +++ b/pytools/__init__.py @@ -2,7 +2,10 @@ # (Yes, it has a point!) -__copyright__ = "Copyright (C) 2009-2013 Andreas Kloeckner" +__copyright__ = """ +Copyright (C) 2009-2013 Andreas Kloeckner +Copyright (C) 2020 Matt Wala +""" __license__ = """ Permission is hereby granted, free of charge, to any person obtaining a copy @@ -165,6 +168,11 @@ Hashing .. autofunction:: unordered_hash +Sampling +-------- + +.. autofunction:: sphere_sample + Type Variables Used ------------------- @@ -2691,6 +2699,47 @@ def unordered_hash(hash_instance, iterable, hash_constructor=None): # }}} +# {{{ sphere_sample + +def sphere_sample(npoints_approx, r=1): + """Generate points regularly distributed on a sphere. + Based on: https://www.cmu.edu/biolphys/deserno/pdf/sphere_equi.pdf + Returns an array of shape (npoints, 3). npoints does not generally equally + npoints_approx. + """ + + import numpy as np + points = [] + + count = 0 + a = 4 * np.pi / npoints_approx + d = a ** 0.5 + M_theta = int(np.ceil(np.pi / d)) # noqa: N806 + d_theta = np.pi / M_theta + d_phi = a / d_theta + + for m in range(M_theta): + theta = np.pi * (m + 0.5) / M_theta + M_phi = int(np.ceil((2 * np.pi * np.sin(theta) / d_phi))) # noqa: N806 + for n in range(M_phi): + phi = 2 * np.pi * n / M_phi + points.append([r * np.sin(theta) * np.cos(phi), + r * np.sin(theta) * np.sin(phi), + r * np.cos(theta)]) + count += 1 + + # Add poles. + for i in range(3): + for sign in [-1, +1]: + pole = np.zeros(3) + pole[i] = r * sign + points.append(pole) + + return np.array(points) + +# }}} + + def _test(): import doctest doctest.testmod() -- GitLab