Skip to main content

Kernel library

All kernels are evaluated in compact form:

W(r,h)  =  Cd h−d k(q),q=rh∈[0,1],W(\mathbf{r}, h) \;=\; C_d\, h^{-d}\, k(q), \qquad q = \frac{r}{h} \in [0, 1],

with every kernel supported on q≤1q \le 1 (i.e. a support radius of 1⋅h1\cdot h, regardless of the classical convention some splines use). The smoothstep-style cpow_warp(x, p) = \max(x, 0)^p convention implements the knot terms. Derivatives are stored as the compact ladder k,k′,k′′,k′′′k, k', k'', k''' and re-scaled with powers of hh at the evaluation level, so a kernel "family" is one module in kernels/kernelFunctions/ exposing *_k, *_dkdq, *_d2kdq2, *_d3kdq3, *_C_d, *_kernelScale, *_packingRatio.

The kernels​

Select with KernelFunctions.<Name> in OperationProperties.

KernelCompact form k(q)k(q) (per dim where it differs)CdC_d (1D / 2D / 3D)Smoothness
Poly6(1−q2)3(1 - q^2)^335/3235/32 / 4/π4/\pi / 315/(64π)315/(64\pi)C2C^2
Spiky(1−q)3(1-q)^322 / 10/π10/\pi / 15/π15/\piC1C^1
CubicSpline(1−q)3−4(0.5−q)3(1-q)^3 - 4(0.5-q)^38/38/3 / 80/(7π)80/(7\pi) / 16/π16/\piC2C^2
QuarticSpline(1−q)4−5(0.6−q)4+10(0.2−q)4(1-q)^4 - 5(0.6-q)^4 + 10(0.2-q)^43125/7683125/768 / 46875/(2398π)46875/(2398\pi) / 15625/(512π)15625/(512\pi)C3C^3
QuinticSpline(1−q)5−6(23−q)5+15(13−q)5(1-q)^5 - 6(\tfrac{2}{3}-q)^5 + 15(\tfrac{1}{3}-q)^5243/40243/40 / 15309/(478π)15309/(478\pi) / 2187/(40π)2187/(40\pi)C4C^4
B7(q−1)6−7(q−57)6+21(q−37)6−35(q−17)6(q-1)^6 - 7(q-\tfrac57)^6 + 21(q-\tfrac37)^6 - 35(q-\tfrac17)^6823543/92160823543/92160 / 5764801/(113149π)5764801/(113149\pi) / 5764801/(61440π)5764801/(61440\pi)C5C^5
B856(q−0.25)7−28(q−0.5)7+8(q−0.75)7−(q−1)756(q-0.25)^7 - 28(q-0.5)^7 + 8(q-0.75)^7 - (q-1)^74096/3154096/315 / 589824/(7435π)589824/(7435\pi) / 16384/(105π)16384/(105\pi)C6C^6
HOCT4Pq+QPq + Q for q≤αq \le \alpha; (1−q)4+165(0.75−q)4−945(0.5−q)4(1-q)^4 + \tfrac{16}{5}(0.75-q)^4 - \tfrac{94}{5}(0.5-q)^4 for q>αq > \alpha (α≈0.214108\alpha \approx 0.214108, Read et al. 2010 eqs. 46-51)2.068432.06843 / 3.715833.71583 / 6.515056.51505C2C^2; designed central cusp k′(0)=P≠0k'(0) = P \ne 0
Gaussianexp⁡(−128q2)\exp(-128q^2), truncated at 16σ16\sigma (σ=h/16\sigma = h/16)(128/π)1/2(128/\pi)^{1/2} / 128/π128/\pi / (128/π)3/2(128/\pi)^{3/2}analytic; truncation at the e−128e^{-128} level
Wendland21D: (1−q)3(1+3q)(1-q)^3(1+3q); d≥2: (1−q)4(1+4q)(1-q)^4(1+4q)5/45/4 / 7/π7/\pi / 21/(2π)21/(2\pi)C2C^2, positive definite
Wendland41D: (1−q)5(1+5q+8q2)(1-q)^5(1+5q+8q^2); d≥2: (1−q)6(1+6q+353q2)(1-q)^6(1+6q+\tfrac{35}{3}q^2)3/23/2 / 9/π9/\pi / 495/(32π)495/(32\pi)C4C^4, positive definite
Wendland61D: (1−q)7(1+7q+19q2+21q3)(1-q)^7(1+7q+19q^2+21q^3); d≥2: (1−q)8(1+8q+25q2+32q3)(1-q)^8(1+8q+25q^2+32q^3)55/3255/32 / 78/(7π)78/(7\pi) / 1365/(64π)1365/(64\pi)C6C^6, positive definite
ViscosityKernel−12q3+q2+12q−1-\tfrac12 q^3 + q^2 + \tfrac{1}{2q} - 1 (XSPH viscosity)15/(2π)15/(2\pi) / 10/(9π)10/(9\pi) / 15/(2π)15/(2\pi)—
AdhesionKernel(−4q2+6q−2)1/4(-4q^2 + 6q - 2)^{1/4} for q>0.5q > 0.5, else 000.0070.007 (all dims)—
CohesionKernel2(1−q)3q3−1/642(1-q)^3 q^3 - 1/64 for q<0.5q < 0.5; (1−q)3q3(1-q)^3 q^3 else32/π32/\pi (all dims)—

