区域体积的Python数值积分

时间:2014-05-29 21:23:16

标签: python algorithm numpy numerical-methods montecarlo

对于程序,我需要一种算法来快速计算实体的体积。这个形状由一个函数指定,给定一个点P(x,y,z),如果P是实体的一个点,则返回1,如果P不是实体的一个,则返回0。

我尝试使用numpy使用以下测试:

import numpy
from scipy.integrate import *
def integrand(x,y,z):
    if x**2. + y**2. + z**2. <=1.:
        return 1.
    else:
        return 0.
g=lambda x: -2.
f=lambda x: 2.
q=lambda x,y: -2.
r=lambda x,y: 2.
I=tplquad(integrand,-2.,2.,g,f,q,r)
print I

但它没有给我以下错误:

  

警告(来自警告模块):     文件“C:\ Python27 \ lib \ site-packages \ scipy \ integrate \ quadpack.py”,第321行       warnings.warn(msg,IntegrationWarning)   IntegrationWarning:已实现最大细分数(50)。     如果增加限制没有产生任何改善,建议分析     被积函数以确定困难。如果一个位置     可以确定局部困难(奇点,不连​​续性)     可能从分割间隔和调用积分器中获益     关于子范围。也许应该使用专用集成商。

     

警告(来自警告模块):     文件“C:\ Python27 \ lib \ site-packages \ scipy \ integrate \ quadpack.py”,第321行       warnings.warn(msg,IntegrationWarning)   IntegrationWarning:算法不收敛。检测到舍入错误     在外推表中。假设要求的容差     无法实现,并且返回的结果(如果full_output = 1)是     最好的,可以获得。

     

警告(来自警告模块):     文件“C:\ Python27 \ lib \ site-packages \ scipy \ integrate \ quadpack.py”,第321行       warnings.warn(msg,IntegrationWarning)   IntegrationWarning:检测到舍入错误的发生,这可以防止     要求的容忍度。错误可能是     低估。

     

警告(来自警告模块):     文件“C:\ Python27 \ lib \ site-packages \ scipy \ integrate \ quadpack.py”,第321行       warnings.warn(msg,IntegrationWarning)   IntegrationWarning:积分可能是发散的,或者是缓慢收敛的。

所以,当然,我寻找“专用集成商”,但找不到任何能满足我需要的东西。

然后,我尝试使用蒙特卡罗方法编写自己的集成,并使用相同的形状对其进行测试:

import random

# Monte Carlo Method
def get_volume(f,(x0,x1),(y0,y1),(z0,z1),prec=0.001,init_sample=5000):
    xr=(x0,x1)
    yr=(y0,y1)
    zr=(z0,z1)
    vdomain=(x1-x0)*(y1-y0)*(z1-z0)
    def rand((p0,p1)):
        return p0+random.random()*(p1-p0)
    vol=0.
    points=0.
    s=0.  # sum part of variance of f
    err=0.
    percent=0
    while err>prec or points<init_sample:
        p=(rand(xr),rand(yr),rand(zr))
        rpoint=f(p)
        vol+=rpoint
        points+=1
        s+=(rpoint-vol/points)**2
        if points>1:
            err=vdomain*(((1./(points-1.))*s)**0.5)/(points**0.5)
        if err>0:
            if int(100.*prec/err)>=percent+1:
                percent=int(100.*prec/err)
                print percent,'% complete\n  error:',err
    print int(points),'points used.'
    return vdomain*vol/points
f=lambda (x,y,z): ((x**2)+(y**2)<=4.) and ((z**2)<=9.) and ((x**2)+(y**2)>=0.25)
print get_volume(f,(-2.,2.),(-2.,2.),(-2.,2.))

但是效果太慢了。对于这个程序,我将使用这个数值积分大约100次左右,我也将在更大的形状上进行,如果不是一两个小时,它将花费几分钟,现在的速度,更不用说我想要的精度高于2位小数。

我尝试过实施MISER Monte Carlo方法,但遇到了一些困难,我仍然不确定它会有多快。

所以,我问是否有任何库可以做我要求的,或者是否有更好的算法可以快几倍(相同的准确度)。欢迎任何建议,因为我已经在这方面工作了很长一段时间。

编辑:

如果我无法在Python中使用它,我可以切换到任何其他可编译且具有相对简单的GUI功能的语言。欢迎任何建议。

3 个答案:

答案 0 :(得分:3)

正如其他人已经注意到的那样,找到布尔函数给出的域的数量很难。您可以使用位于pygalmesh顶部的CGAL(我的一个小项目)并返回一个四面体网格。此

import pygalmesh
class Custom(pygalmesh.DomainBase):
    def __init__(self):
        super(Custom, self).__init__()
        return

    def eval(self, x):
        return (x[0]**2 + x[1]**2 + x[2]**2) - 1.0

    def get_bounding_sphere_squared_radius(self):
        return 2.0

pygalmesh.generate_mesh(Custom(), 'out.mesh', cell_size=0.1)

给你

enter image description here

从那时起,您可以使用各种包来提取音量。一种可能性:voropy(动物园里的另一个):

import numpy
import voropy

mesh, _, _, _ = voropy.read('out.mesh')

print(numpy.sum(mesh.cell_volumes))

给出

4.16203534234

足够接近真值4/3 pi = 4.18879020478...。如果想要更精确,请减少上面网格生成中的cell_size

答案 1 :(得分:2)

你的功能不是一个连续的功能,我认为很难进行整合。

怎么样:

import numpy as np

def sphere(x,y,z):
    return x**2 + y**2 + z**2 <= 1

x, y, z = np.random.uniform(-2, 2, (3, 2000000))

sphere(x, y, z).mean() * (4**3), 4/3.0*np.pi

输出:

(4.1930560000000003, 4.1887902047863905)

或VTK:

from tvtk.api import tvtk

n = 151
r = 2.0
x0, x1 = -r, r
y0, y1 = -r, r
z0, z1 = -r, r
X,Y,Z = np.mgrid[x0:x1:n*1j, y0:y1:n*1j, z0:z1:n*1j]
s = sphere(X, Y, Z)

img = tvtk.ImageData(spacing=((x1-x0)/(n-1), (y1-y0)/(n-1), (z1-z0)/(n-1)), 
                     origin=(x0, y0, z0), dimensions=(n, n, n))

img.point_data.scalars = s.astype(float).ravel()

blur = tvtk.ImageGaussianSmooth(input=img) 
blur.set_standard_deviation(1)
contours = tvtk.ContourFilter(input = blur.output) 

contours.set_value(0, 0.5)
mp = tvtk.MassProperties(input = contours.output)
mp.volume, mp.surface_area

输出:

4.186006622559839, 12.621690438955586

答案 2 :(得分:1)

如果不对边界给出一点暗示似乎很困难:

import numpy as np
from scipy.integrate import *

def integrand(z,y,x):
    return 1. if x**2 + y**2 + z**2 <= 1. else 0.

g=lambda x: -2
h=lambda x: 2
q=lambda x,y: -np.sqrt(max(0, 1-x**2-y**2))
r=lambda x,y: np.sqrt(max(0, 1-x**2-y**2))
I=tplquad(integrand,-2.,2.,g,h,q,r)

print I