简体   繁体   English

具有周期性边界条件的2D插值

[英]2D Interpolation with periodic boundary conditions

I'm running a simulation on a 2D space with periodic boundary conditions. 我在具有周期性边界条件的2D空间上运行模拟。 A continuous function is represented by its values on a grid. 连续函数由其在网格上的值表示。 I need to be able to evaluate the function and its gradient at any point in the space. 我需要能够在空间的任何一点评估函数及其梯度。 Fundamentally, this isn't a hard problem -- or to be precise, it's an almost already solved problem. 从根本上说,这不是一个难题 - 或者确切地说,这是一个几乎已经解决的问题。 The function can be interpolated using a cubic spline with scipy.interpolate.RectBivariateSpline. 该函数可以使用带有scipy.interpolate.RectBivariateSpline的三次样条插值进行插值。 The reason it's almost solved is that RectBivariateSpline cannot handle periodic boundary conditions, nor can anything else in scipy.interpolate, as far as I can figure out from the documentation. 几乎解决的原因是RectBivariateSpline无法处理周期性边界条件,scipy.interpolate中也没有其他任何东西,只要我从文档中可以看出来。

Is there a python package that can do this? 有没有python包可以做到这一点? If not, can I adapt scipy.interpolate to handle periodic boundary conditions? 如果没有,我可以调整scipy.interpolate来处理周期性边界条件吗? For instance, would it be enough to put a border of, say, four grid elements around the entire space and explicitly represent the periodic condition on it? 例如,是否足以在整个空间周围放置四个网格元素的边界并明确表示其上的周期性条件?

[ADDENDUM] A little more detail, in case it matters: I am simulating the motion of animals in a chemical gradient. [附录]稍微详细一点,如果重要的话:我正在用化学梯度模拟动物的运动。 The continuous function I mentioned above is the concentration of a chemical that they are attracted to. 我上面提到的连续功能是它们被吸引的化学物质的浓度。 It changes with time and space according to a straightforward reaction/diffusion equation. 它根据直接的反应/扩散方程随时间和空间变化。 Each animal has an x,y position (which cannot be assumed to be at a grid point). 每只动物都有一个x,y位置(不能假设它位于网格点)。 They move up the gradient of attractant. 它们向上移动了引诱剂的梯度。 I'm using periodic boundary conditions as a simple way of imitating an unbounded space. 我使用周期性边界条件作为模仿无界空间的简单方法。

It appears that the python function that comes closest is scipy.signal.cspline2d. 最接近的python函数似乎是scipy.signal.cspline2d。 This is exactly what I want, except that it assumes mirror-symmetric boundary conditions. 这正是我想要的, 除了它假设镜像对称的边界条件。 Thus, it appears that I have three options: 因此,我认为有三种选择:

  1. Write my own cubic spline interpolation function that works with periodic boundary conditions, perhaps using the cspline2d sources (which are based on functions written in C) as a starting point. 编写我自己的三次样条插值函数,该函数适用于周期性边界条件,可能使用cspline2d源(基于用C编写的函数)作为起点。

  2. The kludge: the effect of data at i on the spline coefficient at j goes as r^|ij|, with r = -2 + sqrt(3) ~ -0.26. kludge:i处的数据对j处的样条系数的影响变为r ^ | ij |,其中r = -2 + sqrt(3)〜-0.26。 So the effect of the edge is down to r^20 ~ 10^-5 if I nest the grid within a border of width 20 all the way around that replicates the periodic values, something like this: 因此,如果我将网格嵌套在宽度为20的边界内,那么边缘的效果会降低到r ^ 20~10 ^ -5,这会复制周期值,如下所示:

    bzs1 = np.array( [zs1[i%n,j%n] for i in range(-20, n+20) for j in range(-20, n+20)] ) bzs1 = np.array([zs1 [i%n,j%n]表示范围(-20,n + 20)中的i,范围(-20,n + 20)中的j)]
    bzs1 = bzs1.reshape((n + 40, n + 40)) bzs1 = bzs1.reshape((n + 40,n + 40))

    Then I call cspline2d on the whole array, but use only the middle. 然后我在整个数组上调用cspline2d,但只使用中间。 This should work, but it's ugly. 这应该有效,但它很难看。

  3. Use Hermite interpolation instead. 请改用Hermite插值。 In a 2D regular grid, this corresponds to bicubic interpolation . 在2D规则网格中,这对应于双三次插值 The disadvantage is that the interpolated function has a discontinuous second derivative. 缺点是内插函数具有不连续的二阶导数。 The advantages are it is (1) relatively easy to code, and (2) for my application, computationally efficient. 优点是(1)相对容易编码,(2)对于我的应用,计算效率高。 At the moment, this is the solution I'm favoring. 目前,这是我喜欢的解决方案。

I did the math for interpolation with trig functions rather than polynomials, as @mdurant suggested. 正如@mdurant建议的那样,我使用trig函数而不是多项式进行插值算法。 It turns out to be very similar to the cubic spline, but requires more computation and produces worse results, so I won't be doing that. 事实证明它与三次样条非常相似,但需要更多的计算并产生更糟的结果,所以我不会这样做。

