3d积分,python,集成集约束

时间:2011-04-26 13:02:42

标签: python math scipy calculus

我想在某个距离b处计算球体和无限圆柱体的交点的体积,我想我会使用快速而肮脏的python脚本来完成它。我的要求是< 1s计算> 3有效数字。

我的想法是这样的: 我们将球体放置在半径为R的位置,使其中心位于原点,然后我们将圆柱体放置在半径为R'的位置,使其轴在(b,0,0)的z方向上。我们使用阶梯函数整合球体,如果我们在圆柱体内则返回1,如果不在,则返回0,因此在由球体和圆柱体内部约束的集合上积分1,即相交。

我用scipy.intigrate.tplquad尝试了这个。它没有成功。我认为这是因为步骤功能的不连续,因为我得到如下警告。当然,我可能只是做错了。假设我没有犯一些愚蠢的错误,我可以尝试制定相交的范围,从而消除了对阶梯函数的需要,但我想我可能会尝试先得到一些反馈。任何人都可以发现任何错误,或指向一些简单的解决方案。

  

警告:最大数量   细分(50)已经实现   如果增加限制产生没有   改进建议分析
  被积函数以确定   困难。如果的位置   当地的困难可以   确定(奇点,   不连续)一个可能会   通过分割间隔获得收益   并呼吁集成商   子范围。也许是一个特殊目的   应该使用积分器。

代码:

from scipy.integrate import tplquad
from math import sqrt


def integrand(z, y, x):
    if Rprim >= (x - b)**2 + y**2:
        return 1.
    else:
        return 0.

def integral():
    return tplquad(integrand, -R, R, 
                   lambda x: -sqrt(R**2 - x**2),          # lower y
                   lambda x: sqrt(R**2 - x**2),           # upper y
                   lambda x,y: -sqrt(R**2 - x**2 - y**2), # lower z
                   lambda x,y: sqrt(R**2 - x**2 - y**2), # upper z
                   epsabs=1.e-01, epsrel=1.e-01
                   )

R=1
Rprim=1
b=0.5
print integral()

4 个答案:

答案 0 :(得分:3)

假设您能够翻译和缩放数据,使得球体的原点位于[0, 0, 0]且其半径为1,那么简单的随机近似可能会给您一个合理的答案足够快。因此,沿线可能是一个很好的起点:

import numpy as np

def in_sphere(p, r= 1.):
    return np.sqrt((p** 2).sum(0))<= r

def in_cylinder(p, c, r= 1.):
    m= np.mean(c, 1)[:, None]
    pm= p- m
    d= np.diff(c- m)
    d= d/ np.sqrt(d** 2).sum()
    pp= np.dot(np.dot(d, d.T), pm)
    return np.sqrt(((pp- pm)** 2).sum(0))<= r

def in_sac(p, c, r_c):
    return np.logical_and(in_sphere(p), in_cylinder(p, c, r_c))

if __name__ == '__main__':
    n, c= 1e6, [[0, 1], [0, 1], [0, 1]]
    p= 2* np.random.rand(3, n)- 2
    print (in_sac(p, c, 1).sum()/ n)* 2** 3

答案 1 :(得分:2)

对两个域上不变的不连续函数执行三重自适应数值积分是一个非常糟糕的想法,特别是如果您希望看到速度或准确度。

我建议更好的想法是分析地减少问题。

通过变换将圆柱体与轴对齐。这会将球体转换为不在原点的某个点。

现在,找到球体与圆柱体沿该轴的交点限制。

整合该轴变量。沿着轴的任何固定值的交叉区域只是两个圆的交叉区域,而这又可以使用三角法和一点点努力来计算。

最后,您将得到一个确切的结果,几乎不需要计算时间。

答案 2 :(得分:1)

我使用简单的MC集成解决了它,正如eat所建议的那样,但我的实现速度很慢。我的要求增加了。因此,我按照木片的建议,在数学上重新解决了这个问题。

基本上我将x的极限表示为z和y的函数,y作为z的函数。然后,我实质上是使用限制在交叉点上积分f(z,y,z)= 1。我这样做是因为速度的提高,允许我绘制体积与b的关系,因为它允许我将更复杂的函数与相对较小的修改集成。

我包含我的代码以防任何人感兴趣。

from scipy.integrate import quad
from math import sqrt
from math import pi

def x_max(y,r):
    return sqrt(r**2-y**2)

def x_min(y,r):
    return max(-sqrt(r**2 - y**2), -sqrt(R**2 - y**2) + b) 

def y_max(r):
    if (R<b and b-R<r) or (R>b and b-R>r):
        return sqrt( R**2 - (R**2-r**2+b**2)**2/(4.*b**2) )
    elif r+R<b:
        return 0.
    else: #r+b<R
        return r

def z_max():
    if R>b:
        return R
    else:
        return sqrt(2.*b*R - b**2) 

def delta_x(y, r):
    return  x_max(y,r) - x_min(y,r)

def int_xy(z):
    r = sqrt(R**2 - z**2)
    return quad(delta_x, 0., y_max(r), args=(r))

def int_xyz():
    return quad(lambda z: int_xy(z)[0], 0., z_max())

R=1.
Rprim=1.
b=0.5
print 4*int_xyz()[0]

答案 3 :(得分:0)

首先关闭:您可以手动计算交叉路口的体积。如果您不想(或不能)这样做,可以选择以下方法:

我为域生成四面体网格,然后将单元格体积相加。 pygalmeshvoropy的示例(均由我自己创作):

System.gc()

这会给你

enter image description here

并将import pygalmesh import voropy ball = pygalmesh.Ball([0, 0, 0], 1.0) cyl = pygalmesh.Cylinder(-1, 1, 0.7, 0.1) u = pygalmesh.Intersection([ball, cyl]) pygalmesh.generate_mesh( u, 'out.mesh', cell_size=0.05, edge_size=0.1 ) mesh, _, _, _ = voropy.read('out.mesh') print(sum(mesh.cell_volumes)) 打印为卷。减小单元格或边缘尺寸以获得更高的精度。