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

时间:2018-10-03 20:32:13

标签: python scipy numerical-integration

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

sage -pip install pandas

...对于一个领域,我实现了以下目标:

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()

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

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)

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

有人能看到我在做什么吗?

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

1 个答案:

答案 0 :(得分:0)

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

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

  • pygalmesh(属于我的项目)
  • pygmsh(属于我的另一个项目)

搜索网格生成,您会发现更多的信息。