Skip to content

Ansh

PyPI - Version PyPI - Python Version

Ansh (अंश), which means a part or an element, is a Python library to define numerical approximation of scalar and vector fields ideal for finite-element analysis. It is a thin wrapper around meshio, pyvista, and skfem. meshio is used for reading and writing meshes. skfem is used for finite-element field definition. pyvista is used for plotting both meshes and fields.


Table of Contents

Installation

pip install ansh       # pip
uv add ansh            # uv
pixi add --pypi ansh   # pixi

Quick Start

Meshes

Load a mesh from file and inspect its properties:

import ansh
import numpy as np

m = ansh.Mesh.from_file("tests/meshes/med/omega.med")
m.info = {"name": "shell", "about": "one of the unit test meshes"}
m.points          # float64 array of shape (n_points, 3)
m.cells           # list of CellBlock objects (type + connectivity)
m.cell_centres    # list of cell centre arrays per cell block
m.subregions      # dict mapping region names to cell index arrays
m.scale           # mesh scaling factor
m.info            # user-attached metadata dict

Subregions can be added via cell indices or selector functions:

def select_unit_cube(centres):
    return (
        ((centres[:, 0] < 1.) & (centres[:, 0] > -1.)) &
        ((centres[:, 1] < 1.) & (centres[:, 1] > -1.)) &
        ((centres[:, 2] < 1.) & (centres[:, 2] > -1.))
    )

# value has one entry per cell block. So no cells from block 0 and 2.
m.add_subregion(
    subregion_name="cube",
    value=[np.array([], dtype=np.int_), select_unit_cube, np.array([], dtype=np.int_)],
)

Convert to PyVista or scikit-fem for further use:

m.to_pyvista()        # returns pyvista.MultiBlock with one block per subregion
m.to_meshio()         # returns meshio.Mesh
m.to_skfem()          # returns skfem MeshTet1
m.plot()              # interactive 3D plot via PyVista

See the full mesh demo for more details.

Scalar fields

Define a scalar field on a mesh by specifying the element type and initial value:

f = ansh.ScalarField(mesh=m, element_type=ansh.element_types.TetP1(), value=42.)
f.element_type     # skfem element (e.g. ElementTetP1)
f.value            # 1D float64 array of DOF values, shape (n_dofs,)
f.dof_locations    # coordinates of the DOF points, shape (n_dofs, 3)

The field is callable — evaluate it at arbitrary points inside the mesh:

f([0, 0, 0])                              # array([42.])
f(np.array([[0, 2., -1.], [0, 0, 0]]))   # array([42., 42.])

Update the value with a callable (receives DOF locations, returns values):

def norm_val(dofs):
    return np.linalg.norm(dofs, axis=-1)

f.value = norm_val

Set values on a specific subregion:

f.set_subregion_value(name="boundary", value=0.)

Export for matrix assembly or visualisation:

f.to_skfem()       # returns skfem DiscreteField
f.skfem_basis      # returns skfem CellBasis
f.to_pyvista()     # returns pyvista MultiBlock with field data attached
f.plot()           # interactive 3D plot with a clip widget

See the full scalar field demo for more details.

Vector fields

Define a vector field similarly, passing a 3-component value:

f = ansh.VectorField(
    mesh=m,
    element_type=ansh.element_types.TetP1(),
    value=[42., 42., 42.],
)
f.element_type     # skfem ElementVector (wrapping ElementTetP1)
f.value            # 2D float64 array of DOF values, shape (n_dofs, 3)
f.dof_locations    # coordinates of the DOF points, shape (n_dofs, 3)

Evaluate at arbitrary points:

f([0, 0, 0])                              # array([[42., 42., 42.]])
f(np.array([[0, 2., -1.], [0, 0, 0]]))   # array([[42., 42., 42.], ...])

Set value via callable (receives DOF locations, returns array of shape (n_dofs, 3)):

f.value = lambda dofs: dofs

Set subregion values:

f.set_subregion_value(name="shell", value=[0., 0., 0.])

Plot with glyphs coloured by component or magnitude:

f.plot(colour_with="z", factor=3e-3, cmap="RdBu")
f.plot(colour_with="magnitude", factor=3e-3, cmap="viridis")

See the full vector field demo for more details.

An advanced example combining meshes with scalar and vector fields is available in the demagnetisation truncation notebook.

I/O

Mesh — read any format supported by meshio (.med, .vol, .vtk, .msh, etc.) and write to Abaqus .inp, Ansh HDF5 .h5, or VTK unstructured grid .vtu:

m.to_file("mesh.inp")
m.to_file("mesh.h5")
m.to_file("mesh.vtu")
ansh.Mesh.from_file("mesh.inp")
ansh.Mesh.from_file("mesh.h5")
ansh.Mesh.from_file("mesh.vtu")

Scalar/vector field — read/write HDF5 (.h5) or VTK unstructured grid (.vtu):

f.to_file("field.h5")
f.to_file("field.vtu")
ansh.ScalarField.from_file("field.h5")
ansh.VectorField.from_file("field.vtu")

Developers

Set up a development environment with Pixi:

git clone https://codeberg.org/Sankhya/Ansh.git
cd Ansh
pixi install
pixi shell -e dev   # activates the environment

Run tests with pytest:

pixi run pytest

Lint the source with Ruff:

pixi run ruff check src/ansh/

Build the package with Hatchling:

pixi run hatch build -t wheel

Publish to PyPI:

pixi run hatch publish

Explore the Jupyter notebooks for ad-hoc testing and demos:

pixi run jupyter lab docs/

Key conventions:

  • Source lives under src/ansh/ (src layout).
  • Public API is exported from src/ansh/__init__.py — symbols: Mesh, CellBlock, ScalarField, VectorField, element_types.
  • Runtime type validation is enforced by beartype decorators wherever it makes sense.
  • Docstrings follow Google style (Args/Returns/Raises/Notes).
  • HDF5 I/O uses the .h5 extension.
  • The remote is on Codeberg.

License

ansh is distributed under the terms of the MIT license.