EDIT: A colleague told me of a fourth solution: 编辑:一位同事告诉我第四个解决方案:

  1. The GNU Scientific Library (GSL) has interpolation functions that can handle periodic boundary conditions. GNU科学库 (GSL)具有可以处理周期性边界条件的插值函数。 There are two (at least) python interfaces to GSL: PyGSL and CythonGSL . GSL有两个(至少)python接口: PyGSLCythonGSL Unfortunately, GSL interpolation seems to be restricted to one dimension, so it's not a lot of use to me, but there's lots of good stuff in GSL. 不幸的是,GSL插值似乎仅限于一个维度,所以它对我来说并不是很有用,但GSL中有很多好东西。

Another function that could work is scipy.ndimage.interpolation.map_coordinates . 另一个scipy.ndimage.interpolation.map_coordinates功能是scipy.ndimage.interpolation.map_coordinates It does spline interpolation with periodic boundary conditions. 它使用周期性边界条件进行样条插值。 It does not not directly provide derivatives, but you could calculate them numerically. 它不直接提供衍生物,但您可以用数字计算它们。

These functions can be found at my github, master/hmc/lattice.py : 这些函数可以在我的github, master/hmc/lattice.py找到:

  • Periodic boundary conditions The Periodic_Lattice() class is described here in full. 周期性边界条件 此处描述了 Periodic_Lattice()类。
  • Lattice Derivatives In the repository you will find a laplacian function, a squared gradient (for the gradient just take the square root) and and overloaded version of np.ndarray 格子衍生物在存储库中你会发现一个拉普拉斯函数,一个平方渐变(对于渐变只取平方根)和np.ndarray重载版本
  • Unit Tests The test cases can be found in same repo in tests/test_lattice.py 单元测试测试用例可以在tests/test_lattice.py中的相同repo中tests/test_lattice.py

I have been using the following function which augments the input to create data with effective periodic boundary conditions. 我一直在使用以下函数来增加输入以创建具有有效周期性边界条件的数据。 Augmenting the data has a distinct advantage over modifying an existing algorithm: the augmented data can easily be interpolated using any algorithm. 与修改现有算法相比,增加数据具有明显的优势:可以使用任何算法轻松地对增强数据进行插值。 See below for an example. 请参阅下面的示例。

def augment_with_periodic_bc(points, values, domain):
    """
    Augment the data to create periodic boundary conditions.

    Parameters
    ----------
    points : tuple of ndarray of float, with shapes (m1, ), ..., (mn, )
        The points defining the regular grid in n dimensions.
    values : array_like, shape (m1, ..., mn, ...)
        The data on the regular grid in n dimensions.
    domain : float or None or array_like of shape (n, )
        The size of the domain along each of the n dimenions
        or a uniform domain size along all dimensions if a 
        scalar. Using None specifies aperiodic boundary conditions.

    Returns
    -------
    points : tuple of ndarray of float, with shapes (m1, ), ..., (mn, )
        The points defining the regular grid in n dimensions with 
        periodic boundary conditions.
    values : array_like, shape (m1, ..., mn, ...)
        The data on the regular grid in n dimensions with periodic
        boundary conditions.
    """
    # Validate the domain argument
    n = len(points)
    if np.ndim(domain) == 0:
        domain = [domain] * n
    if np.shape(domain) != (n,):
        raise ValueError("`domain` must be a scalar or have the same "
                         "length as `points`")

    # Pre- and append repeated points
    points = [x if d is None else np.concatenate([x - d, x, x + d]) 
              for x, d in zip(points, domain)]

    # Tile the values as necessary
    reps = [1 if d is None else 3 for d in domain]
    values = np.tile(values, reps)

    return points, values

Example

The example below shows interpolation with periodic boundary conditions in one dimension but the function above can be applied in arbitrary dimensions. 下面的示例显示了在一维中使用周期性边界条件进行插值,但上述函数可以应用于任意维度。

周期插值的例子

rcParams['figure.dpi'] = 144
fig, axes = plt.subplots(2, 2, True, True)

np.random.seed(0)
x = np.linspace(0, 1, 10, endpoint=False)
y = np.sin(2 * np.pi * x)
ax = axes[0, 0]
ax.plot(x, y, marker='.')
ax.set_title('Points to interpolate')

sampled = np.random.uniform(0, 1, 100)
y_sampled = interpolate.interpn([x], y, sampled, bounds_error=False)
valid = ~np.isnan(y_sampled)
ax = axes[0, 1]
ax.scatter(sampled, np.where(valid, y_sampled, 0), marker='.', c=np.where(valid, 'C0', 'C1'))
ax.set_title('interpn w/o periodic bc')

[x], y = augment_with_periodic_bc([x], y, domain=1.0)
y_sampled_bc = interpolate.interpn([x], y, sampled)
ax = axes[1, 0]
ax.scatter(sampled, y_sampled_bc, marker='.')
ax.set_title('interpn w/ periodic bc')

y_sampled_bc_cubic = interpolate.interp1d(x, y, 'cubic')(sampled)
ax = axes[1, 1]
ax.scatter(sampled, y_sampled_bc_cubic, marker='.')
ax.set_title('cubic interp1d w/ periodic bc')

fig.tight_layout()

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

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