jaxdem.utils.meshes#

Asperity mesh generators for geometric-asperity (GA) particles.

Each generate_*_mesh function produces (nv, dim) or (N, nv, dim) unit-scaled vertex positions — the longest axis has extent 1 — suitable for consumption by create_ga_state().

Available meshes#

  • generate_thomson_mesh() — generalized Thomson problem on a hyper-ellipsoid; iterative Riesz-energy minimization, works in 2D and 3D.

  • generate_icosphere_mesh() — recursive icosahedron subdivision (3D) or regular polygon (2D); deterministic; discrete nv in 3D.

  • generate_fibonacci_sphere_mesh() — golden-angle spiral sphere (3D) or evenly-spaced circle (2D); deterministic; any nv.

  • generate_torus_mesh() — quasi-uniform torus surface points (3D).

  • generate_helix_mesh() — right-handed helix (3D) or Archimedean spiral (2D).

  • generate_arclength_mesh() — 2D only: equal-arc-length spacing along an ellipse / circle perimeter. Closed-form analogue of the converged Thomson ground state in 2D (exact in the α→∞ packing limit).

  • generate_faceted_mesh() — polygonal / icosahedral shell. Vertex asperities at the corners + face-interior fillers to reach nv. Gives genuinely angular, crystalline particles (flat faces, sharp edges) as opposed to the smooth-sphere family.

Functions

generate_arclength_mesh(nv, N[, dim, ...])

2D mesh with equal arc-length spacing along the ellipse / circle perimeter.

generate_faceted_mesh(nv, N, dim[, ...])

Regular n-gon (2D) or icosahedron (3D) with vertex + surface-filler asperities.

generate_fibonacci_sphere_mesh(nv, N, dim[, ...])

Generate N Fibonacci-sphere meshes (3D) or circles (2D).

generate_helix_mesh(nv, N, dim[, n_turns, ...])

Generate N helical meshes (3D) or Archimedean spirals (2D).

generate_icosphere_mesh(nv, N, dim[, ...])

Generate N icosphere meshes (3D) or regular polygons (2D).

generate_thomson_mesh(nv, N, dim[, alpha, ...])

Generate and minimize charges constrained to a hyper-ellipsoid surface.

generate_torus_mesh(nv, N[, dim, ...])

Generate N torus surface meshes with nv quasi-uniform points each.

minimize_on_hyper_ellipsoid(pos, axes, alpha)

Minimize Riesz energy for points constrained to a hyper-ellipsoid surface.

project_to_tangent(grad, pos, aspect_ratio)

Remove the normal component of the gradient (project onto tangent plane of surface).

random_points_on_hyper_ellipsoid(key, nv, N, dim)

Generate nv uniform random points on a dim-dimensional unit hyper-ellipsoid surface with a set aspect ratio, repeated N times.

retract_to_surface(pos, aspect_ratio)

Project point back onto the ellipsoid/ellipse surface.

riesz_energy(pos, alpha)

Riesz energy kernel.

jaxdem.utils.meshes.random_points_on_hyper_ellipsoid(key: Array, nv: int, N: int, dim: int, aspect_ratio: Any = None, use_uniform_sampling: bool = True) tuple[Array, Array][source]#

Generate nv uniform random points on a dim-dimensional unit hyper-ellipsoid surface with a set aspect ratio, repeated N times. If using uniform sampling, expensive rejection sampling is used to ensure the points are uniform across the surface. This gives the exact result for hyper-ellipsoids, but is not necessary for hyper-spheres. Scaled so that the longest axis is unit-length.

jaxdem.utils.meshes.riesz_energy(pos: Array, alpha: float) Array[source]#

Riesz energy kernel. alpha=1 reduces to the Thomson problem. alpha=infty reduces to the packing problem

jaxdem.utils.meshes.project_to_tangent(grad: Array, pos: Array, aspect_ratio: Array) Array[source]#

Remove the normal component of the gradient (project onto tangent plane of surface).

