使用数字积分与scipy的3d形状的体积

时间:2018-03-17 15:12:04

标签: scipy geometry numerical-integration

我已经编写了一个计算立方体和半空间交叉体积的函数,现在我正在为它编写测试。

我试过用数字计算音量:

scipy.integrate.quadpack.IntegrationWarning: The integral is probably divergent, or slowly convergent

...基本上我整合了整个立方体,每个点基于它是否在半空间内得到值1或0。 这变得非常慢(每次调用超过几秒)并不断向我发出警告,如

{{1}}

有没有更好的方法来计算这个量?

3 个答案:

答案 0 :(得分:1)

如果我们假设半空间的边界由$ \ {(x,y,z)\ mid ax + by + cz + d = 0 \} $和$ c \ not = 0 $给出,并且感兴趣的半空间是在平面下方(在$ z $ -direction中),那么你的积分由

给出
scipy.integrate.tplquad(lambda z, y, x: 1,
                        -0.5, 0.5,
                        lambda x: -0.5, lambda x: 0.5,
                        lambda x, y: -0.5, lambda x, y: max(-0.5, min(0.5, -(b*y+a*x+d)/c)))

由于$ a $,$ b $和$ c $中的至少一个必须非零,因此可以通过更改坐标来处理$ c = 0 $的情况。

答案 1 :(得分:1)

集成

不连续功能的整合存在问题,尤其是在多维方面。需要进行一些初步工作,将问题简化为连续功能的组成部分。在这里,我计算出高度(上下)作为x和y的函数,然后使用dblquad:它返回36.2 ms

我将平面方程表示为a*x + b*y + c*z = distance。 c的符号需要注意,因为平面可以是顶部或底部的一部分。

from scipy.integrate import dblquad
distance = 0.1
a, b, c = 3, -4, 2  # normal
zmin, zmax = -0.5, 0.5  # cube bounds

# preprocessing: make sure that c > 0
# by rearranging coordinates, and flipping the signs of all if needed

height = lambda y, x: min(zmax, max(zmin, (distance-a*x-b*y)/c)) - zmin
integral = dblquad(height, -0.5, 0.5, 
                   lambda x: -0.5, lambda x: 0.5,
                   epsabs=1e-5, epsrel=1e-5)

蒙特卡罗方法

随机选取样本点(蒙特卡罗方法)避免了不连续性的问题:对于连续函数,连续函数的精度大致相同,误差以1/sqrt(N)的速率减小,其中N是样本数点。

polytope package在内部使用它。有了它,计算就可以了

import numpy as np
import polytope as pc
a, b, c = 3, 4, -5  # normal vector
distance = 0.1 
A = np.concatenate((np.eye(3), -np.eye(3), [[a, b, c]]), axis=0)
b = np.array(6*[0.5] + [distance])
p = pc.Polytope(A, b)
print(p.volume)

这里A和b将半空间编码为Ax<=b:第一个修复行用于立方体的面,最后一个用于平面。

要更好地控制精度,可以自己实施蒙特卡罗方法(简单)或使用mcint包(一样简单)。

Polytope音量:线性代数的任务,不适用于积分器

你想要计算多面体的体积,这是一个由交叉半空间形成的凸体。这应该有一个代数解决方案。 SciPy有HalfspaceIntersection类,但到目前为止(1.0.0)没有实现查找这样一个对象的体积。如果您可以找到多面体的顶点,则可以使用ConvexHull类来计算体积。但事实上,似乎SciPy空间模块没有任何帮助。也许在SciPy的未来版本......

答案 2 :(得分:1)

特征函数的积分在数学上是正确的,但不实用。一种更适合的方法是首先建立一个离散化的域版本,然后简单地总结一下小四面体的体积。

3D中的离散化可以通过pygalmesh(我的接口项目CGAL)来完成。以下代码将截止立方体离散化为

enter image description here

然后用meshio(我的另一个项目)读取文件,总结四面体体积

0.8050894798758184

您可以通过降低cell_size和/或edge_size来提高精确度,但网格划分需要更长的时间。此外,您可以指定“特征边”以准确地解析交叉边。即使最粗的单元尺寸,这也可以为您提供完全正确的结果。

import pygalmesh
import numpy
import meshio


c = pygalmesh.Cuboid([0, 0, 0], [1, 1, 1])
h = pygalmesh.HalfSpace([1.0, 2.0, 3.0], 4.0, 10.0)
u = pygalmesh.Intersection([c, h])
pygalmesh.generate_mesh(u, 'out.mesh', cell_size=3.0e-2, edge_size=3.0e-2)

points, cells, *_ = meshio.read('out.mesh')

def compute_tet_volumes(vertices, tets):
    cell_coords = vertices[tets]
    a = cell_coords[:, 1, :] - cell_coords[:, 0, :]
    b = cell_coords[:, 2, :] - cell_coords[:, 0, :]
    c = cell_coords[:, 3, :] - cell_coords[:, 0, :]
    # omega = <a, b x c>
    omega = numpy.einsum('ij,ij->i', a, numpy.cross(b, c))
    # https://en.wikipedia.org/wiki/Tetrahedron#Volume
    return abs(omega) / 6.0

print(numpy.sum(compute_tet_volumes(points, cells['tetra'])))

查看pygalmesh(我的一个项目)。它让你轻松创建复杂的三维网格,一旦你拥有网格,只需要总结四面体的体积。