Skip to content

Coordinate systems (symbolic) ​

<!– crosslinks: written by .github/scripts/crosslinks.py from docs/crosslinks.toml; do not edit –>

<!– /crosslinks –>

Differential operators on tensor fields, with exact symbolic derivatives. The mathematics — natural basis, Lamé coefficients, Christoffel symbols, the index placement of each operator — is on Curvilinear differential calculus.

Predefined systems ​

ConstructorCoordinatesLamé
coorsys_cartesian or
coorsys_polar
coorsys_cylindrical
coorsys_spherical  
coorsys_spheroidalprolate spheroidal

Each curvilinear system is drawn, with its normalized basis and its surfaces of constant coordinate, in an interactive figure of Predefined systems.

Load SymPyPythonCall for the default coordinates

Called without coordinates (coorsys_spherical()), a constructor names its coordinates with SymPy symbols, which needs using TensND, SymPyPythonCall. With Symbolics, pass the variables, coorsys_spherical(Tuple(@variables θ ϕ r)), or the type, coorsys_spherical(Val(Num)).

Spherical coordinates are ordered (\theta,\varphi,r)

Not , so that    reproduces the canonical basis in the canonical order.

The operators ​

julia
using TensND, SymPyPythonCall

Polar = coorsys_polar()
r, θ = get_coords(Polar)
𝐞ʳ, 𝐞ᶿ = unitvec(Polar)
@set_coorsys Polar

laplacian(SymFunction("f", real = true)(r, θ))

  

@set_coorsys installs a default system so the operators take one argument; without it, pass the system explicitly, laplacian(f, Polar).

CallDoes
@set_coorsysCS / set_coorsys!(CS)make CS the default
default_coorsys()the current default (throws if none)
unset_coorsys!()forget it

∂ is deliberately not affected by the default

∂(t, x) always means the plain derivative of t with respect to the symbol x; for the covariant derivative pass the chart explicitly, ∂(t, x, CS).

Earlier versions had @set_coorsys define methods in the TensND module, including one for ∂(t, x). That method was more specific than the plain-derivative fallback, so the same call silently changed meaning — and since both CoordinateSystem and Submanifold differentiate a position vector while being built, constructing any new chart after a @set_coorsys failed. The default is now simply stored, and the single-argument operator methods are defined once.

OperatorOrderMeaning
gradgradient, derivative index appended on the right
symgradsymmetrized gradient; for a displacement, the strain
divergencedivergence, contracting the last index
laplacian 
hess 
julia
n = symbols("n", integer = true)
simplify(laplacian(r^n * cos(n * θ)))

Accessors ​

FunctionReturns
get_coordsthe coordinate symbols
position_vectorthe position vector
unitvecvectors of the normalized basis
natvecvectors of the natural basis (:cov or :cont)
normalized_basis, natural_basisthe two bases
lame_coefficientsthe
christoffelthe array  
∂the covariant derivative along one coordinate
julia
using TensND, SymPyPythonCall
Spherical = coorsys_spherical()
lame_coefficients(Spherical)
(r, r*sin(θ), 1)
julia
christoffel(Spherical)[1, 1, 3]     # Γʳ_θθ = −r

Custom systems ​

julia
CoordinateSystem(OM, coords, tmp_coords = (), params = ();
              rules = Dict(), tmp_var = Dict(), to_coords = Dict())

The position vector and the coordinate symbols suffice; the natural basis, Lamé coefficients and Christoffel symbols are derived. A variant takes the normalized basis and the directly, when they are known in closed form.

julia
using TensND, SymPyPythonCall

x, y = symbols("x y", real = true)
a = symbols("a", positive = true)
Parabolic = CoordinateSystem(Tens([(x^2 - y^2) / 2, x * y]), (x, y))
lame_coefficients(Parabolic)
(sqrt(x^2 + y^2), sqrt(x^2 + y^2))

Taming symbolic expressions ​

Non-trivial charts produce metric expressions SymPy will not reduce unaided, and the Christoffel symbols become unusable nested radicals. Four optional arguments control this:

ArgumentRole
tmp_coordsauxiliary symbols standing for compound expressions
paramsconstants appearing in
tmp_varsubstitutions replacing expressions by those symbols
to_coordshow to eliminate them again before differentiating
rulesrewrite rules applied after each simplification

The prolate spheroidal system is the standard illustration, and coorsys_spheroidal is built exactly this way:

julia
Spheroidal = coorsys_spheroidal()
lame_coefficients(Spheroidal)
(c*sqrt(1 - p^2)*sqrt(q^2 - 1), c*sqrt(-p^2 + q^2)/sqrt(1 - p^2), c*sqrt(-p^2 + q^2)/sqrt(q^2 - 1))
julia
simplify(laplacian(position_vector(Spheroidal)[1]^2, Spheroidal))

rules is also what resolves sign ambiguities such as , which SymPy cannot reduce without knowing that   — see Surfaces: fundamental forms and curvature.

Symbolic or numerical? ​

For pointwise evaluation, sweeps and gradients, use the pointwise CoordinateSystem instead — same operator names, derivatives by automatic differentiation. See Numerical coordinate systems.