The Wendland family is the default in the notebooks and tests (KernelFunctions.Wendland2); it is the family the lattice-normalization calibration has a closed-form analysis for (positive definite ⇒\Rightarrow L(nh)>1L(n_h) > 1 strictly, no root in hh).

Packing and support properties​

Each kernel module also provides (Dehnen & Aly, Improving convergence in SPH simulations) the properties properties.py exposes:

  • sphKernelScale(kernel, dim) — the hh-to-spacing scale factor (the support in particle-spacings for a well-packed lattice);
  • sphKernelN_H(kernel, dim) — the expected neighbor count NH=facd packd scaledN_H = \mathrm{fac}_d\, \mathrm{pack}^{d}\, \mathrm{scale}^{d} with fac1=2\mathrm{fac}_1 = 2, fac2=π\mathrm{fac}_2 = \pi, fac3=43π\mathrm{fac}_3 = \tfrac{4}{3}\pi;
  • sphKernel_xi(kernel, dim) — packing ratio × scale, the support in spacing units.

These are properties of the kernel, not part of the evaluation: the lattice-normalization factor is deliberately not applied to them.

Pairwise evaluation and the support schemes​

The pairwise functions take both particles' supports (hi,hj)(h_i, h_j) and resolve the effective support with computePairwiseSupport (util/support.py):

SupportSchemevalueWijW_{ij}∇Wij\nabla W_{ij}
Gather11W(xij,hi)W(x_{ij}, h_i)∇W(xij,hi)\nabla W(x_{ij}, h_i)
Scatter12W(xij,hj)W(x_{ij}, h_j)∇W(xij,hj)\nabla W(x_{ij}, h_j)
MeanSymmetric13W(xij,(hi+hj)/2)W(x_{ij}, (h_i+h_j)/2)∇W(xij,(hi+hj)/2)\nabla W(x_{ij}, (h_i+h_j)/2)
KernelMeanSymmetric1412(W(xij,hi)+W(xij,hj))\tfrac12\big(W(x_{ij},h_i) + W(x_{ij},h_j)\big)12(∇W(xij,hi)+∇W(xij,hj))\tfrac12\big(\nabla W(x_{ij},h_i) + \nabla W(x_{ij},h_j)\big)
SuperSymmetric1512(W(xij,hi)+W(xij,hj))\tfrac12\big(W(x_{ij},h_i) + W(x_{ij},h_j)\big)12(∇W(xij,hi)−∇W(xji,hj))\tfrac12\big(\nabla W(x_{ij},h_i) - \nabla W(x_{ji},h_j)\big) — the CRK-SPH formulation
PartialSymmetric16(PESPH) fif_i-weighted hih_i + fjf_j-weighted hjh_j kernel combinationsame

KernelMeanSymmetricKernelMeanSymmetric and SuperSymmetricSuperSymmetric are provably identical for the kernel value and gradient at qq, but genuinely differ for the Naive kernel Laplacian (which is why sphKernelLaplacian keeps a two-branch dispatch and the Laplacian JVP mirrors it).

