Skip to content

Feature Request: Support for Exclusive Periodic Boundaries in ND Interpolators #115

@henry2004y

Description

@henry2004y

Hi again!

FastInterpolations.jl is quickly evolving. I'm going to seek help on my common use cases of interpolating a field from numerical simulation outputs. As the boundary conditions are only supported for part of the methods (e.g. not for linear_interp), I am using WrapExtrap() currently in my workflow.

The grid setup in the following demo is a common pattern in Finite Volume simulations and periodic signal processing, where the grid points represent unique cell centers (or unique samples) and the total grid span is one short of the full period. In Interpolations.jl, this is distinguished by the gridstyle (OnGrid(), OnCell()) Interpolations.jl

Grid Configuration

  • Coordinates: x = [0.5, 1.5, 2.5] ($N=3$ points).
  • Values: y = [1.0, 2.0, 3.0].
  • Spacing: $\Delta x = 1.0$.
  • Desired Period: $L = N \times \Delta x = 3.0$.

This setup describes a domain of length 3.0 (e.g., $[0.0, 3.0]$) divided into 3 cells of width $1.0$. The data points are located at the cell centers. In a periodic system of this type:

  • The point $x = 3.0$ is equivalent to $x = 0.0$.
  • The point $x = 3.5$ is equivalent to $x = 0.5$ (the first cell center).
  • Expected Value at $x=3.5$: 1.0.

The Issue with Standard Wrapping

By default, FastInterpolations.jl's Extrap(:wrap) uses the distance between the first and last points as the period ($L_{\text{default}} = x_{\text{end}} - x_{\text{start}} = 2.0$).

  • Standard Result: $x=3.5 \pmod{2.0}$ maps to $x=1.5$.
  • Returned Value: 2.0 (Incorrect for this grid type).

Expected native support

While cubic_interp supports this via PeriodicBC(endpoint=:exclusive, period=3.0), the generic and ND methods (linear_interp, cardinal_interp, etc.) do not currently provide a unified keyword-based way to specify the Exclusive Period $L$. Is it possible to have a similar support with Extrap(:warp) without the need of manual padding? Or if you have a better idea, let me know!

using FastInterpolations

println("--- Exclusive Periodic Grid Demo ---")

# 1. Setup an "Exclusive" Grid (e.g., cell centers)
# Grid: [0.5, 1.5, 2.5], Values: [1.0, 2.0, 3.0]
# Expected Period L = 3.0 (not 2.0! Next point at 3.5 should wrap to 0.5)
x = [0.5, 1.5, 2.5]
y = [1.0, 2.0, 3.0]
dx = 1.0
L_expected = 3.0

println("Original Grid: ", x)
println("Original Values: ", y)
println("Expected Period (N*dx): ", L_expected)

# 2. Standard FastInterpolations wrapping (Inclusive)
# This uses period = last(x) - first(x) = 2.0
itp_standard = linear_interp(x, y; extrap = Extrap(:wrap))
val_standard = itp_standard(3.5)

println("\nStandard Wrap (Inclusive):")
println("  Query at x=3.5 maps to: ", 0.5 + mod(3.5 - 0.5, 2.5 - 0.5))
println("  Value at x=3.5: ", val_standard)

# 3. Exclusive Wrap via Padding
# We add a virtual point at x=3.5 with value y=1.0
x_padded = [0.5, 1.5, 2.5, 3.5]
y_padded = [1.0, 2.0, 3.0, 1.0]

itp_exclusive = linear_interp(x_padded, y_padded; extrap = Extrap(:wrap))
val_exclusive = itp_exclusive(3.5)

println("\nExclusive Wrap (via Padding):")
println("  Query at x=3.5 maps to: ", 0.5 + mod(3.5 - 0.5, 3.5 - 0.5))
println("  Value at x=3.5: ", val_exclusive, " (Success!)")

# 4. Same demo for Cardinal (Order 3) Interpolation
# This is even more important for splines to ensure C2 continuity across the boundary
itp_cardinal = cardinal_interp(x_padded, y_padded; extrap = Extrap(:wrap))
val_cardinal = itp_cardinal(3.5)

println("\nCardinal Exclusive (Order 3):")
println("  Value at x=3.5: ", val_cardinal)

# 5. Native FastInterpolations PeriodicBC (Order 3)
# Note: For non-uniform grids (including Vector), we need to specify the period.
itp_periodicBC = cubic_interp(x, y; bc = PeriodicBC(endpoint = :exclusive, period = 3.0))
val_periodicBC = itp_periodicBC(3.5)

println("\nNative PeriodicBC (endpoint=:exclusive, period=3.0):")
println("  Value at x=3.5: ", val_periodicBC)
Original Grid: [0.5, 1.5, 2.5]
Original Values: [1.0, 2.0, 3.0]
Expected Period (N*dx): 3.0

Standard Wrap (Inclusive):
  Query at x=3.5 maps to: 1.5
  Value at x=3.5: 2.0

Exclusive Wrap (via Padding):
  Query at x=3.5 maps to: 0.5
  Value at x=3.5: 1.0 (Success!)

Cardinal Exclusive (Order 3):
  Value at x=3.5: 1.0

Native PeriodicBC (endpoint=:exclusive, period=3.0):
  Value at x=3.5: 1.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureNew functionality or capability

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions