Core concepts¶
This page is the mental model behind PaNTr. Read it once and the rest of the library — the API reference, the tutorials, and the topic guides — falls into place. Everything here is illustrated end-to-end in Your first B-spline.
Spaces vs. geometry¶
PaNTr keeps a strict separation between a function space (a basis) and a geometry (a map built from that basis). This is the same split that underpins isogeometric analysis [Hughes et al., 2005], and it is worth internalizing:
Object |
What it is |
Holds |
|---|---|---|
a 1-D spline space |
one knot vector + a degree |
|
a tensor-product space |
a tuple of 1-D spaces |
|
a geometry |
a space + control points |
A space defines which functions exist (the basis) and the parametric dimension; it holds no shape. A geometry pairs that basis with coefficients (control points) to give an actual curve, surface, or volume:
import numpy as np
from pantr.bspline import Bspline, BsplineSpace, BsplineSpace1D
# A quadratic 1-D space on the knot vector [0,0,0,1,2,3,3,3]
line_space = BsplineSpace1D([0, 0, 0, 1, 2, 3, 3, 3], 2)
# A 1-direction tensor-product space (a curve space)
space = BsplineSpace([line_space])
# Geometry = space + control points
control_points = np.array([[0.0, 0.0], [1.0, 2.0], [2.0, -1.0], [3.0, 1.5], [4.0, 0.0]])
curve = Bspline(space, control_points)
The same space can carry many different geometries (just swap the control points), and the same control points mean different things in different spaces. Keeping the two apart is what makes refinement, change of basis, and assembly clean.
Parametric dimension (dim) vs. embedding rank (rank)¶
Two independent integers describe every geometry:
dim— the number of parametric directions, i.e. how many 1-D spaces the tensor-product space has.dim == 1is a curve,2a surface,3a volume.rank— the dimension of the values the map produces (its codomain).
They are orthogonal — a surface (dim == 2) can live in the plane (rank == 2) or
in space (rank == 3), and a rank == 1 geometry is a scalar field over its
parametric domain:
|
|
Meaning |
|---|---|---|
1 |
2 or 3 |
planar / spatial curve |
2 |
2 or 3 |
planar / spatial surface |
3 |
3 |
volume |
1, 2, 3 |
1 |
a scalar field |
Control points¶
The control points are the coefficients of the geometry in its space’s basis. They are
stored as a single array of shape (*num_basis, rank): one coefficient vector per
basis function, laid out on the tensor-product grid of basis functions
(num_basis gives the per-direction counts). The
curve does not pass through its control points; it lies in their convex hull and is
pulled toward them — the control net.
control_points returns the array (read-only). The natural
parameter value attached to each control point is its Greville abscissa
(get_greville_abscissae()), which is where interpolation places its
data by default (see Approximation: interpolation, fitting, projection, quasi-interpolation).
Rational geometry (NURBS)¶
Polynomials cannot represent a circle or any other conic exactly; rational polynomials can. A rational B-spline (a NURBS) attaches a positive weight to each control point and evaluates the weighted average. Piegl and Tiller [1997] and de Boor [2001] are the standard references for the underlying spline theory.
PaNTr stores NURBS in the standard homogeneous (projective) representation: the
control-point array carries one extra trailing coordinate, the weight w, and the
spatial coordinates are pre-multiplied by it ([w·x, w·y, …, w]). Evaluation maps the
homogeneous B-spline and divides by the weight, so evaluate()
returns ordinary Euclidean points. Consequences worth remembering:
is_rationalisTrue; the storedcontrol_pointsinclude the weight column (shape(*num_basis, rank + 1)), butrankexcludes it (i.e.rankis the geometric dimension without the weight).create_circle()(and the conic-based CAD operations) produce exact rational quadratics — see Your first B-spline and Constructive Solid Geometry.
The parametric domain¶
A geometry is a map from a box-shaped parametric domain into rank-space. The
domain of each direction is [first knot, last knot];
domain returns the per-axis bounds, and
pantr.geometry.AABB is the axis-aligned box primitive PaNTr uses for both
parametric domains and grid-cell bounds.
evaluate() accepts either a plain (npts, dim) array of
parameters or a PointsLattice (a tensor product of per-axis
parameter vectors), which is the efficient way to sample on a grid.
Continuity comes from knot multiplicity¶
Inside a knot span a B-spline is a polynomial of the given degree; across an interior
knot of multiplicity m it is C^{p-m} continuous. Repeating a knot lowers
smoothness; a knot of multiplicity p+1 (as at the clamped ends of an open knot
vector) breaks the geometry into independent pieces. This is the lever behind knot
insertion, Bézier extraction, and adaptive refinement — all covered in
Spaces, knots & element extraction.
Bézier and THB-splines¶
Two specializations reuse the same model:
A
Bezieris a single polynomial patch in Bernstein form — thedim/rank/control-point vocabulary is identical, but there are no interior knots (one element). Every element of a B-spline is a Bézier patch, recovered withto_beziers().A
THBSplineSpaceis a hierarchy of nested tensor-product spaces for adaptive local refinement; its API mirrorsBsplineSpace(see THB-splines: adaptive local refinement).
Library shape¶
The serial core (pantr.bspline, pantr.bezier, pantr.basis,
pantr.cad, pantr.grid, pantr.quad, …) depends only on NumPy, SciPy,
and Numba. Two further modules ship with every install but are dependency-gated —
they need a third-party backend and activate automatically once it is importable (until
then, using them raises a clear error). They are never imported by the core:
pantr.viz— PyVista/VTK rendering and export; needspyvista(Visualization).pantr.mpi— MPI-distributed spaces; needsmpi4py(Distributed spaces).
The viz / mpi / metis install extras are just a convenience that pulls those
backends in; installing the backend directly (e.g. pip install pyvista) is equivalent.
Geometric predicates that need a floating-point tolerance (knot-multiplicity tests,
endpoint detection) draw it from pantr.tolerance, so the whole library shares one
consistent notion of “equal”.
Continue with Tutorials to build something, or Spaces, knots & element extraction for knot vectors, refinement, and element extraction.