Axes¶
An axis is the fundamental building block in numgrids. It represents a single coordinate dimension – a 1D set of grid points with a specific spacing strategy. One or more axes are combined into a Grid via tensor product.
from numgrids import *
import numpy as np
Creating axes¶
Use the create_axis factory function:
ax = create_axis(AxisType.EQUIDISTANT, 50, 0.0, 1.0)
The arguments are:
axis type – one of the
AxisTypeenum membersnum_points – number of grid points
low – lower coordinate bound
high – upper coordinate bound
Axis types¶
Equidistant (non-periodic)¶
ax = create_axis(AxisType.EQUIDISTANT, 50, 0.0, 1.0)
Uniformly spaced points from low to high (both endpoints included).
Differentiation uses classical finite differences via the findiff library.
Best for problems where uniform resolution is sufficient and the solution does
not have sharp boundary layers.
Equidistant periodic¶
ax = create_axis(AxisType.EQUIDISTANT_PERIODIC, 64, 0.0, 2 * np.pi)
Uniformly spaced points on a periodic domain. The last point is not included because it would coincide with the first after wrapping. Differentiation uses FFT spectral methods, giving spectral accuracy for smooth periodic functions.
Chebyshev¶
ax = create_axis(AxisType.CHEBYSHEV, 30, -1.0, 1.0)
Grid points are placed at Chebyshev nodes, which cluster near the domain boundaries:
mapped linearly to \([\text{low}, \text{high}]\). Differentiation uses the Chebyshev spectral differentiation matrix, achieving spectral accuracy for smooth non-periodic functions. Excellent for resolving boundary layers.
Logarithmic¶
ax = create_axis(AxisType.LOGARITHMIC, 40, 0.01, 10.0)
Points are logarithmically spaced, clustering near the lower boundary. The internal coordinate is \(\xi = \ln(x)\), and differentiation applies the chain rule:
Warning
The lower bound must be strictly positive (low > 0). A ValueError is
raised otherwise.
Choosing the right axis type¶
Scenario |
Recommended type |
Why |
|---|---|---|
Smooth, periodic function (e.g. Fourier modes) |
|
FFT spectral accuracy |
Smooth, non-periodic function |
|
Spectral accuracy without Runge phenomenon |
Uniform resolution, moderate accuracy needs |
|
Simple, controllable via |
Fine resolution near one boundary (e.g. \(r \to 0\)) |
|
Concentrates points near |
Radial coordinate away from origin |
|
Depends on whether the lower bound is positive |
Tip
When in doubt, start with CHEBYSHEV. It gives spectral accuracy and works
well for most smooth, non-periodic problems.
Axis properties¶
Every axis exposes the following properties:
coords¶
The 1D array of coordinate values in the user-specified domain:
ax = create_axis(AxisType.CHEBYSHEV, 5, 0.0, 1.0)
print(ax.coords)
# array([0. , 0.14644661, 0.5, 0.85355339, 1. ])
coords_internal¶
The raw coordinates in the axis’s internal (canonical) coordinate system. For Chebyshev axes, these are the nodes on \([-1, 1]\); for logarithmic axes, the uniformly spaced values in \([\ln(\text{low}), \ln(\text{high})]\):
ax = create_axis(AxisType.CHEBYSHEV, 5, 0.0, 1.0)
print(ax.coords_internal)
# array([ 1. , 0.70710678, 0. , -0.70710678, -1. ])
boundary¶
A slice that selects interior points (excluding boundary). For periodic
axes the full range is returned because there are no boundary points:
ax = create_axis(AxisType.EQUIDISTANT, 10, 0.0, 1.0)
print(ax.boundary) # slice(1, -1, None)
ax_p = create_axis(AxisType.EQUIDISTANT_PERIODIC, 10, 0.0, 2 * np.pi)
print(ax_p.boundary) # slice(None, None, None)
periodic¶
Boolean flag indicating whether the axis uses periodic boundary conditions:
print(ax.periodic) # False
print(ax_p.periodic) # True
Resizing an axis¶
The resized() method returns a new axis of the same type and domain with a
different number of points. All metadata (periodicity, name) is preserved:
ax = create_axis(AxisType.CHEBYSHEV, 20, 0.0, 1.0)
ax_fine = ax.resized(80)
print(len(ax_fine)) # 80
print(type(ax_fine)) # <class 'numgrids.axes.ChebyshevAxis'>
This is used internally by Grid.refine(), Grid.coarsen(), and the
adaptive mesh refinement module.
Named axes¶
Pass the name keyword to give an axis a label. Named axes produce nicer
plot labels:
ax_r = create_axis(AxisType.CHEBYSHEV, 30, 0.1, 5.0, name="r")
print(ax_r.name) # "r"
Visualization¶
In Jupyter notebooks, call plot() to see where the grid points are placed:
ax = create_axis(AxisType.CHEBYSHEV, 20, 0.0, 1.0)
ax.plot()
For periodic axes, plot() renders the points on a circle to emphasize the
wrap-around topology.
Indexing¶
Axes support integer indexing. For periodic axes, the index wraps around automatically:
ax = create_axis(AxisType.EQUIDISTANT_PERIODIC, 8, 0.0, 2 * np.pi)
print(ax[0]) # 0.0
print(ax[8]) # 0.0 (wraps)
print(ax[-1]) # same as ax[7]
Length¶
len(ax) returns the number of grid points:
ax = create_axis(AxisType.EQUIDISTANT, 100, 0.0, 1.0)
print(len(ax)) # 100