jaxdem.utils.meshes.retract_to_surface(pos: Array, aspect_ratio: Array) Array[source]#

Project point back onto the ellipsoid/ellipse surface.

jaxdem.utils.meshes.minimize_on_hyper_ellipsoid(pos: Array, axes: Array, alpha: float, lr: float = 0.01, steps: int = 1000) tuple[Array, Array][source]#

Minimize Riesz energy for points constrained to a hyper-ellipsoid surface.

jaxdem.utils.meshes.generate_thomson_mesh(nv: int, N: int, dim: int, alpha: float = 1.0, lr: float = 0.01, steps: int = 1000, aspect_ratio: Any = None, use_uniform_sampling: bool = True, batch_size: int | None = None, seed: int | None = None) tuple[Array, Array][source]#

Generate and minimize charges constrained to a hyper-ellipsoid surface.

jaxdem.utils.meshes.generate_icosphere_mesh(nv: int, N: int, dim: int, aspect_ratio: Any = None) Array[source]#

Generate N icosphere meshes (3D) or regular polygons (2D).

In 3D, nv must be 10 * frequency**2 + 2 for some integer frequency >= 1 (i.e. one of {12, 42, 92, 162, 252, ...}). Powers of two use recursive midpoint subdivision; other frequencies use direct triangular geodesic subdivision. Use generate_fibonacci_sphere_mesh() if you need an arbitrary vertex count on a sphere.

In 2D, nv can be any integer and the output is a regular nv-gon on the unit circle.

The mesh is deterministic, so all N bodies are identical copies; per-body random orientation is typically applied downstream by distribute_bodies().

jaxdem.utils.meshes.generate_fibonacci_sphere_mesh(nv: int, N: int, dim: int, aspect_ratio: Any = None) Array[source]#

Generate N Fibonacci-sphere meshes (3D) or circles (2D).

In 3D the points are laid out by a golden-angle spiral (the sunflower / Fibonacci lattice): z is stratified in (-1, 1) and the azimuth advances by the golden angle on each step. The result is a near-optimal, deterministic, low-discrepancy covering of the sphere for any nv >= 1. It’s the “I want uniform points fast” default and a drop-in alternative to Thomson that skips the minimization.

In 2D the output is nv evenly-spaced points on the unit circle (there’s no non-trivial 1D analogue of the spiral).

The mesh is deterministic, so all N bodies are identical copies; per-body random orientation is typically applied downstream by distribute_bodies().

jaxdem.utils.meshes.generate_torus_mesh(nv: int, N: int, dim: int = 3, tube_ratio: float = 0.3, aspect_ratio: Any = None) Array[source]#

Generate N torus surface meshes with nv quasi-uniform points each.

The torus is parameterized by two radii: the major radius R (center of the tube → center of the torus) and the minor radius r (tube half-thickness). tube_ratio sets r directly under the “longest axis has extent 1” convention: r = tube_ratio and R = 1 - tube_ratio, so the torus fits in x, y [-1, 1] and z [-r, r].

Points are placed by stratified angular sampling around the major axis (theta, evenly spaced) paired with a golden-ratio quasi-random phase around the tube (phi). This gives good 2D coverage of the torus surface for any nv, with a slight over-representation of the inner rim relative to the outer rim (exact-uniform sampling would require a (R + r cos(phi)) area weighting, which we skip for simplicity).

Useful for non-convex, genus-1 particles — e.g. studies of interlocking or linking in packings where non-convexity matters.

jaxdem.utils.meshes.generate_helix_mesh(nv: int, N: int, dim: int, n_turns: float = 3.0, helix_radius: float = 0.3, aspect_ratio: Any = None) Array[source]#

Generate N helical meshes (3D) or Archimedean spirals (2D).

In 3D the points trace a right-handed helix along the z axis: nv points evenly spaced in the arc parameter, making n_turns full turns from z = -1 to z = 1 on a circle of radius helix_radius. This gives chiral, rod-like bodies with a controllable pitch; good for studies of enantiomeric packing or helical-fiber clumps.

