Working with numerical grids made easy.
Setting up numerical grids, differentiation matrices, and coordinate transformations by hand is tedious and error-prone. numgrids gives you a high-level, NumPy-friendly API that handles all of this — so you can focus on the physics or mathematics of your problem instead of bookkeeping grid indices and scale factors.
Main Features
- Quickly define numerical grids for any rectangular or curvilinear coordinate system
- Multiple axis types: equidistant, Chebyshev, logarithmic, and periodic
- Built-in spherical, cylindrical, and polar coordinate grids
- Custom curvilinear coordinates — supply scale factors and get gradient, divergence, curl, and Laplacian automatically
- Vector calculus operators: gradient, divergence, curl, and Laplacian on curvilinear grids
- High-precision spectral methods (FFT + Chebyshev) selected automatically where possible
- Differentiation, integration, and interpolation
- Boundary conditions: Dirichlet, Neumann, and Robin — at the array level or inside sparse linear systems
- Adaptive mesh refinement with built-in Richardson-extrapolation error estimation
- Save / load grids and data to
.npzfiles - Multigrid hierarchies with inter-level transfer operators
- Fully compatible with NumPy and SciPy
pip install --upgrade numgridsFull documentation including API reference and example notebooks is available at maroba.github.io/numgrids.
As a quick example, here is how you define a grid on the unit disk using polar coordinates. Along the azimuthal (angular) direction, choose an equidistant spacing with periodic boundary conditions:
from numgrids import *
from numpy import pi
axis_phi = create_axis(AxisType.EQUIDISTANT, 50, 0, 2*pi, periodic=True)Along the radial axis, let's choose a non-equidistant spacing:
axis_radial = create_axis(AxisType.CHEBYSHEV, 20, 0, 1)Now combine the axes to a grid:
grid = Grid(axis_radial, axis_phi)Sample a meshed function on this grid:
from numpy import exp, sin
R, Phi = grid.meshed_coords
f = R**2 * sin(Phi)**2Define partial derivatives
# second argument means derivative order, third argument means axis index:
d_dr = Diff(grid, 1, 0)
d_dphi = Diff(grid, 1, 1)
df_dr = d_dr(f)
df_dphi = d_dphi(f)Obtain the matrix representation of the differential operators:
d_dr.as_matrix()
Out: <1000x1000 sparse matrix of type '<class 'numpy.float64'>'
with 20000 stored elements in COOrdinate format>Define integration operator
I = Integral(grid)Calculate the area integral
(taking into account the appropriate integration measure
I(f * R)Setting boundary values to zero
f[grid.boundary] = 0 # grid.boundary is boolean mask selecting boundary grid pointsor to something more complicated:
f[grid.boundary] = exp(-R[grid.boundary])Create an interpolation function
inter = Interpolator(grid, f)Interpolate for a single point
point = (0.1, 0.5)
inter(point)or for many points at once, like for a parametrized curve:
t = np.linspace(0, 1, 100)
points = zip(2*t, t**2)
inter(points)numgrids provides dedicated grid classes for the most common curvilinear coordinate systems, each with built-in vector calculus operators that correctly account for scale factors and coordinate singularities.
from numgrids import *
import numpy as np
grid = SphericalGrid(
create_axis(AxisType.CHEBYSHEV, 25, 0.1, 5), # r
create_axis(AxisType.CHEBYSHEV, 20, 0.1, np.pi-0.1), # theta
create_axis(AxisType.EQUIDISTANT_PERIODIC, 30, 0, 2*np.pi), # phi
)
R, Theta, Phi = grid.meshed_coords
f = R**2
lap_f = grid.laplacian(f) # scalar Laplacian (= 6)
gr, gt, gp = grid.gradient(f) # (2r, 0, 0)
div_v = grid.divergence(gr, gt, gp) # div(grad f) = laplacian f
cr, ct, cp = grid.curl(gr, gt, gp) # curl(grad f) = 0grid = CylindricalGrid(
create_axis(AxisType.CHEBYSHEV, 20, 0.1, 3),
create_axis(AxisType.EQUIDISTANT_PERIODIC, 30, 0, 2*np.pi),
create_axis(AxisType.CHEBYSHEV, 20, -1, 1),
)
R, Phi, Z = grid.meshed_coords
f = R**2 + Z**2
grid.laplacian(f) # = 6
grid.gradient(f) # (2r, 0, 2z)grid = PolarGrid(
create_axis(AxisType.CHEBYSHEV, 30, 0.1, 1),
create_axis(AxisType.EQUIDISTANT_PERIODIC, 40, 0, 2*np.pi),
)
R, Phi = grid.meshed_coords
f = R * np.cos(Phi) # this is just x
grid.laplacian(f) # = 0 (harmonic)
grid.gradient(f) # (cos φ, −sin φ)
grid.curl(R*0, R) # scalar z-component = 2All operators handle the coordinate singularities at r = 0 and θ = 0, π gracefully — non-finite values are automatically replaced by zero.
All three coordinate grids above inherit from CurvilinearGrid, which you
can use directly to define any orthogonal curvilinear coordinate system.
Just supply the metric scale factors h_i as callables:
from numgrids import CurvilinearGrid
grid = CurvilinearGrid(
axis_q1, axis_q2, axis_q3,
scale_factors=(
lambda c: np.ones_like(c[0]), # h_1(q)
lambda c: c[0], # h_2(q) = q_1
lambda c: c[0] * np.sin(c[1]), # h_3(q) = q_1 sin(q_2)
),
)
grid.laplacian(f)
grid.gradient(f)
grid.divergence(v1, v2, v3)
grid.curl(v1, v2, v3)Each callable receives the tuple of meshed coordinate arrays and returns an
array of shape grid.shape. The class automatically derives all vector
calculus operators from the standard orthogonal curvilinear identities — no
subclassing required.
To get an idea how numgrids can be used, have a look at the following example notebooks:
- How to define grids
- Partial derivatives in any dimension
- Polar coordinates on unit disk
- Spherical Grid and the Spherical Laplacian
- Solving the Schrödinger equation for the quantum harmonic oscillator
If you use numgrids in a publication, please cite it as:
M. Baer. numgrids software package. URL: https://github.com/maroba/numgrids. 2023
BibTeX entry:
@misc{numgrids,
title = {{numgrids} Software Package},
author = {M. Baer},
url = {https://github.com/maroba/numgrids},
key = {numgrids},
note = {\url{https://github.com/maroba/numgrids}},
year = {2023}
}Clone the repository
git clone https://github.com/maroba/numgrids.git
In the project root directory, submit
pip install -e .
to install the package in development mode.
Run the tests:
python -m pytest tests
- Fork the repository
- Develop
- Write tests!
- Create an issue
- Create a pull request, when done




