Coordinate Systems: General Info#
PyMetric provides a flexible and extensible framework for defining and working with coordinate systems, especially curvilinear coordinate systems used in scientific and engineering applications. The coordinate systems are built on top of a symbolic foundation, allowing for advanced operations in both symbolic and numerical form.
Coordinate systems in PyMetric are defined in the coordinates
module and all
inherit from a common base, ensuring a consistent interface across systems. Each coordinate system represents a
curvilinear coordinate space (e.g., spherical, cylindrical, prolate spheroidal) and includes full support for:
Coordinate transformations
Metric and inverse metric tensor evaluations,
Symbolic and numeric differential operators (e.g., gradient, divergence, Laplacian),
Custom parameters and extensibility via subclassing.
These systems can be used in both analytical and simulation contexts, making them ideal for finite-difference, spectral, or tensor calculus applications in custom geometries.
Note
The underlying design goal of PyMetric is to ensure that
Coordinate systems are easy to use.
Coordinate systems are easily extensible to allow custom geometries.
Coordinate systems are accurate in computation.
In many cases, we have pursued as efficient an implementation as possible; however, efficiency is not the highest priority in this module. Therefore, coordinate systems and their differential operations are not suitable for use in (for example) high resolution time dependent PDEs, where calls to differential geometry functions would occur many 1000’s of times. Instead, PyMetric is ideal for instances where a PDE needs to be solved on the order of 1 time in order to perform a necessary task.
Overview#
Coordinate systems in PyMetric represent curvilinear geometries such as spherical, cylindrical, and prolate spheroidal spaces. These coordinate systems provide a symbolic and numerical interface to geometric quantities and operations, including:
Coordinate transformations
Metric and inverse metric tensors
Jacobians and metric densities
Symbolic and numerical differential operators
Parameterized geometries
Coordinate systems are very useful in their own right; however, they are most commonly used in PyMetric as part of the construction of a grid (see Geometric Grids: General Info) or a field (see Fields: Overview).
Each coordinate system class inherits from a common abstract base and defines:
Axes: Named coordinate directions (e.g.,
["r", "theta", "phi"]
).Symbolic infrastructure: Metric tensors, derivatives, and other expressions are computed symbolically using SymPy.
Numerical evaluation: Expressions are compiled into NumPy-compatible functions for high-performance evaluation on structured grids or unstructured inputs.
Parameter support: Some systems are parameterized (e.g., ellipsoidal focus distance
a
), allowing for flexible instantiation of geometric families.Differential operators: Each system defines methods for computing gradients, divergences, Laplacians, and other tensor calculus expressions in its own basis.
Coordinate systems in PyMetric are suitable for use in:
Finite difference and finite volume solvers
Symbolic exploration of curvilinear geometry
Tensor calculus in custom geometries
Evaluation of scalar or vector fields in native coordinates
Hint
Coordinate systems are not intended for high-throughput time-stepping applications where millions of derivative evaluations are required per second. Instead, they are optimized for flexibility, clarity, and correctness in symbolic and semi-analytic workflows.
Important
All coordinate systems in PyMetric share a consistent interface, and expose symbolic and numerical methods for working with geometry. This makes it easy to switch between geometries or extend the framework with custom systems.
Coordinate systems are defined in the coordinates
module, and typically subclass
either:
OrthogonalCoordinateSystem
(for diagonal metric tensors)CurvilinearCoordinateSystem
(for full curvilinear geometries)
Constructing Coordinate Systems#
Coordinate systems in PyMetric are available in the coordinates
module. Each class represents
a specific curvilinear coordinate system, such as
SphericalCoordinateSystem
: Spherical coordinates.CartesianCoordinateSystem2D
: 2D cartesian coordinates.CylindricalCoordinateSystem
: Cylindrical coordinates.
These classes provide symbolic and numerical support for differential geometry and coordinate transformations, and can be directly instantiated as needed.
To create a coordinate system, import the desired class and instantiate it:
from pymetric.coordinates import (
SphericalCoordinateSystem,
ProlateSpheroidalCoordinateSystem
)
# Create a standard spherical coordinate system
spherical = SphericalCoordinateSystem()
# Create a prolate spheroidal system with a custom focal length
prolate = ProlateSpheroidalCoordinateSystem(a=1.5)
Coordinate system instances are lightweight and behave like symbolic geometry containers. Once created, they provide access to axes, symbolic tensors, and geometry-aware operations such as gradient or Laplacian computations.
Hint
PyMetric coordinate systems support both symbolic inspection and NumPy-compatible numerical evaluation.
Required Parameters#
Some coordinate systems require parameters to define their shape or scaling. For example, the
ProlateSpheroidalCoordinateSystem
requires the focal
distance a
as a parameter, which defines the spacing between the foci of the ellipsoids.
If parameters are not provided, default values are used:
cs1 = ProlateSpheroidalCoordinateSystem() # uses a = 1.0 by default
cs2 = ProlateSpheroidalCoordinateSystem(a=2.0) # custom focal parameter
print(cs1.parameters)
{'a': 1.0}
print(cs2.parameters)
{'a': 2.0}
To inspect the current parameters of a coordinate system, use the
parameters
attribute.
Accessing Coordinate System Metadata#
Each coordinate system exposes useful metadata via attributes:
axes
: The logical axis names (e.g.,["r", "theta", "phi"]
).ndim
: The dimensionality of the coordinate system.parameters
: Dictionary of any shape or transformation parameters.
This metadata is used throughout PyMetric to ensure consistency between coordinate systems, grids, and differential operations.
cs = SphericalCoordinateSystem()
print(cs.axes) # ['r', 'theta', 'phi']
print(cs.ndim) # 3
print(cs.parameters) # {}
Note
Some coordinate systems (especially those with nontrivial geometry) may emit logging messages during initialization. These messages provide information about expression parsing, symbolic expression caching, or internal warnings.
You can configure or disable this output using the PyMetric logging tools via
logging
.
Converting Between Coordinate Systems#
PyMetric provides a unified and extensible API for converting coordinates between different coordinate systems. All conversions are performed using Cartesian space as an intermediate representation:
native (source) → Cartesian → native (target)
This ensures generality and allows conversion between any pair of coordinate systems with matching dimensionality.
Important
Coordinate systems must have the same number of dimensions to be convertible.
Basic Conversion#
Use the convert_to()
method to perform a one-shot conversion
between coordinate systems:
from pymetric.coordinates import SphericalCoordinateSystem, CylindricalCoordinateSystem
sph = SphericalCoordinateSystem()
cyl = CylindricalCoordinateSystem()
# Convert from spherical to cylindrical coordinates
r, theta, phi = 1.0, 3.14 / 2, 0.0
rho, phi_cyl, z = sph.convert_to(cyl, r, theta, phi)
This method returns the native coordinates of the target system by first converting to Cartesian and then to the destination system’s basis.
Creating Reusable Converters#
To avoid repeating transformation logic, you can construct a reusable conversion function using
get_conversion_transform()
:
transform = sph.get_conversion_transform(cyl)
rho, phi_cyl, z = transform(1.0, 3.14 / 2, 0.0)
This is especially useful when you need to convert many points across different contexts, or embed conversion logic into higher-level functions.
Conversion to/from Cartesian Space#
Each coordinate system provides direct access to Cartesian conversion:
to_cartesian()
converts from native coordinates to Cartesian.from_cartesian()
converts from Cartesian to native coordinates.
x, y, z = sph.to_cartesian(r, theta, phi)
r2, theta2, phi2 = sph.from_cartesian(x, y, z)
These methods work with both scalar and array inputs, and are automatically vectorized using NumPy broadcasting.
Symbolic Manipulations#
Coordinate systems in PyMetric utilize a mixed design in which symbolic (CAS) based manipulations are favored for deriving analytical quantities in the coordinate system (metrics, Christoffel Symbols, etc.) but then provides numerical access to these quantities via efficient numpy conversion. The symbolic side of PyMetric coordinate systems is handled by SymPy.
These symbolic representations form the foundation for both analytical exploration and numerical computations, allowing you to derive differential operators like gradients or divergences while respecting the geometry of the coordinate system.
Coordinate System Symbols#
When a coordinate system class is created, its axes and parameters are converted into symbolic attributes which
are stored in the axes_symbols
and
parameter_symbols
attributes respectively.
cs = SphericalCoordinateSystem()
print(cs.axes_symbols)
[r, theta, phi]
These symbols are then fed into the class’s methods in order to construct critical symbolic infrastructure like the metric tensor, the inverse metric, etc.
The Metric Tensor#
There are a number of symbolic attributes derived as part of class definition; however, the most important
is the metric tensor. The metric tensor is essential for performing a variety of differential operations and
is therefore present in every class. You can access the symbolic version of the attribute using
metric_tensor_symbol
cs = SphericalCoordinateSystem()
print(cs.metric_tensor_symbol)
[1, r**2, r**2*sin(theta)**2]
Note
Many of the coordinate systems defined in PyMetric are not only curvilinear, but are also
orthogonal. In this case, the metric is diagonal and is therefore represented internally as a vector
instead of a tensor. For classes like OblateHomoeoidalCoordinateSystem
,
which are fully curvilinear, the output here is a true matrix.
The metric tensor is also available as a numpy-like numerical function:
cs = SphericalCoordinateSystem()
cs.metric_tensor(1,np.pi/2,0)
array([1., 1., 1.])
You can call the metric tensor function by simply passing arrays for each coordinate into the function.
Creating / Retrieving Derived Attributes#
PyMetric supports derived expressions beyond the metric, such as:
Christoffel terms (for custom systems)
Coordinate Jacobians
System-specific auxiliary expressions
along with a few symbols which are of critical importance internally for differential geometry operations (like the metric determinant). Regardless of which symbolic attribute is of interest, it is always possible to access the attribute symbolically and numerically.
Attributes which are not implemented by default are called derived attributes and a list of them can be accessed with
cs = OblateHomoeoidalCoordinateSystem(ecc=0.3)
print(cs.list_expressions())
['Lterm', 'Dterm', 'metric_tensor', 'metric_density', 'inverse_metric_tensor']
If you want to retrieve a particular symbolic attribute, you can simply
use the get_expression()
method.
cs = OblateHomoeoidalCoordinateSystem(ecc=0.3)
print(cs.get_expression('metric_density'))
sqrt(-xi**4*sin(theta)**2/(0.000729*sin(theta)**6 - 0.0243*sin(theta)**4 ^ 0.27*sin(theta)**2 - 1.0))
cs = OblateHomoeoidalCoordinateSystem(ecc=0.0)
print(cs.get_expression('metric_density'))
sqrt(xi**4*sin(theta)**2)
Accessing Numerical Versions of Symbolic Expressions#
All symbolic expressions can be turned into callable NumPy functions using:
fn = cs.get_numeric_expression("metric_density")
val = fn(r=1.0, theta=np.pi/2, phi=0.0)
This process uses sympy.lambdify()
under the hood, and allows fast evaluation over grids or datasets.
Class Level Expressions#
Some expressions—like the metric tensor—are computed at the class level and shared across all instances (symbolically). You can inspect or retrieve these without instantiating the coordinate system:
from pymetric.coordinates.coordinate_systems import CylindricalCoordinateSystem
g = CylindricalCoordinateSystem.get_class_expression("metric_tensor")
print(g)
This is useful for inspecting or manipulating symbolic expressions analytically before plugging in parameter values.