In 2D the helix degenerates to an Archimedean spiral centered at the origin, with nv points going from near the origin out to the unit circle over n_turns turns.

jaxdem.utils.meshes.generate_faceted_mesh(nv: int, N: int, dim: int, n_facets: int = 6, aspect_ratio: Any = None) Array[source]#

Regular n-gon (2D) or icosahedron (3D) with vertex + surface-filler asperities.

Unlike the smooth-sphere family (thomson / icosphere / fibonacci), this mesh keeps the particle genuinely faceted: the shape is a polygon or polyhedron with sharp vertices and flat faces, and the asperities sit on those flat features rather than being projected to a circumscribed sphere.

Asperity layout

  • Vertex asperities are always placed at the corners of the shape (n_facets in 2D, 12 for the icosahedron in 3D).

  • The remaining nv - n_vertices asperities are distributed uniformly across the surface primitives — edges in 2D, triangular face interiors in 3D — to achieve an (approximately) uniform surface density. If nv - n_vertices is not divisible by the number of primitives, the first few get one extra asperity so the total exactly matches nv.

In 2D, edge-interior asperities are evenly spaced along each edge (excluding the endpoints, which are already vertex asperities).

In 3D, face-interior asperities are quasi-uniformly sampled inside each triangular face via a barycentric-coordinate remap. They stay on the flat face — not projected to the circumscribing sphere — so the particle’s faceted character is preserved (asperities on faces sit closer to the center than vertex asperities).

Parameters:
  • nv (int) – Total number of asperities. Must be >= n_facets in 2D and >= 12 in 3D. Larger nv → higher surface density.

  • N (int) – Number of bodies (all identical copies; per-body orientation is handled downstream).

  • dim (int) – Spatial dimension (2 or 3).

  • n_facets (int) – 2D only: number of polygon sides / vertices. Must be >= 3. Ignored in 3D (the icosahedron has 12 vertices / 20 faces fixed).

  • aspect_ratio (None, scalar, or (dim,) array-like) – Axis stretch in the usual “normalize to max=1” convention.

Returns:

Shape (nv, dim) if N == 1 else (N, nv, dim). Vertex asperities appear first in the array, followed by edge/face fillers grouped by primitive.

Return type:

jax.Array

jaxdem.utils.meshes.generate_arclength_mesh(nv: int, N: int, dim: int = 2, aspect_ratio: Any = None, n_fine: int | None = None) Array[source]#

2D mesh with equal arc-length spacing along the ellipse / circle perimeter.

This is the closed-form analogue of the converged (n_steps ) Thomson ground state in 2D. On a circle the two coincide exactly — both give the regular nv-gon. On an ellipse, “uniform neighbor distance” (= equal arc-length spacing) is exactly the α packing-problem limit of Riesz-energy minimization and a very close approximation to the α = 1 Thomson ground state. Use this when you’d otherwise run Thomson with a huge n_steps budget on a 2D particle — it arrives at essentially the same answer in one numerical integration, deterministically.

The algorithm inverts the arc-length parameterization of the ellipse (a cos t, b sin t) numerically: trapezoidal-rule the arclength integrand on a fine grid, then linearly interpolate the angles corresponding to nv equally spaced target arc lengths. Accuracy is controlled by n_fine (auto-sized to max(10_000, 200 * nv) by default).

Parameters:
  • nv (int) – Number of surface vertices. Must be >= 3.

  • N (int) – Number of bodies (all identical copies; per-body orientation is handled downstream).

  • dim (int) – Must be 2. In 3D “uniform neighbor distance” is generically the multi-point Thomson problem — use generate_thomson_mesh().

  • aspect_ratio (None, scalar, or (2,) array-like) – Ellipse semi-axes in the usual “normalize to max=1” convention. None gives a circle.

  • n_fine (int, optional) – Grid resolution for the arc-length integral. None auto-sizes.

Returns:

Shape (nv, 2) if N == 1 else (N, nv, 2).

Return type:

jax.Array