繁体   English   中英

使用python3-scipy.integrate的球体体积

[英]Volume of a sphere using python3-scipy.integrate

在一段时间未使用python之后,我正试图赶上使用python进行数值求解的问题。 我正在努力解决一个玩具问题,即计算任意形状(参数指定)的体积。 到目前为止,我有一个如下的基类:

class BaseWithRange():
    def __init__(self):
        self._x_range = None

    @property
    def x_range_min(self):
        return self.x_range[0]

    @property
    def x_range_max(self):
        return self.x_range[1]

    @property
    def x_range(self):
        if self._x_range is None:
            raise NotImplementedError("You need to set the x range value")
        return self._x_range

    @x_range.setter
    def x_range(self, range):
        self._x_range = range

    def _check_in_x_range(self, x):
        errString = f"'x' is out of range of ({self.x_range_min}, {self.x_range_max}) at {x}"
        if hasattr(x, "__iter__") and not isinstance(x, str):
            if (x < self.x_range_min).any() or (x > self.x_range_max).any():
                raise ValueError(errString)
        else:
            if x < self.x_range_min or x > self.x_range_max:
                raise ValueError(errString)

    def _val(self, *args):
        raise NotImplementedError()

    def val(self, *args):
        raise NotImplementedError()


class BaseWithTwoRanges(BaseWithRange):
    def __init__(self):
        super().__init__()
        self._y_range = None

    @property
    def y_range_min(self):
        return self.y_range[0]

    @property
    def y_range_max(self):
        return self.y_range[1]

    @property
    def y_range(self):
        if self._y_range is None:
            raise NotImplementedError("You need to set the y range value")
        return self._y_range

    @y_range.setter
    def y_range(self, range):
        self._y_range = range

    def _check_in_y_range(self, y):
        errString = f"'y' is out of range of ({self.y_range_min}, {self.y_range_max}) at {y}"
        if hasattr(y, "__iter__") and not isinstance(y, str):
            if (y < self.y_range_min).any() or (y > self.y_range_max).any():
                raise ValueError(errString)
        else:
            if y < self.y_range_min or y > self.y_range_max:
                raise ValueError(errString)

class SolidBase(BaseWithTwoRanges):
    def __init__(self):
            super().__init__()
    @property
    def volume(self):
        volume, error = integrate.tplquad(self._val, self.x_range_min, self.x_range_max, self.y_lower_of_x, self.y_upper_of_x, self.z_lower_of_xy, self.z_upper_of_xy)
        print("Error: ", error)
        return volume

    def _val(self, z, y, x, *args): 
        try:
            self._check_in_x_range(x)
            self._check_in_y_range(y)
            return self.val(z, y, x, *args)
        except:
            t, v, tb = sys.exc_info()
            errorString = "Failed when called with these variables: \n y    = {0}\n x    = {1}\n args = {2}".format(y, x, args)

            raise t("{0}\n{1}".format(v, errorString))

    def val(self, z, y, x, *args):
        raise NotImplementedError()

    def y_lower_of_x(self, x):
        raise NotImplementedError()

    def y_upper_of_x(self, x):
        raise NotImplementedError()

    def z_lower_of_xy(self, x, y):
        raise NotImplementedError()

    def z_upper_of_xy(self, x, y):
        raise NotImplementedError()

...对于一个领域,我实现了以下内容:

class Sphere(SolidBase):
    def __init__(self, radius, center = [0,0,0]):
        super().__init__()
        self.radius = radius
        self.center = center
        self.x_range = [center[0], center[0] + radius]
        self.y_range = [center[1], center[1] + radius]

    def val(self, z, y, x):
        a = self.center[0]
        b = self.center[1]
        c = self.center[2]
        return c + sqrt(self.radius**2 - (x - a)**2 - (y - b)**2)

    def y_lower_of_x(self, x):
        return 0

    def y_upper_of_x(self, x):
        a = self.center[0]
        b = self.center[1]
        return b + sqrt(self.radius**2 - (x - a)**2)

    def z_lower_of_xy(self, x, y):
        return 0

    def z_upper_of_xy(self, x, y):
        a = self.center[0]
        b = self.center[1]
        c = self.center[2]
        return c + sqrt(self.radius**2 - (x - a)**2 - (y - b)**2)

问题在于半径为2的球体的体积结果给出的四分之一半球的结果为6.28,这是不正确的。 换一种说法:

In [14]: sphere = sh.Sphere(2)

In [15]: sphere.volume
Error:  8.845676088407289e-08
Out[15]: 6.283185307096017

...而不是预期的(4/3 * pi * 2 ** 3)/ 8 = 4.1887902047863905。

谁能看到我在做什么错?

编辑:添加更多的代码添加缺少的单词。

如果您正在寻找任意形状的近似体积,则正交数可能对您没有太大帮助。 问题是几乎所有的正交方法都假定您要积分的函数具有合理的平滑度(例如,某种程度的多项式)。 领域的特征功能一点都不平滑。

更好的方法是用单纯形来近似您的域,然后添加它们的体积。 可以帮助您完成任务的Python工具是

搜索网格生成 ,您将找到更多。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM