Conservative Interpolation

Conservative Interpolation#

A common requirement for interpolation in astrophysics is that it is conservative. Let’s see what that means.

Imagine that we divide our domain into uniformly sized bins, and in each bin we place some mass. We can describe the mass by its density, and in each of the bins, we store the average density, ρ:

density in equally spaced bins

Here, xi1/2 is the coordinate of the left edge of bin i, and xi+1/2 is the coordinate of the right edge of bin i. The mass in bin i would be computed as:

mi=xi1/2xi+1/2ρ(x)dx

and the average density in that bin would be:

ρi=miΔx

Imagine that we want to construct a cubic interpolant through zones i2, i1, i, and i+1 that conserves mass, i.e., when we integrate the interpolant over each of the bins, it gives us the correct amount of mass for that bin. This is called a conservative interpolant and is the basis for reconstruction of the density / mass when we have gridded data that we will use when we move onto PDEs.

So we seek something of the form:

ρ(x)=a(xxi)3+b(xxi)2+c(xxi)+d

constrained such that:

ρi2=1Δxxi5/2xi3/2ρ(x)dxρi1=1Δxxi3/2xi1/2ρ(x)dxρi=1Δxxi1/2xi+1/2ρ(x)dxρi+1=1Δxxi+1/2xi+3/2ρ(x)dx

This is 4 equations and 4 unknowns (a, b, c, and d), so we can solve this.

Let’s use SymPy

from sympy import init_session
init_session(use_latex="mathjax")
IPython console for SymPy 1.12 (Python 3.10.14-64-bit) (ground types: python)

These commands were executed:
>>> from sympy import *
>>> x, y, z, t = symbols('x y z t')
>>> k, m, n = symbols('k m n', integer=True)
>>> f, g, h = symbols('f g h', cls=Function)
>>> init_printing()

Documentation can be found at https://docs.sympy.org/1.12/
x0 = symbols("x0")
dx = symbols("\Delta{}x")
rm2, rm, r0, rp = symbols(r"\rho_{i-2} \rho_{i-1} \rho_i \rho_{i+1}")
xm52 = x0 - Rational(5,2)*dx
xm32 = x0 - Rational(3,2)*dx
xm12 = x0 - Rational(1,2)*dx
xp12 = x0 + Rational(1,2)*dx
xp32 = x0 + Rational(3,2)*dx
a, b, c, d = symbols("a b c d")

Make our cubic function

f = a*(x-x0)**3 + b*(x-x0)**2 + c*(x-x0) + d
f
a(xx0)3+b(xx0)2+c(xx0)+d

Do the 4 integrals

A = simplify(integrate(f/dx, (x, xm52, xm32)))
B = simplify(integrate(f/dx, (x, xm32, xm12)))
C = simplify(integrate(f/dx, (x, xm12, xp12)))
D = simplify(integrate(f/dx, (x, xp12, xp32)))

Solve for our coefficients

coeffs = solve([A - rm2, B - rm, C - r0, D - rp], [a, b, c, d], check=False)
coeffs
{a:3ρi+ρi+1+3ρi1ρi26Δx3, b:2ρi+ρi+1+ρi12Δx2, c:15ρi+7ρi+127ρi1+5ρi224Δx, d:13ρi12ρi+124ρi124}

and let’s see the polynomial

fc = f.subs(a,coeffs[a]).subs(b,coeffs[b]).subs(c,coeffs[c]).subs(d,coeffs[d])
fc
13ρi12ρi+124ρi124+(xx0)(15ρi+7ρi+127ρi1+5ρi2)24Δx+(xx0)2(2ρi+ρi+1+ρi1)2Δx2+(xx0)3(3ρi+ρi+1+3ρi1ρi2)6Δx3

In the paper The Piecewise Parabolic Method (PPM) for Gas-dynamical Systems this cubic conservative interpolant is used to find the state on the middle edge of our interval, xi1/2. Let’s evaluate our function there:

fc.subs(x, x0-Rational(1,2)*dx)
7ρi12ρi+112+7ρi112ρi212

This is equivalent to equation 1.9 in the PPM paper:

ρi1/2=712(ρi1+ρi)112(ρi2+ρi+1)

The PPM algorithm is widely-used in astrophysics for modeling hydrodynamics. Using a conservative interpolant ensure that we are not creating any extra mass when we go from our bin-average values of density to the density at a specific location.