A high-performance Python package for generating 2D and 3D Representative Volume Elements (RVE) and unit cells containing geometric inclusions following specified size, orientation, and spatial distributions.
Sample generated cells with
| Initial Random Cell | Optimized Non-Overlapping Cell |
|---|---|
![]() |
![]() |
![]() |
![]() |
pip install shapeforgeOr for development with uv:
git clone https://github.com/338rajesh/shapeforge.git
cd shapeforge
uv syncRun cell generation directly from a YAML configuration file:
# Generate RVE with default configuration
shapeforge demo/config.yaml
# Run with verbose info/debug logging
shapeforge demo/config.yaml --log debug
# Profile execution to find bottlenecks
shapeforge demo/config.yaml --profile --profile-sort cumulative --profile-lines 25from pathlib import Path
from shapeforge.main import generate_cell_2d
from shapeforge.cell import Cell
# Generate cells using a configuration file or mapping
cells = generate_cell_2d("demo/config.yaml")
# Each cell pair contains (initial_cell, final_optimized_cell)
initial_cell, final_cell = cells[0]
# Inspect properties
print(f"Number of inclusions: {len(final_cell.shapes)}")
print(f"Domain area: {final_cell.domain.area:.2f}")
print(f"Inclusions area: {final_cell.shapes.area:.2f}")
print(
f"Actual volume fraction: {final_cell.shapes.area / final_cell.domain.area:.4f}"
)
# Save or plot programmatically
final_cell.save("output/cell_0.json", overwrite=True)
final_cell.plot(
"output/cell_0.png", size=(512, 512), background=0, foreground=255, dpi=100
)- Geometric Inclusions: Circular and non-circular shapes (such as
Circle,Ellipse), with constant or statistical parameter distributions (viascipy.stats). - High-Performance Overlap Optimization:
- Vectorized distance queries with
scipy.spatial.cKDTreeand analytical gradients. - Non-Monotone Spectral Projected Gradient (
nmspg) optimization engine for fast, robust overlap resolution.
- Vectorized distance queries with
- Advanced Spatial Initialization:
- Spatial point initialization with
uniformrandom sampling, Latin Hypercube Sampling (lhs), or Sobol low-discrepancy sequences (sobol) viascipy.stats.qmc.
- Spatial point initialization with
- Exact Volume Fraction Bounds:
- Option to dynamically scale domain bounds (
packing.adjust_bounds_to_exact_vf) to achieve exact target volume fractions.
- Option to dynamically scale domain bounds (
- Config-Driven Multi-Format Export:
- Raster images:
.png,.jpg,.pdfwith customizable dimensions, DPI, background, and facecolor. - Cell serialization:
.json,.yaml,.pkl, and.npzwith lossless round-trip saving and loading (Cell.save()/Cell.load()). - Raw microstructure arrays:
.npybinary arrays ready for machine learning and finite element pipelines. - Diagnostic convergence curves: Automatically plots objective function and gradient norm histories.
- Raster images:
- Built-in Profiling Suite:
- Profile runs anytime with
--profileto pinpoint hotspots in execution time and memory. Compatible with SnakeViz.
- Profile runs anytime with
ShapeForge is completely driven by declarative YAML configuration files:
# ============================================================================
# ShapeForge Configuration
# ============================================================================
num_cells: 1
metadata:
title: shapeforge-demo
rng_seed: 42
log_level: info # debug | info | warning | error
domain:
shape: rectangle
bounds:
x_min: 0.0
y_min: 0.0
x_max: 50.0
y_max: 50.0
shapes:
- name: Circle
volume_fraction: 0.25
params:
radius: 1.0 # constant or distribution, e.g. uniform(1.0, 0.2)
- name: Ellipse
volume_fraction: 0.20
params:
semi_major_length: 2.0
semi_minor_length: 1.0
major_axis_angle: uniform(0.0, 1.0)
packing:
periodicity: false
min_gap_ratio: 0.05
proj_buffer_ratio: 2.0
adjust_bounds_to_exact_vf: false
solver:
init_method: uniform # uniform | lhs | sobol
optimiser:
name: nmspg
max_iter: 1000
epsilon: 1.0e-6
export:
output_dir: ./scratch/outputs/test_1
as_array: false # save raw uint8 array as .npy
formats: # png, jpg, pdf, json, pkl, yml, npz
- png
- json
background: 0
facecolor: 255
image_dimensions: [512, 512]
dpi: 100ShapeForge supports user-defined shapes beyond the built-in Circle and Ellipse from gbox. Users can implement custom 2D particles by subclassing gbox.Shape2D in a standalone Python file.
A custom shape needs:
-
from_params(position_params, size_params): Factory method for spatial sampling. -
union_of_circles() -> gb.CirclesArray: Multi-circle decomposition used by the SPG overlap optimizer. -
bounding_box: Rectangular bounds for broadphase culling & periodic boundary copying. -
areaandclone(): Area calculation and deep copy. -
Plotting: Either
to_patch()returning a MatplotlibPatch(e.g.Rectangle,PathPatch) orboundary_points()returning an$(N, 2)$ array of boundary coordinates (rendered cleanly as a closedPolygon).
import numpy as np
import gbox as gb
from shapeforge import register_shape
@register_shape("stadium")
class Stadium(gb.Shape2D):
def __init__(self, length: float, radius: float, centre=(0.0, 0.0), orientation=None):
self._length = float(length)
self._radius = float(radius)
self._position = gb.shapes_2d.Shape2DPose(centre[0], centre[1], orientation or gb.Angle.rad(0.0))
@property
def area(self) -> float:
return 2.0 * self._length * self._radius + np.pi * self._radius**2
def union_of_circles(self) -> gb.CirclesArray:
xs = np.linspace(-self._length / 2.0, self._length / 2.0, max(3, int(np.ceil(self._length / self._radius))))
return gb.CirclesArray(np.column_stack([xs, np.zeros_like(xs)]), np.full_like(xs, self._radius))
def boundary_points(self) -> np.ndarray:
# Return (N, 2) world-space boundary coordinates for crisp vector rendering
...
@classmethod
def from_params(cls, position_params, size_params):
return cls(size_params["length"], size_params["radius"],
centre=(position_params["xc"], position_params["yc"]),
orientation=position_params.get("major_axis_angle"))Include the script in your configuration file:
custom_shapes:
- "shapes/my_custom_shapes.py"
shapes:
- name: stadium
volume_fraction: 0.15
params:
length: 3.0
radius: 0.8Or pass via the CLI flag:
shapeforge config.yaml --custom-shapes shapes/my_custom_shapes.pyA Cell consists of:
-
Domain: Spatial boundary (e.g.
CellDomain2Dwith rectangular bounds). -
Inclusions: Collection of shapes (
Inclusions2D) containinggb.Shape2Dobjects.-
Positional parameters: Centroid coordinates
$(x, y)$ and orientation angle$\theta$ . These are optimized by the solver while preserving shape sizes. -
Size parameters: Semi-major length, semi-minor length, radius, etc. Defined either as fixed numbers or continuous distributions (
norm,uniform,lognorm, etc.).
-
Positional parameters: Centroid coordinates
ShapeForge includes an integrated profiler. You can profile any generation run to analyze runtime hotspots:
# Profile and display the top 20 cumulative time hotspots
shapeforge demo/config.yaml --profile --profile-sort cumulative --profile-lines 20
# Visualize binary profile with SnakeViz
snakeviz scratch/outputs/test_1/shapeforge.profRun the test suite with pytest:
pytest -vThis project is licensed under the MIT License - see the LICENSE file for details.



