在球壳内生成积分点

时间:2018-03-04 23:08:56

标签: python numpy math

给定一个球壳体积(即一个球体被相同中心的较小球体减去),我需要计算该体积内的所有积分位置。

我可以通过计算外半径内的所有积分点(通过直径达到外径的所有整数的笛卡尔积)并随后移除内半径内的点来轻松完成此任务。

然而,在某些情况下,半径可能相当大,因此生成外半径内的所有点可能比我想要的更耗时。因此,如果可能的话,我想消除浪费计算内半径内点数的处理时间(或至少最小化必要数量)。

以下是一些当前代码,但半径可能更大。

import numpy as n
import numpy.linalg as la

def cartesian_product(*arrays):
    la = len(arrays)
    dtype = n.result_type(*arrays)
    arr = n.empty([len(a) for a in arrays] + [la], dtype=dtype)
    for i, a in enumerate(n.ix_(*arrays)):
        arr[..., i] = a
    return arr.reshape(-1, la)

def points_in_shell(inner_radius, outer_radius):
    cutoff = int(n.ceil(outer_radius))
    int_range = n.arange(-cutoff, cutoff+1)
    pos_initial = cartesian_product(int_range, int_range, int_range)
    pos_norm = la.norm(pos_initial, axis=1)

    valid_idx = n.where((pos_norm >= inner_radius) & (pos_norm <= outer_radius))
    pos_final = pos_initial[valid_idx]
    return pos_final

vals = points_in_shell(89.634, 95.254)

有什么想法吗?

2 个答案:

答案 0 :(得分:0)

它不是numpy - 友好,但显而易见的答案是计算所需的整数范围。对于每个 x ,计算最大的| y |这样 x ^ 2 + y ^ 2&lt; = R ^ 2(到目前为止只是外半径);通过向下舍入sqrt(R*R-x*x)来实现此目的。

然后,对于该范围内的每个 y ,计算| z |的范围。这样 r ^ 2&lt; = x ^ 2 + y ^ 2 + z ^ 2&lt; = R ^ 2,向下舍入sqrt(R*R-x*x-y*y)并向上舍入sqrt(max(r*r-x*x-y*y,0))。然后在两个结果范围内生成 z 的所有三元组(但不是两次!)。

答案 1 :(得分:0)

更新:适应更新的问题,并使解决方案完全稀疏。

def f_sparser_oct(IR, OR):
    OR2, IR2 = OR*OR, IR*IR
    out = np.ones((int(OR)+1, 3), dtype=int)
    out[:, 0] = i = np.arange(int(OR)+1)
    i2 = i*i
    jh = np.sqrt(OR2-i2).astype(int)
    out = np.repeat(out, jh+1, axis=0)
    I, J, K = out.T
    J[0] = 0
    J[np.cumsum(jh[:-1]+1)] = -jh[:-1]
    J[:] = np.cumsum(J)
    kh = np.sqrt(OR2-i2[I]-i2[J]).astype(int)
    kl = np.ceil(np.sqrt(np.maximum(0, IR2-i2[I]-i2[J]))).astype(int)
    out = np.repeat(out, 1+kh-kl, axis=0)
    I, J, K = out.T
    K[0] = kl[0]
    K[np.cumsum(1 + kh[:-1] - kl[:-1])] = kl[1:] - kh[:-1]
    K[:] = np.cumsum(K)
    return out

def f_sparser_full(IR, OR):
    OR2, IR2 = OR*OR, IR*IR
    out = np.ones((2*int(OR)+1, 3), dtype=int)
    out[:, 0] = i = np.arange(-int(OR), int(OR)+1)
    i2 = i*i
    i2i = np.r_[i2[int(OR):], i2[:int(OR)]]
    jh = np.sqrt(OR2-i2).astype(int)
    out = np.repeat(out, 2*jh+1, axis=0)
    I, J, K = out.T
    J[0] = -jh[0]
    J[np.cumsum(2*jh[:-1]+1)] = -jh[1:] - jh[:-1]
    J[:] = np.cumsum(J)
    kh = np.sqrt(OR2-i2i[I]-i2i[J]).astype(int)
    kl = np.ceil(np.sqrt(np.maximum(0, IR2-i2i[I]-i2i[J]))).astype(int)
    szs = 2*(1 + kh - kl)-(kl==0)
    out = np.repeat(out, szs, axis=0)
    I, J, K = out.T
    K[0] = -kh[0]
    SZS = np.cumsum(szs)
    K[SZS[:-1]] = -kh[1:] - kh[:-1]
    K[SZS[kl!=0] - 1 - kh[kl!=0] + kl[kl!=0]] = 2 * kl[kl!=0]
    K[:] = np.cumsum(K)
    return out

演示:

>>> np.all(f_sparser_full(89.634, 95.254) == vals)
True
>>> t0 = time.perf_counter(); f_sparser_full(998, 1000).shape; t1 = time.perf_counter()
(25092914, 3)
>>> t1-t0
0.969647804973647

更新结束。

以下是两个快速numpy建议:第一个很短但使用了大量内存。第二个循环但使用较少的内存:

import numpy as np

def f_where(IR, OR):
    i, j, k = np.ogrid[:OR+1, :OR+1, :OR+1]
    R2 = i*i + j*j + k*k
    return np.argwhere((R2>=IR*IR) & (R2<=OR*OR))

def f_sparse(IR, OR):
    out = []
    for I in range(int(OR)+1):
        O = np.sqrt(OR*OR - I*I)
        i, j, k = np.ogrid[I:I+1, :O+1, :O+1]
        R2 = i*i + j*j + k*k
        out.append(np.argwhere((R2>=IR*IR) & (R2<=OR*OR))+(I, 0, 0))
    return np.concatenate(out, axis=0)

演示:

>>> np.all(f_sparse(89.634, 95.254) == vals)
True
>>> 
>>> import time
>>> 
>>> t0 = time.perf_counter(); f_where(298.5, 300.0).shape; t1 = time.perf_counter()
(211366, 3)
>>> t1-t0
0.20139808906242251
>>> t0 = time.perf_counter(); f_sparse(298.5, 300.0).shape; t1 = time.perf_counter()
(211366, 3)
>>> t1-t0
0.1110449800034985
>>> t0 = time.perf_counter(); f_sparse(888.513, 900.099).shape; t1 = time.perf_counter()
(14577694, 3)
>>> t1-t0
3.6043728749500588