Skip to content

maroba/numgrids

numgrids

Working with numerical grids made easy.

PyPI version build Documentation

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 .npz files
  • Multigrid hierarchies with inter-level transfer operators
  • Fully compatible with NumPy and SciPy

Installation

pip install --upgrade numgrids

Documentation

Full documentation including API reference and example notebooks is available at maroba.github.io/numgrids.

Quick Start

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)**2

Define partial derivatives $\partial/\partial r$ and $\partial/\partial \varphi$ and apply them:

# 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

$$ \int \dots dr d\varphi $$

I = Integral(grid)

Calculate the area integral

$$ \int f(r, \varphi) r dr d\varphi $$

(taking into account the appropriate integration measure $r$ for polar coordinates):

I(f * R)

Setting boundary values to zero

f[grid.boundary] = 0  # grid.boundary is boolean mask selecting boundary grid points

or 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)

Curvilinear Grids & Vector Calculus

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.

Spherical coordinates

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) = 0

Cylindrical coordinates

grid = 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)

Polar coordinates (2D)

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 = 2

All operators handle the coordinate singularities at r = 0 and θ = 0, π gracefully — non-finite values are automatically replaced by zero.

Custom curvilinear coordinates

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.

Usage / Example Notebooks

To get an idea how numgrids can be used, have a look at the following example notebooks:

How to Cite

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}
}

Development

Setting up the project

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

Contributing

  1. Fork the repository
  2. Develop
  3. Write tests!
  4. Create an issue
  5. Create a pull request, when done

About

Working with numerical grids made easy.

Topics

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Languages