#Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project follows Semantic Versioning.
Note: Pre-0.1.0 history (releases
0.0.1through0.0.7) lives atsachncs/bounded-commuting-discrete-trace-preserving-projections. The current canonical repository issachncs/traceprojector.
#[Unreleased]
#Added
examples/: ten runnable programs (quick start, your own mesh, the four form degrees, a singular field, commuting, boundary values, refinement, time-dependent data, worker threads, higher order), each with its recorded output;validation/examples.test.jsruns them.validateMeshInputis exported, to check a mesh without building anything, andparseGmshreads an ASCII Gmsh.mshfile (formats 2.2 and 4.1, first-order tetrahedra) into the two arrays the operators take, refusing anything else with aValidateError.The playground compares operators: a Web Worker runs
Projector,CanonicalProjector,BoundedCommutingProjector,StableProjectorandTracePreservingProjectoron smooth, singular, discontinuous and localized fields, on a cube, a pasted JSON mesh or an uploaded Gmsh.msh, shows what each one cannot answer, measures the commuting and boundary-value residuals with their cost, refines near the centre, and exports a program that reproduces the run (scripts/verify-lab-code.mjsruns every exported program).ZeroTraceProjector,BoundedCommutingProjectorandStableProjectorhavecount(l)andedge(index), so a caller can loop over every coefficient without reaching into the weights.validation/showcase.test.js: every public export must have an API reference entry and a recipe or a stated reason it needs none.TracePreservingProjector({degree: p})forp >= 1: the boundary operator above the lowest order (boundary-traces.js,high-order-boundary.js,high-order-trace-preserving.js), evaluated withlocal(u, du, l). Validated atp = 1(all four degrees) andp = 2(l = 0) on cubes: trace preserved to 10⁻¹³, projection to 5·10⁻¹³, commuting to 10⁻¹¹ for fields with a discrete trace. Its stability is not measured.CoefficientPool(traceprojector/parallel): coefficients of the bounded operators on any number of worker threads, bitwise identical to serial.npm run bench,bench:check,bench:update: solver and weight-pipeline benchmarks in calibration units, with a committed baseline.Solver certificates (residual, stationarity, duality gap, factor size) with limits fixed in
validation/tolerances.json.Documentation on the website, standalone: a getting-started guide, a guide to choosing an operator, performance, a developer guide, and every other page of
docs/, rendered from the repository's Markdown at https://sachncs.github.io/traceprojector/docs/ (no links out to GitHub). The code examples in the user guides are executed byvalidation/docs-examples.test.js.HighOrderWeights.extensionConstantandvalidation/assumption-2-4.test.js: Assumption 2.4 of Arnold and Guzmán (the uniform bound on the right inverse ofdon extended stars) is now measured, not only assumed: 0.002 to 0.36 across jitter, 100:1 stretch andp = 1, growing by 3% fromn = 4ton = 8.HigherOrderProjectoracceptsboundaryConditions: truefor the zero-trace version (weights vanish on the boundary entities); validated atp = 1near the boundary for all four form degrees.Paper claims W4 (partition of unity), W6 (norms of the boundary weights and their extensions) and W7 (discrete Poincaré constants) tested at lowest order; end-to-end commuting of the higher-order projection for degrees 0 to 3, and its stability for all four degrees (ratios 0.995 to 1.007).
banded-linear-algebra.js: reverse Cuthill–McKee ordering, skyline Cholesky and a minimum-norm solver by the method of multipliers.ZeroTraceProjectorandStableProjector(lowest order): locally L²-bounded projections built from the local weight functions of arXiv:2502.03601. Interior coefficients are integrals against the weights, so an input unbounded at an interior mesh vertex is projected stably. The boundary part still uses canonical degrees of freedom; seedocs/VALIDATION.mdfor what is and is not established.BoundedCommutingProjector(lowest order): the projection of arXiv:2502.03601 Thm 5.1 without boundary condition. Locally L²-bounded and commuting for every input, boundary included; does not preserve traces.HigherOrderProjector: the locally bounded commuting projection of Arnold and Guzmán for any degreepand every form degree, withHighOrderWeights(weights of any degree),AgSpace(their degrees of freedom) and the correctionQ_r. Commutes withdto 2.5e-13 and keeps itsL²constant (1.007) on narrow bumps atp = 1.refineTetrahedraandProjector.refine(conforming longest-edge bisection),Projector.sampler(point location cached across time steps) andprojectInParallel(Node worker threads).CanonicalProjector: canonical interpolation ontoP_r^- Λ^kof any degree for all form degrees (Lagrange, Nédélec, Raviart–Thomas, DG), with the polynomial form spaces and the reference-element degrees of freedom it is built from. Commutes withdand preserves the trace.TracePreservingProjector(lowest order): the full operatorΠ = mΠ(I − Π_∂) + Π_∂of the boundary-preserving paper: a projection onto the Whitney forms that preserves the discrete trace and commutes with the exterior derivative for fields with a lowest-order trace (4e-14 measured). Built from the boundary weights, the Worsey–Farin extension andZeroTraceProjector.docs/VALIDATION.mdand avalidation/suite with tolerances fixed before the first run (validation/tolerances.json).npm run verify: lint, tests, build, export parity, types parity (tsdand a JS-versus-declaration check),publint,attw, and a consumer test that installs the packed tarball into an empty directory.
#Changed
docs/api.mdis generated byscripts/gen-api.mjsfrom the declaration files (npm run docs, checked bynpm run docs:check). It lackedStableProjector,ZeroTraceProjector,BoundedCommutingProjector,CanonicalProjector,HigherOrderProjector,TracePreservingProjectorandCoefficientPool, and listed internal constants; it now lists exactly the public API.jsdoc-to-markdownis no longer a dependency.The local minimum-norm solves use preconditioned conjugate gradients on the dual problem with sparse Cholesky and a minimum-degree ordering; one higher-order weight at
p = 1is 3 to 7 times faster than before (cell: 5.7 s to 0.78 s).Dense typed-array polynomials, tabulated bubble bases and geometry-keyed caches in the higher-order weights.
The local problems of the higher-order weights are solved with a banded Cholesky factorization instead of a dense one. One minimum-norm solve of about 1,500 unknowns went from 1.9 s to 0.02 s; a whole weight of an interior entity at
p = 1on a 6 × 6 × 6 cube went from 3.1 s to 0.45 s (l = 1), 14.2 s to 2.0 s (l = 2) and 42.8 s to 5.7 s (l = 3). A zero-trace weight next to the boundary that exceeded an hour now takes seconds.Polynomial.evaluateuses precomputed powers.README rewritten;
docs/setup.md,development.md,testing.mdandpublishing.mdmerged into the developer guide;architecture.md,math.md,exceptions.mdandCONTRIBUTING.mdbrought up to date.Code style follows the Google JavaScript Style Guide (ESLint with
eslint-config-google) instead of StandardJS. The reformat is mechanical: the unit tests and the bit-exact determinism output are unchanged.The package is ESM-only (
sideEffects: false). The CommonJS bundle was never reachable through theexportsmap and is no longer built; on Node 22.12 and laterrequire('traceprojector')loads the ESM entry.The README and package description no longer claim that
Projectoris bounded: it uses canonical degrees of freedom, and the paper's boundary weights only cross-check them.CI runs on Node 24 and 26, audits production dependencies, and builds the site; the
web:buildscript reference is nowsite:build.
#Fixed
The playground's exported program imported
generateUnitCubeMeshfromtraceprojector, where it is not exported, and passed a placeholder cell index; it now runs as written. Two accessibility issues in the site footer (a skipped heading level and an aria-label that did not contain its visible text) are fixed.The operators that take vertices and tetrahedra (
ZeroTraceProjector,BoundedCommutingProjector,StableProjector,CanonicalProjector,HigherOrderProjector,TracePreservingProjector) accepted NaN or infinite coordinates, repeated vertices and flat tetrahedra silently, and raised a bareTypeErrorfor a vertex that does not exist. They now check the mesh on construction and raiseValidateError, asMeshdoes and as the getting-started guide says. Behaviour change: a mesh that used to be accepted by accident and is broken in one of these ways now fails at construction. Orientation is not checked by these operators, because they sort the vertices of each cell and give the same answer, to roundoff, for either orientation (validation/orientation.test.js), so meshes from Gmsh or TetGen with mixed orientation keep working.MeshandProjector, which use the sign, still require positive orientation.minimumNormSolveDensereturned a non-minimal solution (up to 80% more energy) when the constraint matrix had a null space; it now always solves the dual system. Regression test added.The deployed website loaded its assets from the host root and showed an unstyled page on GitHub Pages; it is now built with the
/traceprojectorbase path.The README's quick-start example printed a wrong value (it claimed about 1.365; the interpolant is 0.6937) and imported
generateUnitCubeMeshfrom a path that does not export it.Projections returned
InfinityorNaNsilently when the input field was not finite at a degree of freedom; they now throwProjectError.Geometry depended on the unit system. Meshes with edges below about 0.1 mm (in metres) were rejected as degenerate, point location failed on them, face normals were silently replaced by
[0, 0, 1], and small 3x3 systems were declared singular. Degeneracy is now judged by the volume relative to the longest edge cubed (MIN_RELATIVE_VOLUME, 1e-10), and a degenerate surface triangle raises an error instead of inventing a normal.
#[0.1.0] — 2026-09-03
The first release under the traceprojector name on Node 26. Adds
the new web/ playground, drops Babel, and finishes the file-layout
rebrand.
#Added
- New
web/workspace. A Next.js 16 + shadcn/ui playground that consumes the lib via npm workspaces, with a 3D tetrahedral-mesh viewer (three.js / @react-three/fiber), an API playground with copy-to-clipboard code export, and a live convergence plot (Recharts). Start it withnpm run web:dev; build it withnpm run web:build. A newwebjob in.github/workflows/ci.ymlbuilds the playground on every push. - New "Try it online" entry in the root
README.mdthat points at the bundled web playground, alongside a convexfolio-style What is this? / Who is this for? / What can it do? / Before you start / Installation / Your first run / Configuration / Where to go next skeleton. The original technical depth (API, math, architecture, performance, tech stack) is kept as a second half. Projector.verifyBoundaryWeights(Section 6.3 cross-check): the exact boundary DoFs stayu(v),∫_e u·t,∫_f u·nfor any input, and each wired §6.3 weight functional is applied to its boundary simplex's canonical discrete trace basis field (P¹ hatλ_v, Whitney 1-formW_e, faceRT_0) to confirm it recovers the normalized DoF — seetraceprojector/boundaryVerify.jsandtests/weight.test.js.Weightvertex-weight assembly fix after an Alfeld/Worsey-Farin split: the mesh appends barycenter vertices, sobweight.vertexWeightnow receives a compact, remapped local vertex set for each boundary star. The previous full-vertex pass produced a singular stiffness matrix after the split and silently skipped every vertex weight.- Edge boundary weight
ζ_{0,e}^1(Section 6.3.2) on the lowest-order surfaceN_0(Whitney 1-form) space: build the star's edge indexing, assemble the Whitney mass matrix, solve forη_e^1 = M^{-1}dfrom the intrinsic edge momentsd_k = ∫_e W_k·t_e, and expose the duality functional(ζ, u) = (η_e^1, u)reproducing the edge degree of freedom for H(curl) traces inN_0(eq. 6.31). - Face boundary weight
ζ_{0,f}^2(Section 6.3.3) on the lowest-order surfaceRT_0(Raviart-Thomas) space: assemble the RT_0 mass matrix over the extended star, solve forη_f^2 = M^{-1}dfrom the featured-face normal momentsd_k = ∫_f RT_k·n, and expose the duality functional(ζ, u) = (η_f^2, u)reproducing the face degree of freedom for H(div) traces inRT_0(eq. 6.36). - Section 2.3 surface differential operators (
grad_Γ,curl_Γ,div_Γ,rot_Γ) and the Section 6.3 barycenter tentμon Alfeld-split faces, in the newtraceprojector/surface.jsmodule. - Section 6.3 boundary-weight cascade wired into
Weight.compute():vertexBoundaryWeights,edgeBoundaryWeights, andfaceBoundaryWeights(built frombweight.vertexWeight/edgeWeight/faceWeightover each boundary vertex's star, edge's star, and face's extended star), stored onProjectorviacomputeBoundaryWeights. - README sections: Tech stack, Security, Code of Conduct, centered header with aligned badges, and license copyright.
engines.node: ">=26.0.0"inpackage.jsonand aworkspaces: ["web"]field. New convenience scriptsweb:dev,web:build, andweb:startproxy into the workspace..gitignoreentries for the web workspace:.next/,out/,next-env.d.ts,.turbo/.
#Changed
- README is rewritten in the convexfolio skeleton (with the
technical depth kept as a second half) and the GitHub URL is
updated to
sachncs/traceprojectorin every badge. - CONTRIBUTING and SECURITY are updated to use the public class
name
Projectorand the package nametraceprojector(no moreTraceProjectororBcdtpp). - Docs (
docs/architecture.md,docs/development.md,docs/exceptions.md,docs/math.md,docs/setup.md,docs/testing.md) are updated to reflect the new source path, the new error class names, the new requirement of Node 26, the new test files, and the newweb/workflow. docs/api.mdis regenerated vianpm run docsto include thebweightandsurfaceexports and the Section 6.3 boundary-weight cascade.- Bump dev-dependency group to current latest:
@rollup/plugin-node-resolve^16.0.1→^16.0.3jsdoc-to-markdown^9.1.1→^9.1.3mocha^11.8.0→^12.0.0- The rest of the dev deps (
@rollup/plugin-commonjs,@rollup/plugin-terser,rollup,c8,chai,sinon,standard) were already at latest.
- Bump GitHub Actions:
actions/checkoutandactions/setup-nodetov8. - Tighten the StandardJS
ignorelist to also skipweb/(the playground is type-checked withtscand linted withnext lintrather than StandardJS). - Section 6.3 tent
μfix (line 1559 / eq. 6.21):μis1at each face barycenter and0on every face boundary (edges / vertices), piecewise affine on the Alfeld-split sub-triangles — the globally-continuous paper definition — instead of the reverse vertex-valued tent. - Use the paper's
μ_σ = χ_{es_∂(σ)} μ(barycenter tent on the boundary-extended star) in the vertex boundary weightζ_{0,v}^0, replacing the vertex tent; the dual property(ζ, v^0, u) = u(v)(eq. 6.25) is preserved. - Enforce the mean-zero constraint in
Solverwith an exact Lagrange-multiplier (bordered) solve instead of row replacement, so every stiffness row is honored. - Use a single mesh-orientation normal flux for all H(div) faces with Whitney-basis sign alignment, giving a continuous normal trace across interior faces.
- Fall back to the cell mean (never a silent zero) when a higher-order L² mass matrix solve is singular.
- Evaluate the H¹ boundary DoF exactly at the vertex
u(v)(eq. 6.25) instead of approximating the weighted surface integral. - Use the exact edge DoF
∫_e u·t dson every H(curl) edge, interior included, instead of midpoint tangential sampling. - Source files renamed to single-word names:
math_utils→utils,local_solver→solver,boundary_weight_computer→weight,higher_order_projection→bubble,mesh_refinement→refinement,mesh_generator→generator,point_locator→locator,convergence_harness→harness,projectors/*_projector→projectors/h1|hcurl|hdiv|l2. - Classes renamed to single words:
TraceProjector→Projector,H1Projector→H1,HcurlProjector→Hcurl,HdivProjector→Hdiv,L2Projector→L2,BoundaryWeightComputer→Weight,LocalSolver→Solver,HigherOrderProjection→Bubble,MeshRefinement→Refinement,PointLocator→Locator,MeshValidationError→ValidateError,ProjectionError→ProjectError,SingularMatrixError→SingularError. - Plain public member names for every field and method; no
#privatemarkers and no leading-underscore semi-private prefixes. - Package export subpaths renamed (
./utils,./locator) and thePointLocator/buildPointLocatorAPI toLocator/buildLocator. - Fix case-sensitivity bug in tests importing
traceProjector.js(nowtraceprojector.js). - Restructure README header with centered layout and aligned badges.
- Rename package from
bcdtpptotraceprojector(the public class wasBcdtppat the time, later renamed toTraceProjector, and finally toProjector). - Remove the obsolete
Weight.computeVertexWeightsSolver /nodeMaproute and the now-deadvertexBoundaryData/Projector.vertexData(the H¹ boundary DoF is exactu(v)). Drop the unusedmeshRefinementconstructor argument fromWeight, and drop the now-deadWeight fault isolationcoverage tests.
#Changed (BREAKING)
- Source tree moved from
src/lib/totraceprojector/. Thesrc/wrapper is gone — the package now ships with the source living in a top-leveltraceprojector/directory, so the package name and the directory name match exactly and the layout is obvious from the repo root.package.json(main,module,unpkg,jsdelivr, everyexports.*,types,files), thedocsscript glob, the rollupinput, thearchitecture.mdmodule map, themath.mdcross-references, thesetup.md/testing.md/development.mdexamples, the README, and everytests/*.test.jsimport are updated. The internal layout of the package is preserved, so relative imports inside the package still resolve. - Babel is removed from the build pipeline.
@babel/core,@babel/preset-env,@rollup/plugin-babel, andbabel.config.jsonare deleted: the project is pure ESM and only ever runs on Node 26+, so the Babel pass was a no-op. - Pin to Node 26. The CI matrix is reduced from
[20, 22]to a single Node 26 job, and thepublish.ymlworkflow is updated accordingly.docs/setup.mdrequirements are bumped to Node 26, npm 11. A newwebCI job compiles the Next.js playground. - Repository URL is updated to
https://github.com/sachncs/traceprojectorinpackage.json(homepage,repository.url,bugs.url), every README badge, the CI workflow URLs (implicit), and thedocs/setup.mdinstall instructions. The historical commit links in the changelog still point at the originalbounded-commuting-discrete-trace-preserving-projectionsrepo because they reference real past commits. - Error class names in docs:
MeshValidationError→ValidateError,ProjectionError→ProjectError,SingularMatrixError→SingularError(the class names in source were already renamed earlier in this release; this just brings the docs in line).
#0.0.7 — 2026-06-20
- Add comprehensive test suite: interior projector, convergence harness, local solver, mesh generator, point locator, Whitney basis, quadrature, coverage edge cases
- Add convergence test framework (h-refinement and p-refinement on all four form degrees)
- Add mesh generator for cube subdivision
- Add PointLocator AABB tree for O(log N) point-in-tet queries
- Add LocalSolver with surface stiffness assembly and constrained solve
- Add BoundaryWeightComputer for trace-preserving projection weights
- Implement Worsey-Farin split (§6.1.4) with idempotency
- Implement Alfeld split (§6.1.3) with idempotency
- Fix UMD global name to TraceProjector in rollup config
- Fix linting issues across all source files
#0.0.6 — 2026-05-12
- Add Mesh class with topology (faces, edges, boundary), geometry (volume, normals), and adjacency
- Add Whitney class for barycentric coordinates and Whitney finite-element basis functions
- Add math utilities: LU solver, 3x3 inverse, numerical gradient, vector operations
- Add higher-order projection framework (§7) with bubble corrections
- Add L2 projector (l=3) for piecewise constants
- Add MeshRefinement class for Alfeld/Worsey-Farin splits
- Fix orientation sign computation for edges and faces
- Fix tetDeterminant (scalar triple product) and add tetVolume
- Refactor projectors into separate files under
src/lib/projectors/
#0.0.5 — 2026-05-12
- Add H(curl) projector (l=1) for Nédélec first-kind (Whitney 1-form) space
- Add H(div) projector (l=2) for Raviart-Thomas (Whitney 2-form) space
- Implement interior projector Π_ring^l and discrete extension operator E^l
- Implement boundary correction part Π_partial^l
- Implement global projector decomposition: Π^l = Π_partial^l + Π_ring^l (I − Π_partial^l)
- Add computeBoundaryWeights for trace-preserving projections
#0.0.4 — 2026-05-05
- Add H1 projector (l=0) for continuous piecewise-linear space
- Implement boundary-aware projection with vertex weights
- Add projectAtPoint convenience method with AABB point locator
- Add extractBoundaryDofs for boundary degree-of-freedom extraction
#0.0.3 — 2026-05-05
- Add TraceProjector main class with project method dispatching to form degree l
- Add basic error classes: MeshValidationError, ProjectionError, SingularMatrixError
- Implement first version of H1 projection
- Add rollup UMD bundle configuration
#0.0.2 — 2026-05-04
- Add Mesh class with tetrahedral mesh construction and validation
- Add Whitney class with barycentric coordinates and edge/face basis functions
- Add math utilities: dot, cross, norm, subtract, matrix operations
- Add Gaussian quadrature for triangles, tetrahedra, and lines
- Add Mocha/Chai test infrastructure
#0.0.1 — 2026-05-03
- Initial project setup with npm, StandardJS, Mocha/Chai, c8 coverage
- Add README, CONTRIBUTING, LICENSE, and project configuration
- Add GitHub Actions CI workflow
- Add documentation: architecture, math background, setup, testing, publishing
#Dependency Updates
#2026-07-07
- Merge pull request #1 — Bump actions/checkout from 4 to 7
- Merge pull request #2 — Bump actions/setup-node from 4 to 6
- Merge pull request #3 — Bump dev-dependencies group (4 updates)
- Merge pull request #4 — Bump chai from 5.3.3 to 6.2.2
- Merge pull request #5 — Bump c8 from 10.1.3 to 11.0.0
- Merge pull request #6 — Bump @babel/preset-env from 7.29.5 to 8.0.2
- Merge pull request #7 — Bump @babel/core from 7.29.0 to 8.0.1