Quickstart#
The bunny examples use files from this repository, so start from a source checkout:
git clone https://github.com/Rishit-dagli/torch-diffsim
cd torch-diffsim
pip install -e .
Run a standard simulation#
This example holds the bunny by the tips of its ears and lets gravity deform the rest of the body:
from pathlib import Path
import torch
from diffsim import (
SemiImplicitSolver,
Simulator,
StableNeoHookean,
TetrahedralMesh,
)
device = "cuda" if torch.cuda.is_available() else "cpu"
source = TetrahedralMesh.from_file(
Path("assets/tetmesh/stanford_bunny.msh"),
device=device,
)
mesh = TetrahedralMesh(
source.vertices + source.vertices.new_tensor([0.0, 0.60, 0.0]),
source.tetrahedra,
device=device,
)
simulator = Simulator(
mesh,
StableNeoHookean(youngs_modulus=2e5, poissons_ratio=0.4),
SemiImplicitSolver(
dt=0.002,
gravity=-9.81,
damping=0.996,
substeps=6,
),
density=1000.0,
device=device,
)
support = torch.where(
mesh.vertices[:, 1] >= torch.quantile(mesh.vertices[:, 1], 0.96)
)[0]
simulator.set_fixed_vertices(support)
for _ in range(500):
simulator.step()
F = mesh.compute_deformation_gradient(simulator.positions)
print("minimum J:", torch.linalg.det(F).min().item())
The fixed indices are a boundary condition: their positions stay prescribed, their velocities stay zero, and the other vertices remain free. The complete example opens Polyscope and lets you inspect stress and strain:
python examples/suspended_bunny.py
Train through the simulator#
The differentiable solver is built from PyTorch tensor operations, so a simulation rollout can sit inside an ordinary training loop. Here a small MLP learns to move one ear toward a target:
import torch
from examples.train_neural_controller import (
EarController,
build_problem,
rollout,
)
device = "cuda" if torch.cuda.is_available() else "cpu"
torch.manual_seed(8)
if torch.cuda.is_available():
torch.cuda.manual_seed_all(8)
problem = build_problem(device)
controller = EarController().to(device)
optimizer = torch.optim.Adam(controller.parameters(), lr=0.018)
for _ in range(12):
optimizer.zero_grad()
for target_distance, initial_velocity in problem["training_conditions"]:
result = rollout(
problem,
controller,
steps=48,
target_distance=target_distance,
initial_tip_velocity=initial_velocity,
)
(result["loss"] / len(problem["training_conditions"])).backward()
torch.nn.utils.clip_grad_norm_(controller.parameters(), 1.0)
optimizer.step()
loss.backward() differentiates through all 48 FEM steps. The full script
also fits a constant actuator for comparison and evaluates both controllers on
a new target and initial velocity:
python examples/train_neural_controller.py --plot neural_bunny.png
CUDA is recommended for the default training run.
Choose a contact model#
The package has three contact paths:
SemiImplicitSolverhas a fast projected ground plane for forward demos. It has no CCD or tetrahedron-inversion protection.DifferentiableSolveruses a smooth finite ground penalty. Gradients flow through it, but it cannot guarantee zero penetration.IPCImplicitEulerSolveruses the official IPC barrier and conservative CCD, together with a tetrahedron-inversion step bound.
The IPC solver’s backward pass uses an implicit adjoint around the converged step.
pip install -e ".[ipc]"
python examples/ipc_bunny.py
Where to go next#
Examples briefly describes every maintained example.
Simulation explains the FEM and contact formulations.
Differentiation explains how gradients are computed.
Mesh and Differentiable Simulator contain the main API references.