x_{ij} = x_i - x_j is always the periodic minimum-image distance (computeDistanceVec with the domain's periodicity); tangents use the plain difference dxi−dxjdx_i - dx_j (periodic-wrap discontinuity is out of scope for the JVPs, per warpier_adjoint.md Tier 2.1).

The building blocks​

FunctionComputesNotes
sphKernel(x_i, x_j, h_i, h_j, kernelProperties, domain)WijW_{ij}+ lattice factor
sphKernelGradient(...)∇iWij\nabla_i W_{ij}+ lattice factor
sphKernelHessian(...)∇2Wij\nabla^2 W_{ij}ε\varepsilon-regularized; the q<εq<\varepsilon branch gives the finite peak curvature
sphKernelDkDh(...)∂Wij/∂h\partial W_{ij}/\partial h=−Cdh−(d+2)(d h k+r k′)= -C_d h^{-(d+2)}\big(d\,h\,k + r\,k'\big)
sphGradientDkDh(...)∂∇Wij/∂h\partial\nabla W_{ij}/\partial h=−Cdh−(d+2)(q k′′+(d+1)k′) x^= -C_d h^{-(d+2)}\big(q\,k'' + (d+1)k'\big)\,\hat x
sphKernelLaplacian(...)the analytic kernel Laplacian (see below)

Every function above is multiplied by the lattice-normalization factor resolveNormalization(kernelState) — 1.0 unless the calibration is enabled, in which case 1/L1/L. The scaling is applied at the public boundary of each function (not at the 13 internal eval_C_d sites) because everything is linear in CdC_d, so one scale cannot be forgotten for a derivative.

The naive kernel Laplacian​

sphKernelLaplacian_ evaluates

∇2W=s k2+t k1,k1=Cdh−(d+1)k′(q),    k2=Cdh−(d+2)k′′(q),\nabla^2 W = s\,k_2 + t\,k_1, \quad k_1 = C_d h^{-(d+1)} k'(q),\;\; k_2 = C_d h^{-(d+2)} k''(q), s=r2rε2,t=−r2rε3+drε,rε=r+εh,s = \frac{r^2}{r_\varepsilon^2},\qquad t = -\frac{r^2}{r_\varepsilon^3} + \frac{d}{r_\varepsilon}, \qquad r_\varepsilon = r + \varepsilon h,

returning 00 for q<εq < \varepsilon or q>1q > 1 — the estimator the Laplacian Naive scheme consumes. Its position-derivative sphKernelLaplacianGradient_ is derived from the k1→k2→k3k_1 \to k_2 \to k_3 ladder (cross-checked against the ε→0\varepsilon\to0 closed form k3−d−1r2k1+d−1rk2k_3 - \frac{d-1}{r^2}k_1 + \frac{d-1}{r}k_2) and needs no self-pair branch: every term carries a factor of the zero-at-origin direction vector.

Custom adjoint of the kernel gradient​

adj_sphGradient_* replaces Warp's automatic reverse-mode adjoint of sphGradient_ with a closed form built on sphKernelHessian_ and sphGradientDkDh_. The automatic composition is correct for r>0r > 0 but wrong at the exact self-pair (r=0r=0): the ε\varepsilon-regularized Jacobian of vectorNormalize_warp blows up like O(1/ε)O(1/\varepsilon) and gets multiplied by k′(q=0)=0k'(q=0) = 0 — "huge × 0.0" silently collapses to zero instead of the true finite limit (a removable 0×∞0 \times \infty singularity). The closed form has an explicit near-origin branch with the right r→0r\to0 limit and is validated independently at r=0r=0 and r>0r>0 (see wp_densityHVP.py's docstring and kernel_sanity_native.py sections 6/8).

Kernel JVPs​

kernels/kernelJVP.py provides the forward-mode building blocks the operator JVPs are built from — each mirrors its primal function branch-for-branch on SupportScheme:

FunctionReturns
sphKernelJVP(x_i, x_j, h_i, h_j, dx_i, dx_j, dh_i, dh_j, ...)(Wij,dWij)(W_{ij}, dW_{ij})
sphKernelGradientJVP(...)(∇Wij,d∇Wij)(\nabla W_{ij}, d\nabla W_{ij}) — chain rule through HH (the Hessian) and ∂∇W/∂h\partial\nabla W/\partial h
sphKernelLaplacianJVP(...)(∇2Wij,d∇2Wij)(\nabla^2 W_{ij}, d\nabla^2 W_{ij}) — built from sphKernelLaplacianGradient_/sphKernelLaplacianDkDh_; keeps the primal's two-branch dispatch (SuperSymmetric explicit, everything else via computePairwiseSupport's max-fallback)

dWdW and friends are chain-rule through xij=xi−xjx_{ij} = x_i - x_j and hij=computePairwiseSupport(hi,hj,mode)h_{ij} = \mathrm{computePairwiseSupport}(h_i, h_j, \mathrm{mode}) (computePairwiseSupportJVP is an ordinary subgradient on the max⁡\max branch, exact away from the hi=hjh_i = h_j kink). All three apply the lattice factor to both primal and tangent.

Tests​

  • tests/operations/test_operations_core.py — kernel-driven operator smoke tests.
  • scripts/gradcheck/kernel_sanity_native.py (gated by tests/operations/test_gradcheck_scripts.py) — the derivative ladder, the custom adjoint at r=0r=0/r>0r>0, and the Laplacian derivatives against wp.Tape.
  • scripts/gradcheck/gradcheck_*.py — operator-level gradcheck at float64, which exercises every kernel × scheme combination.

See also​

Support schemes in the operator pages · CRK corrected kernel · Renormalization · Lattice calibration