Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TranscriptionSolvers.jl

version

A pluggable grid-transcription toolkit in Julia. Turns differentiation and integration into matrix operations — differentiation matrices D/D2 and quadrature weights ws on a grid of nodes taus — so that a continuous problem (a BVP, a calculus-of-variations problem) can be discretized into a linear system, a nonlinear system, or an NLP. Built around a single parametric type, TranscriptionSolver{R,N,SK}, with four pluggable transcription kinds. Defines no problem types of its own — see VariationalProblems.jl for a consumer.

Features

  • Four transcription kinds (solver_kind symbol): :FiniteDiff (uniform grid, 2nd/4th-order central differences + composite-trapezoidal quadrature), :Chebyshev (Clenshaw-Curtis pseudospectral), :LGL (Legendre-Gauss-Lobatto pseudospectral), :LGR (Legendre-Gauss-Radau pseudospectral)
  • Domain-agnostic operators: nodes/D/D2/ws are built once on the reference domain [-1,1]; rescale to any physical [x0,xf] via 3-arg accessor methods, no reconstruction needed
  • Runtime factory: new_transcription_solver(Nx; solver_kind, kwargs...) resolves solver_kind to its concrete type by naming convention — adding a new kind never touches the factory
  • Pseudospectral kinds (:Chebyshev, :LGL, :LGR) are exact for polynomials up to degree Nx-1

Installation

Requires Julia 1.12.6. Direct dependencies: LegendreTools, StaticArrays, SparseArrays, PrecompileTools (see Dependencies).

using Pkg
Pkg.add(url="https://github.com/raholanda/TranscriptionSolvers.jl")

Quick Start

Build a solver

using TranscriptionSolvers

Nx = 8
solver = new_transcription_solver(Nx; solver_kind=:Chebyshev)
# also available: :FiniteDiff (kwarg order=2 or 4, default 4), :LGL, :LGR

Reference-domain operators ([-1,1])

using LinearAlgebra

taus = grid_points(solver)                 # SVector{Nx}, nodes on [-1,1]
D, D2 = differential_matrix(solver)        # SparseMatrixCSC, N×N
ws = quadrature_weights(solver)            # Vector, length N

y = taus .^ 2
D * y       2taus            # first derivative
D2 * y      fill(2.0, Nx)    # second derivative (exact: pseudospectral, degree 2 ≤ Nx-1)
dot(ws, y)                    # ∫_{-1}^{1} τ² dτ

Physical-domain operators

x0, xf = 0.0, 10.0

xs = grid_points(solver, x0, xf)                       # nodes rescaled onto [x0,xf]
D_phys, D2_phys = differential_matrix(solver, x0, xf)   # scaled by 2/Tspan, 4/Tspan^2
ws_phys = quadrature_weights(solver, x0, xf)            # scaled by Tspan/2

One-shot transcription

ys, yps, ypps = transcribe(sin, x0, xf, solver)   # y, y', y'' at the physical-domain nodes

Examples

Three scripts under examples/ explore D/D2 accuracy for progressively harder test functions. Plotting uses CairoMakie, kept out of the main package's dependencies via a separate examples/Project.toml:

  • derivative_comparison.jl — analytical vs. numerical D/D2 across all four kinds on smooth test functions (polynomial, sin, exp, Runge), with a tiled overlay plot and a convergence-with-Nx study showing pseudospectral (spectral) vs. :FiniteDiff (algebraic) error decay.
  • wavenumber_resolution.jl — differentiation accuracy vs. wavenumber for f_k(x) = cos(kπx/(2N)), k = 0:N-1, N = Nx: sweeps from fully-resolved down to the grid's own Nyquist-like resolution limit.
  • nonsmooth_functions.jl — differentiation accuracy as smoothness degrades: a kink (x|x|), a C⁰ kink (|x|), and a genuine jump (sign(x), visualized via the Gibbs phenomenon in D*y rather than an error table, since no classical derivative exists there).
# one-time setup
using Pkg
Pkg.activate("examples")
Pkg.develop(path=".")
Pkg.add("CairoMakie")
julia --project=examples examples/derivative_comparison.jl

Running the validation suite

bash scripts/validate.sh

Expected output:

=== TranscriptionSolvers: precompile ===
OK
=== TranscriptionSolvers: tests ===
Test Summary: | Pass  Total  Time
FiniteDiff    |   36     36  1.0s
Test Summary: | Pass  Total  Time
Chebyshev     |   32     32  0.1s
Test Summary: | Pass  Total  Time
LGL           |   32     32  1.4s
Test Summary: | Pass  Total  Time
LGR           |   33     33  0.1s

scripts/validate.sh skips the JET lint pass (see Commands); run julia --project=. -e 'using Pkg; Pkg.test()' for the full suite including JET.

Element Set — Solver Kinds

Kind Method D/D2 from Notes
:FiniteDiff Uniform grid Central differences (order=2|4) Ascending taus; composite-trapezoidal ws
:Chebyshev Clenshaw-Curtis pseudospectral Lagrange-polynomial differentiation Descending taus (native, not canonicalized)
:LGL Legendre-Gauss-Lobatto pseudospectral Legendre-polynomial differentiation Ascending taus
:LGR Legendre-Gauss-Radau pseudospectral Legendre-polynomial differentiation Ascending taus; fixed at -1 only (taus[end] < 1)

solver_kind is resolved to its tag type (:FiniteDiffFiniteDiffKind) by naming convention, not a hardcoded dispatch table — see Module Structure.

Module Structure

TranscriptionSolvers
├── core.jl                 — TranscriptionSolver{R,N,SK} struct, accessors, new_transcription_solver factory
├── FiniteDiff.jl            — FiniteDiffKind, central-difference D/D2, trapezoidal ws
├── Chebyshev.jl              — ChebyshevKind, Clenshaw-Curtis nodes/quadrature
├── LGL.jl                    — LGLKind, Legendre-Gauss-Lobatto nodes/quadrature
├── LGR.jl                    — LGRKind, Legendre-Gauss-Radau nodes/quadrature
└── precompile.jl              — @compile_workload warmup for all kinds

Each kind lives in its own file/module and exports its ...Kind tag type plus a new_solver(::Type{<:SolverKind}, Nx; kwargs...) constructor. core.jl never imports the kind modules or hardcodes an if/elseif over them — to add a kind, add one new file/module; core.jl itself never changes. Only the tag types are re-exported at the top level (TranscriptionSolvers.jl), so every kind module can reuse the identical internal constructor name new_solver without collision.

Dependencies

Known Limitations

  • :Chebyshev's native node ordering is descending, unlike :FiniteDiff/:LGL's ascending order; this is not canonicalized to a uniform convention across kinds.

License

MIT © 2026 raholanda

Preferred citation:

raholanda (2026). TranscriptionSolvers.jl (v1.0.0-DEV). GitHub. https://github.com/raholanda/TranscriptionSolvers.jl

Contact

Rodrigo Holanda Role: maintainer / developer Email: rodrigo.holanda.21@gmail.com

References

  • Huntington, Geoffrey & Rao, Anil. (2008). Comparison of Global and Local Collocation Methods for Optimal Control. Journal of Guidance, Control, and Dynamics, 31, 432-436. doi:10.2514/1.30915
  • Trefethen, Lloyd N. (2000). Spectral Methods in MATLAB. SIAM. doi:10.1137/1.9780898719598
  • Davis, P. J., & Rabinowitz, P. (1984). Methods of Numerical Integration (2nd ed.). Academic Press.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages