### Perform Automatic Differentiation with ti.ad.Tape Source: https://context7.com/taichi-dev/difftaichi/llms.txt Shows how to use the Tape context manager to record kernel operations and compute gradients for optimization. The example includes a forward simulation pass followed by gradient descent to update initial velocity parameters. ```python import taichi as ti import matplotlib.pyplot as plt ti.init(default_fp=ti.f32, arch=ti.gpu) steps = 1024 loss = ti.field(dtype=ti.f32, shape=(), needs_grad=True) init_v = ti.Vector.field(2, dtype=ti.f32, shape=(), needs_grad=True) # Optimization loop learning_rate = 10 for iteration in range(30): with ti.ad.Tape(loss=loss): forward() # Executes simulation kernels grad = init_v.grad[None] init_v[None][0] -= learning_rate * grad[0] init_v[None][1] -= learning_rate * grad[1] ``` -------------------------------- ### Install DiffTaichi Dependencies Source: https://context7.com/taichi-dev/difftaichi/llms.txt Commands to install the Taichi framework and its required scientific and visualization libraries via pip. ```bash python3 -m pip install taichi pip install matplotlib numpy opencv-python scipy imageio torch torchvision cd examples python3 diffmpm.py ``` -------------------------------- ### Billiard Collision Simulator with Taichi Source: https://context7.com/taichi-dev/difftaichi/llms.txt A differentiable collision simulator for billiard balls using Taichi. This example optimizes the initial position and velocity of the cue ball to hit a target ball into a goal position, demonstrating gradient-based optimization through collision dynamics. Key parameters include elasticity, time step, and learning rate. ```python import taichi as ti import math import numpy as np real = ti.f32 ti.init(default_fp=real) max_steps = 2048 steps = 1024 billiard_layers = 4 n_balls = 1 + (1 + billiard_layers) * billiard_layers // 2 target_ball = n_balls - 1 goal = [0.9, 0.75] radius = 0.03 elasticity = 0.8 dt = 0.003 learning_rate = 0.01 ``` -------------------------------- ### GUI Initialization and Rendering Source: https://context7.com/taichi-dev/difftaichi/llms.txt Methods to initialize the simulation viewer window and render geometric primitives like circles and lines to represent physical states. ```APIDOC ## GUI Initialization ### Description Initializes a new Taichi GUI window for visualizing simulation frames. ### Method Constructor ### Endpoint ti.GUI(name, res, background_color) ### Parameters #### Path Parameters - **name** (string) - Required - The title of the window. - **res** (tuple) - Required - Resolution of the window (width, height). - **background_color** (hex) - Optional - Background color in hex format. ### Request Example gui = ti.GUI("Simulation Viewer", res=(640, 640), background_color=0xFFFFFF) ## Render Particles ### Description Draws a set of particles as circles on the current GUI frame. ### Method POST ### Endpoint gui.circles(pos, color, radius) ### Parameters #### Request Body - **pos** (numpy.ndarray) - Required - Array of particle positions. - **color** (hex/list) - Required - Color or list of colors for particles. - **radius** (float) - Required - Radius of the circles. ### Response - **Success** (void) - Renders the shapes to the internal buffer. ``` -------------------------------- ### Taichi GUI for Real-time Simulation Visualization Source: https://context7.com/taichi-dev/difftaichi/llms.txt This snippet demonstrates initializing Taichi and setting up a field for storing particle positions over time, intended for use with Taichi's GUI for real-time visualization. It requires the 'taichi' library. ```python import taichi as ti ti.init() n_particles = 1000 steps = 512 x = ti.Vector.field(2, dtype=ti.f32, shape=(steps, n_particles)) ``` -------------------------------- ### Create GUI Window with Taichi Source: https://context7.com/taichi-dev/difftaichi/llms.txt Initializes a graphical user interface window for visualizing simulations. It sets the window title, resolution, and background color. This is a prerequisite for displaying any visual elements. ```python gui = ti.GUI("Simulation Viewer", res=(640, 640), background_color=0xFFFFFF) ``` -------------------------------- ### Visualization with Taichi GUI Source: https://context7.com/taichi-dev/difftaichi/llms.txt Real-time visualization of simulations using Taichi's built-in GUI. ```APIDOC ## Taichi GUI Visualization ### Description Taichi provides a built-in GUI module that allows for real-time visualization of simulations. This is particularly useful for debugging and understanding the behavior of particle-based simulations created with the Scene class. ### Usage To use the Taichi GUI, you typically need to initialize Taichi, define your simulation fields (e.g., particle positions), and then create a window to display them. ### Code Example ```python import taichi as ti ti.init() # Define simulation parameters n_particles = 1000 steps = 512 # Create a field to store particle positions over time # x is a 2D vector field, storing 2D positions for each particle at each time step x = ti.Vector.field(2, dtype=ti.f32, shape=(steps, n_particles)) # --- Simulation logic would go here --- # For example, updating particle positions in a loop: # for t in range(steps): # for i in range(n_particles): # # Update x[t, i] based on physics simulation # pass # --- GUI Setup --- # Create a window for visualization # The window size can be adjusted as needed window = ti.ui.Window('MPM Simulation', size=(512, 512)) canvas = window.canvas # --- Rendering Loop --- # This loop would typically run as part of your simulation update # For demonstration, let's assume we are rendering the last frame current_frame = steps - 1 # Clear the canvas for the new frame canvas.clear(0x333333) # Clear with a dark grey color # Draw particles # You can customize the color and size of the particles # Here, we draw each particle as a small circle # The 'radius' parameter controls the size of the drawn circle # The 'color' parameter is an RGBA integer canvas.particles(x[current_frame], radius=1.5, color=0x00FF00) # Green particles # Update the window to show the rendered frame window.show() # Note: In a real application, the rendering would be inside your main simulation loop # to update the visualization in real-time. ``` ### Parameters - **`ti.init()`**: Initializes the Taichi runtime. Should be called once at the beginning. - **`ti.Vector.field(dim, dtype, shape)`**: Creates a Taichi field for storing vectors. `dim` is the vector dimension (e.g., 2 for 2D), `dtype` is the data type (e.g., `ti.f32`), and `shape` defines the dimensions of the field. - **`ti.ui.Window(title, size)`**: Creates a GUI window for rendering. `title` is the window's title bar text, and `size` is a tuple `(width, height)` in pixels. - **`window.canvas`**: Provides access to the drawing canvas of the window. - **`canvas.clear(color)`**: Clears the canvas with a specified color (RGBA integer format). - **`canvas.particles(field, radius, color)`**: Draws particles from a given field. `field` should be a Taichi field containing particle positions. `radius` controls the size of the drawn particles, and `color` sets the particle color. - **`window.show()`**: Updates the window to display the contents drawn on the canvas. ``` -------------------------------- ### Training Loop with Gradient Clipping Source: https://context7.com/taichi-dev/difftaichi/llms.txt Executes the optimization process using Taichi's automatic differentiation tape. It includes loss calculation, gradient computation, and manual weight updates with gradient clipping. ```python for iter in range(100): with ti.ad.Tape(loss): forward() total_norm = 0.0 # ... (gradient calculation logic) ... clip = 0.2 scale = min(1.0, clip / (math.sqrt(total_norm) + 1e-6)) for i in range(n_hidden): for j in range(n_input_states()): weights1[i, j] -= scale * learning_rate * weights1.grad[i, j] bias1[i] -= scale * learning_rate * bias1.grad[i] ``` -------------------------------- ### Initialize Taichi Runtime Source: https://context7.com/taichi-dev/difftaichi/llms.txt Configures the Taichi runtime environment, specifying the backend architecture (CPU/GPU), floating-point precision, and memory limits. This must be invoked before defining any fields or kernels. ```python import taichi as ti # Initialize with GPU backend and 32-bit floats real = ti.f32 tie.init(default_fp=real, arch=ti.gpu, flatten_if=True) # For CPU-only execution tie.init(default_fp=ti.f32, arch=ti.cpu) # With GPU memory limit (for large simulations) tie.init(default_fp=real, arch=ti.cuda, device_memory_GB=4.5) ``` -------------------------------- ### Scene Configuration API Source: https://context7.com/taichi-dev/difftaichi/llms.txt Utilities for creating rectangular particle regions with different material types and actuator assignments for MPM simulations. ```APIDOC ## Scene Class ### Description The Scene class provides utilities for creating rectangular particle regions with different material types and actuator assignments, enabling the construction of soft robots and complex scenes for MPM simulations. ### Methods - **`__init__(self)`**: Initializes a new Scene object with empty particle data and default offsets. - **`new_actuator(self)`**: Creates a new actuator and returns its ID. Each actuator must have a unique ID. - **`set_offset(self, x, y)`**: Sets a position offset for all subsequent shapes added to the scene. This is useful for positioning elements relative to a common origin. - **`add_rect(self, x, y, w, h, actuation, ptype=1, dx=1/128)`**: Adds a rectangular region of particles to the scene. - **`x, y`** (float): The position of the bottom-left corner of the rectangle. - **`w, h`** (float): The width and height of the rectangle. - **`actuation`** (int): The actuator ID for the particles. Use -1 for passive particles. - **`ptype`** (int, optional): The particle type. 0 for fluid, 1 for solid. Defaults to 1 (solid). - **`dx`** (float, optional): The grid spacing used to determine particle density. Defaults to 1/128. - **`finalize(self)`**: Finalizes the scene construction and returns a dictionary containing particle data, including positions, actuator IDs, particle types, and counts. ### Request Body Example (Conceptual - This is a Python class) ```python # Initialization scene = Scene() # Setting an offset scene.set_offset(0.1, 0.03) # Adding a passive solid rectangle scene.add_rect(x=0.0, y=0.1, w=0.3, h=0.1, actuation=-1, ptype=1) # Adding an actuated rectangle (leg) leg_actuator_id = scene.new_actuator() scene.add_rect(x=0.0, y=0.0, w=0.05, h=0.1, actuation=leg_actuator_id) # Finalizing the scene to get particle data particle_data = scene.finalize() ``` ### Response Example (Conceptual - This is a Python class) ```python # Output of scene.finalize() { 'x': numpy.ndarray, # Array of particle positions (N, 2) 'actuator_id': numpy.ndarray, # Array of actuator IDs (N,) 'particle_type': numpy.ndarray, # Array of particle types (N,) 'n_particles': int, # Total number of particles 'n_solid_particles': int, # Number of solid particles 'n_actuators': int # Number of actuators created } ``` ``` -------------------------------- ### Visualize Simulation Steps with Taichi Source: https://context7.com/taichi-dev/difftaichi/llms.txt Renders simulation frames at specified intervals. It retrieves particle positions, draws them as circles, adds a ground line, and marks a target position. The visualization can be displayed live or saved to a file. ```python def visualize_simulation(): for t in range(1, steps): # ... run simulation step ... # Visualize every 16 frames if t % 16 == 0: # Get particle positions as numpy array particles = x.to_numpy()[t] # Draw particles as circles gui.circles(pos=particles, color=0x112233, radius=3) # Draw ground line gui.line(begin=(0.0, 0.1), end=(1.0, 0.1), color=0x000000, radius=2) # Draw target position gui.circle(pos=(0.8, 0.2), color=0xFF0000, radius=10) # Show frame (use gui.show('filename.png') to save) gui.show() ``` -------------------------------- ### Mass-Spring System Simulator with Taichi Source: https://context7.com/taichi-dev/difftaichi/llms.txt A differentiable mass-spring system for soft body simulation. It models interconnected mass points with springs and uses gradient-based optimization to learn spring rest lengths for a target configuration. Dependencies include Taichi, math, numpy, and matplotlib. ```python import taichi as ti import math import numpy as np import matplotlib.pyplot as plt real = ti.f32 ti.init(default_fp=real) max_steps = 1024 steps = 1024 n_objects = 3 n_springs = 3 mass = 1 spring_stiffness = 10 damping = 20 dt = 0.001 learning_rate = 5 # Define fields x = ti.Vector.field(2, dtype=real, shape=(max_steps, n_objects), needs_grad=True) v = ti.Vector.field(2, dtype=real, shape=(max_steps, n_objects), needs_grad=True) force = ti.Vector.field(2, dtype=real, shape=(max_steps, n_objects), needs_grad=True) spring_anchor_a = ti.field(ti.i32, shape=n_springs) spring_anchor_b = ti.field(ti.i32, shape=n_springs) spring_length = ti.field(dtype=real, shape=n_springs, needs_grad=True) loss = ti.field(dtype=real, shape=(), needs_grad=True) @ti.kernel def apply_spring_force(t: ti.i32): for i in range(n_springs): a, b = spring_anchor_a[i], spring_anchor_b[i] x_a, x_b = x[t - 1, a], x[t - 1, b] dist = x_a - x_b length = dist.norm() + 1e-4 # Hooke's law: F = k * (length - rest_length) * direction F = (length - spring_length[i]) * spring_stiffness * dist / length # Apply equal and opposite forces (Newton's 3rd law) ti.atomic_add(force[t, a], -F) ti.atomic_add(force[t, b], F) @ti.kernel def time_integrate(t: ti.i32): for i in range(n_objects): # Velocity damping s = math.exp(-dt * damping) new_v = s * v[t - 1, i] + dt * force[t, i] / mass new_x = x[t - 1, i] + dt * new_v # Simple collision with right wall if new_x[0] > 0.4 and new_v[0] > 0: new_v[0] = 0 v[t, i] = new_v x[t, i] = new_x @ti.kernel def compute_loss(t: ti.i32): # Compute triangle area formed by the 3 masses x01 = x[t, 0] - x[t, 1] x02 = x[t, 0] - x[t, 2] area = ti.abs(0.5 * (x01[0] * x02[1] - x01[1] * x02[0])) target_area = 0.1 loss[None] = (area - target_area)**2 @ti.kernel def clear_forces(): for t in range(max_steps): for i in range(n_objects): force[t, i] = ti.Vector([0.0, 0.0]) def forward(): for t in range(1, steps): apply_spring_force(t) time_integrate(t) compute_loss(steps - 1) # Initialize mass positions (triangle) x[0, 0] = [0.3, 0.5] x[0, 1] = [0.3, 0.4] x[0, 2] = [0.4, 0.4] # Initialize springs connecting the masses spring_anchor_a[0], spring_anchor_b[0], spring_length[0] = 0, 1, 0.1 spring_anchor_a[1], spring_anchor_b[1], spring_length[1] = 1, 2, 0.1 spring_anchor_a[2], spring_anchor_b[2], spring_length[2] = 2, 0, 0.1 * math.sqrt(2) # Optimization loop losses = [] for iter in range(25): clear_forces() with ti.ad.Tape(loss): forward() print(f'Iter={iter}, Loss={loss[None]:.6f}') losses.append(loss[None]) # Update spring rest lengths via gradient descent for i in range(n_springs): spring_length[i] -= learning_rate * spring_length.grad[i] # Print optimized spring lengths for i in range(n_springs): print(f'Spring {i}: rest_length = {spring_length[i]:.4f}') plt.plot(losses) plt.xlabel('Gradient descent iterations') plt.ylabel('Loss') plt.title('Spring Rest Length Optimization') plt.show() ``` -------------------------------- ### Define Differentiable GPU Kernels with Taichi Source: https://context7.com/taichi-dev/difftaichi/llms.txt Demonstrates how to create parallel kernels for particle-to-grid (P2G) and grid-to-particle (G2P) transfers. These kernels use Taichi fields with needs_grad=True to enable automatic gradient computation during simulation steps. ```python import taichi as ti import math ti.init(default_fp=ti.f32, arch=ti.gpu) n_particles = 3600 n_grid = 120 dx = 1 / n_grid inv_dx = n_grid dt = 3e-4 gravity = 9.8 x = ti.Vector.field(2, dtype=ti.f32, shape=(1024, n_particles), needs_grad=True) v = ti.Vector.field(2, dtype=ti.f32, shape=(1024, n_particles), needs_grad=True) grid_v = ti.Vector.field(2, dtype=ti.f32, shape=(1024, n_grid, n_grid), needs_grad=True) grid_m = ti.field(dtype=ti.f32, shape=(1024, n_grid, n_grid), needs_grad=True) @ti.kernel def p2g(f: ti.i32): for p in range(n_particles): base = ti.cast(x[f, p] * inv_dx - 0.5, ti.i32) fx = x[f, p] * inv_dx - ti.cast(base, ti.i32) w = [0.5 * (1.5 - fx)**2, 0.75 - (fx - 1)**2, 0.5 * (fx - 0.5)**2] for i in ti.static(range(3)): for j in ti.static(range(3)): offset = ti.Vector([i, j]) weight = w[i][0] * w[j][1] grid_v[f, base + offset] += weight * v[f, p] grid_m[f, base + offset] += weight @ti.kernel def g2p(f: ti.i32): for p in range(n_particles): base = ti.cast(x[f, p] * inv_dx - 0.5, ti.i32) fx = x[f, p] * inv_dx - ti.cast(base, ti.f32) w = [0.5 * (1.5 - fx)**2, 0.75 - (fx - 1.0)**2, 0.5 * (fx - 0.5)**2] new_v = ti.Vector([0.0, 0.0]) for i in ti.static(range(3)): for j in ti.static(range(3)): weight = w[i][0] * w[j][1] new_v += weight * grid_v[f, base[0] + i, base[1] + j] v[f + 1, p] = new_v x[f + 1, p] = x[f, p] + dt * new_v ``` -------------------------------- ### MPM Elastic Object Simulation in Taichi Source: https://context7.com/taichi-dev/difftaichi/llms.txt This Python script implements a 2D MPM simulator for elastic materials with a Neo-Hookean model. It initializes particles, defines simulation parameters, and uses Taichi's kernels for P2G, grid operations, and G2P transfers. The simulation optimizes initial velocity to reach a target position using automatic differentiation. ```Python import taichi as ti import numpy as np import matplotlib.pyplot as plt real = ti.f32 ti.init(arch=ti.cuda, default_fp=real) # Simulation parameters dim = 2 N = 60 n_particles = N * N n_grid = 120 dx = 1 / n_grid inv_dx = n_grid dt = 3e-4 p_mass = 1 p_vol = 1 E = 100 # Young's modulus mu = E # Shear modulus la = E # Lame's first parameter steps = 1024 gravity = 9.8 target = [0.3, 0.6] # Define fields x = ti.Vector.field(dim, dtype=real, shape=(steps, n_particles), needs_grad=True) v = ti.Vector.field(dim, dtype=real, shape=(steps, n_particles), needs_grad=True) C = ti.Matrix.field(dim, dim, dtype=real, shape=(steps, n_particles), needs_grad=True) F = ti.Matrix.field(dim, dim, dtype=real, shape=(steps, n_particles), needs_grad=True) grid_v_in = ti.Vector.field(dim, dtype=real, shape=(steps, n_grid, n_grid), needs_grad=True) grid_v_out = ti.Vector.field(dim, dtype=real, shape=(steps, n_grid, n_grid), needs_grad=True) grid_m = ti.field(dtype=real, shape=(steps, n_grid, n_grid), needs_grad=True) init_v = ti.Vector.field(dim, dtype=real, shape=(), needs_grad=True) x_avg = ti.Vector.field(dim, dtype=real, shape=(), needs_grad=True) loss = ti.field(dtype=real, shape=(), needs_grad=True) @ti.kernel def p2g(f: ti.i32): for p in range(n_particles): base = ti.cast(x[f, p] * inv_dx - 0.5, ti.i32) fx = x[f, p] * inv_dx - ti.cast(base, ti.i32) w = [0.5 * (1.5 - fx)**2, 0.75 - (fx - 1)**2, 0.5 * (fx - 0.5)**2] # Update deformation gradient new_F = (ti.Matrix.diag(dim=2, val=1) + dt * C[f, p]) @ F[f, p] F[f + 1, p] = new_F J = new_F.determinant() # Neo-Hookean stress computation r, s = ti.polar_decompose(new_F) cauchy = 2 * mu * (new_F - r) @ new_F.transpose() + \ ti.Matrix.diag(2, la * (J - 1) * J) stress = -(dt * p_vol * 4 * inv_dx * inv_dx) * cauchy affine = stress + p_mass * C[f, p] # Scatter to grid for i, j in ti.static(ti.ndrange(3, 3)): offset = ti.Vector([i, j]) dpos = (ti.cast(ti.Vector([i, j]), real) - fx) * dx weight = w[i][0] * w[j][1] grid_v_in[f, base + offset] += weight * (p_mass * v[f, p] + affine @ dpos) grid_m[f, base + offset] += weight * p_mass @ti.kernel def grid_op(f: ti.i32): for i, j in ti.ndrange(n_grid, n_grid): inv_m = 1 / (grid_m[f, i, j] + 1e-10) v_out = inv_m * grid_v_in[f, i, j] v_out[1] -= dt * gravity # Boundary conditions bound = 3 if i < bound and v_out[0] < 0: v_out[0] = 0 if i > n_grid - bound and v_out[0] > 0: v_out[0] = 0 if j < bound and v_out[1] < 0: v_out[1] = 0 if j > n_grid - bound and v_out[1] > 0: v_out[1] = 0 grid_v_out[f, i, j] = v_out @ti.kernel def g2p(f: ti.i32): for p in range(n_particles): base = ti.cast(x[f, p] * inv_dx - 0.5, ti.i32) fx = x[f, p] * inv_dx - ti.cast(base, real) w = [0.5 * (1.5 - fx)**2, 0.75 - (fx - 1.0)**2, 0.5 * (fx - 0.5)**2] new_v = ti.Vector([0.0, 0.0]) new_C = ti.Matrix([[0.0, 0.0], [0.0, 0.0]]) for i, j in ti.static(ti.ndrange(3, 3)): dpos = ti.cast(ti.Vector([i, j]), real) - fx g_v = grid_v_out[f, base[0] + i, base[1] + j] weight = w[i][0] * w[j][1] new_v += weight * g_v new_C += 4 * weight * g_v.outer_product(dpos) * inv_dx v[f + 1, p] = new_v x[f + 1, p] = x[f, p] + dt * new_v C[f + 1, p] = new_C @ti.kernel def compute_loss(): for i in range(n_particles): x_avg[None] += (1.0 / n_particles) * x[steps - 1, i] dist = (x_avg[None] - ti.Vector(target))**2 loss[None] = 0.5 * (dist[0] + dist[1]) # Initialize particles in a grid pattern for i in range(N): for j in range(N): x[0, i * N + j] = [dx * (i * 0.7 + 10), dx * (j * 0.7 + 25)] F[0, i * N + j] = [[1, 0], [0, 1]] # Optimization init_v[None] = [0, 0] losses = [] for iter in range(30): grid_v_in.fill(0) grid_m.fill(0) x_avg[None] = [0, 0] with ti.ad.Tape(loss=loss): for s in range(steps - 1): p2g(s) grid_op(s) g2p(s) compute_loss() losses.append(loss[None]) grad = init_v.grad[None] print(f'Iter {iter}: loss={loss[None]:.6f}') learning_rate = 10 init_v[None][0] -= learning_rate * grad[0] init_v[None][1] -= learning_rate * grad[1] ``` -------------------------------- ### Hierarchical Field Allocation Source: https://context7.com/taichi-dev/difftaichi/llms.txt Uses ti.root for structured memory layout and lazy_grad() to automatically generate gradient fields for complex simulations. ```python import taichi as ti tie.init(default_fp=ti.f32) # ... field declarations ... def allocate_fields(): # Neural network weights: (n_actuators, n_sin_waves) ti.root.dense(ti.ij, (n_actuators, n_sin_waves)).place(weights) ti.root.dense(ti.i, n_actuators).place(bias) # Time-indexed particle data: (max_steps, n_particles) ti.root.dense(ti.k, max_steps).dense(ti.l, n_particles).place(x, v, C, F) # Grid data: (n_grid, n_grid) ti.root.dense(ti.ij, n_grid).place(grid_v_in, grid_m_in, grid_v_out) # Scalar loss ti.root.place(loss) # Automatically create gradient fields for all placed fields ti.root.lazy_grad() allocate_fields() ``` -------------------------------- ### Wave Equation Simulator using FDTD in Taichi Source: https://context7.com/taichi-dev/difftaichi/llms.txt This Taichi code implements a differentiable height-field water wave simulator using the Finite-Difference Time-Domain (FDTD) method. It's designed to optimize an initial wave pattern to match a target image after wave propagation. The simulation includes parameters for grid size, wave speed, damping, and time step calculation based on the CFL condition. ```Python import taichi as ti import math import numpy as np import cv2 real = ti.f32 ti.init(default_fp=real, arch=ti.cuda) n_grid = 256 dx = 1 / n_grid inv_dx = n_grid max_steps = 512 steps = 256 c = 340 # Wave speed alpha = 0.0 # Damping coefficient learning_rate = 1.0 # Time step based on CFL condition dt = (math.sqrt(alpha * alpha + dx * dx / 3) - alpha) / c ``` -------------------------------- ### Taichi FDTD 2D Wave Equation Solver Source: https://context7.com/taichi-dev/difftaichi/llms.txt Implements a 2D wave equation solver using the finite difference time domain (FDTD) method in Taichi. It defines fields for simulation state, boundary conditions, and loss calculation, and includes functions for initialization, time stepping, and gradient application. ```python p = ti.field(dtype=real, shape=(max_steps, n_grid, n_grid), needs_grad=True) target = ti.field(dtype=real, shape=(n_grid, n_grid), needs_grad=True) initial = ti.field(dtype=real, shape=(n_grid, n_grid), needs_grad=True) loss = ti.field(dtype=real, shape=(), needs_grad=True) @ti.func def get_p(t, i, j): # Boundary handling: zero outside domain return p[t, i, j] if 0 <= i < n_grid and 0 <= j < n_grid else 0.0 @ti.func def laplacian(t, i, j): # 5-point stencil Laplacian inv_dx2 = inv_dx * inv_dx return inv_dx2 * (-4 * get_p(t, i, j) + get_p(t, i, j - 1) + get_p(t, i, j + 1) + get_p(t, i + 1, j) + get_p(t, i - 1, j)) @ti.kernel def initialize(): for i, j in ti.ndrange(n_grid, n_grid): p[0, i, j] = initial[i, j] p[1, i, j] = initial[i, j] @ti.kernel def fdtd(t: ti.i32): """2D wave equation: d²p/dt² = c² * ∇²p""" for i, j in ti.ndrange(n_grid, n_grid): laplacian_p = laplacian(t - 2, i, j) laplacian_q = laplacian(t - 1, i, j) # Second-order finite difference in time with damping p[t, i, j] = (2 * p[t - 1, i, j] + (c * c * dt * dt + c * alpha * dt) * laplacian_q - p[t - 2, i, j] - c * alpha * dt * laplacian_p) @ti.kernel def compute_loss(t: ti.i32): for i, j in ti.ndrange(n_grid, n_grid): loss[None] += dx * dx * (target[i, j] - p[t, i, j])**2 @ti.kernel def apply_grad(): for i, j in initial.grad: initial[i, j] -= learning_rate * initial.grad[i, j] def forward(): initialize() for t in range(2, steps): fdtd(t) compute_loss(steps - 1) # Load target image target_img = cv2.imread('taichi.png', cv2.IMREAD_GRAYSCALE) / 255.0 target_img -= target_img.mean() target_img = cv2.resize(target_img, (n_grid, n_grid)) @ti.kernel def fill_target(img: ti.types.ndarray()): for i, j in ti.ndrange(n_grid, n_grid): target[i, j] = float(img[i, j]) fill_target(target_img.astype(np.float32)) # Optimization for opt in range(200): with ti.ad.Tape(loss): forward() print(f'Iter {opt}, Loss = {loss[None]:.6f}') apply_grad() ``` -------------------------------- ### Taichi Scene Class for MPM Simulations Source: https://context7.com/taichi-dev/difftaichi/llms.txt The Scene class facilitates the creation of rectangular particle regions for MPM simulations. It supports different material types and actuator assignments, enabling the construction of soft robots and complex scenes. It requires the 'taichi' and 'numpy' libraries. ```python import taichi as ti import numpy as np class Scene: def __init__(self): self.n_particles = 0 self.n_solid_particles = 0 self.x = [] self.actuator_id = [] self.particle_type = [] self.offset_x = 0 self.offset_y = 0 self.num_actuators = 0 def new_actuator(self): """Create a new actuator and return its ID""" self.num_actuators += 1 return self.num_actuators - 1 def set_offset(self, x, y): """Set position offset for subsequent shapes""" self.offset_x = x self.offset_y = y def add_rect(self, x, y, w, h, actuation, ptype=1, dx=1/128): """ Add a rectangular region of particles. Args: x, y: Bottom-left corner position w, h: Width and height actuation: Actuator ID (-1 for passive) ptype: 0=fluid, 1=solid dx: Grid spacing """ if ptype == 0: assert actuation == -1, "Fluid cannot be actuated" w_count = int(w / dx) * 2 h_count = int(h / dx) * 2 real_dx = w / w_count real_dy = h / h_count for i in range(w_count): for j in range(h_count): self.x.append([ x + (i + 0.5) * real_dx + self.offset_x, y + (j + 0.5) * real_dy + self.offset_y ]) self.actuator_id.append(actuation) self.particle_type.append(ptype) self.n_particles += 1 self.n_solid_particles += int(ptype == 1) def finalize(self): """Finalize scene and return particle data""" return { 'x': np.array(self.x, dtype=np.float32), 'actuator_id': np.array(self.actuator_id, dtype=np.int32), 'particle_type': np.array(self.particle_type, dtype=np.int32), 'n_particles': self.n_particles, 'n_solid_particles': self.n_solid_particles, 'n_actuators': self.num_actuators } ``` ```python # Example: Build a walking robot def build_robot(): scene = Scene() scene.set_offset(0.1, 0.03) # Body (passive solid) scene.add_rect(0.0, 0.1, 0.3, 0.1, actuation=-1, ptype=1) # Legs with individual actuators scene.add_rect(0.0, 0.0, 0.05, 0.1, actuation=scene.new_actuator()) scene.add_rect(0.05, 0.0, 0.05, 0.1, actuation=scene.new_actuator()) scene.add_rect(0.2, 0.0, 0.05, 0.1, actuation=scene.new_actuator()) scene.add_rect(0.25, 0.0, 0.05, 0.1, actuation=scene.new_actuator()) return scene.finalize() # Example: Build a fish with fluid def build_fish(): scene = Scene() # Water pool (fluid) scene.add_rect(0.025, 0.025, 0.95, 0.1, actuation=-1, ptype=0) # Fish body (passive solid) scene.add_rect(0.1, 0.2, 0.15, 0.05, actuation=-1, ptype=1) # Fish fins (actuated) scene.add_rect(0.1, 0.15, 0.025, 0.05, actuation=scene.new_actuator()) scene.add_rect(0.125, 0.15, 0.025, 0.05, actuation=scene.new_actuator()) scene.add_rect(0.2, 0.15, 0.025, 0.05, actuation=scene.new_actuator()) scene.add_rect(0.225, 0.15, 0.025, 0.05, actuation=scene.new_actuator()) return scene.finalize() # Usage robot_data = build_robot() print(f"Robot: {robot_data['n_particles']} particles, {robot_data['n_actuators']} actuators") fish_data = build_fish() print(f"Fish: {fish_data['n_particles']} particles, {fish_data['n_actuators']} actuators") ``` -------------------------------- ### Taichi Neural Network Controller for Soft Robots Source: https://context7.com/taichi-dev/difftaichi/llms.txt Demonstrates training a neural network to control soft robots using Taichi's differentiable programming capabilities. It defines network architecture, simulation fields, and spring mechanics for end-to-end training. ```python import taichi as ti import math import numpy as np real = ti.f32 ti.init(default_fp=real) n_objects = 10 n_springs = 20 n_sin_waves = 10 n_hidden = 32 max_steps = 4096 steps = 2048 spring_omega = 10 dt = 0.004 # Network architecture: n_input -> n_hidden -> n_springs def n_input_states(): return n_sin_waves + 4 * n_objects + 2 # sin waves + positions/velocities + goal # Simulation fields x = ti.Vector.field(2, dtype=real, shape=(max_steps, n_objects), needs_grad=True) v = ti.Vector.field(2, dtype=real, shape=(max_steps, n_objects), needs_grad=True) center = ti.Vector.field(2, dtype=real, shape=max_steps, needs_grad=True) goal = ti.Vector.field(2, dtype=real, shape=(), needs_grad=True) loss = ti.field(dtype=real, shape=(), needs_grad=True) # Spring fields spring_anchor_a = ti.field(ti.i32, shape=n_springs) spring_anchor_b = ti.field(ti.i32, shape=n_springs) spring_length = ti.field(dtype=real, shape=n_springs) spring_stiffness = ti.field(dtype=real, shape=n_springs) spring_actuation = ti.field(dtype=real, shape=n_springs) # Neural network weights weights1 = ti.field(dtype=real, shape=(n_hidden, n_input_states()), needs_grad=True) bias1 = ti.field(dtype=real, shape=n_hidden, needs_grad=True) weights2 = ti.field(dtype=real, shape=(n_springs, n_hidden), needs_grad=True) bias2 = ti.field(dtype=real, shape=n_springs, needs_grad=True) ``` -------------------------------- ### Dynamic Visualization Source: https://context7.com/taichi-dev/difftaichi/llms.txt Methods for mapping simulation data to visual properties, such as color-coding particles based on actuation values. ```APIDOC ## visualize_with_color_mapping ### Description Maps simulation actuation values to RGB colors for particle visualization. ### Method GET ### Endpoint visualize_with_color_mapping(t, actuations) ### Parameters #### Query Parameters - **t** (int) - Required - Current time step index. - **actuations** (numpy.ndarray) - Required - Actuation data array for color mapping. ### Response - **Success** (void) - Updates the GUI display with color-mapped particles. ``` -------------------------------- ### Convert Taichi Fields to NumPy Arrays Source: https://context7.com/taichi-dev/difftaichi/llms.txt Demonstrates how to convert Taichi fields into NumPy arrays for integration with other libraries. This is crucial for using data from DiffTaichi simulations in frameworks like PyTorch or TensorFlow. ```python particles = x.to_numpy()[t] ``` -------------------------------- ### Visualize Particles with Color Mapping in Taichi Source: https://context7.com/taichi-dev/difftaichi/llms.txt Renders particles with colors determined by actuation values. It calculates RGB color components based on the actuation of each particle and converts them to a hex color format for display. This allows for visualizing the influence of control inputs. ```python def visualize_with_color_mapping(t, actuations): """Visualize with actuation-based coloring""" particles = x.to_numpy()[t] colors = [] for i in range(n_particles): # Color based on actuation value act = actuations[t, i] if i < len(actuations[t]) else 0 r = 0.5 - act * 0.5 g = 0.5 - abs(act) * 0.5 b = 0.5 + act * 0.5 colors.append(ti.rgb_to_hex((r, g, b))) gui.circles(pos=particles, color=colors, radius=2) gui.show() ``` -------------------------------- ### Physics Simulation Kernels Source: https://context7.com/taichi-dev/difftaichi/llms.txt Implements the physics engine components including spring force application and object movement with gravity and ground collision. ```python @ti.kernel def apply_spring_force(t: ti.i32): for i in range(n_springs): a = spring_anchor_a[i] b = spring_anchor_b[i] dist = x[t, a] - x[t, b] length = dist.norm() + 1e-4 target_length = spring_length[i] * (1.0 + spring_actuation[i] * act[t, i]) impulse = dt * (length - target_length) * spring_stiffness[i] / length * dist v[t + 1, a] += -impulse v[t + 1, b] += impulse @ti.kernel def advance(t: ti.i32): gravity = ti.Vector([0.0, -4.8]) damping = 15.0 ground_height = 0.1 for i in range(n_objects): s = math.exp(-dt * damping) new_v = s * v[t - 1, i] + dt * gravity new_x = x[t - 1, i] + dt * new_v if new_x[1] < ground_height and new_v[1] < 0: new_v = ti.Vector([0.0, 0.0]) v[t, i] = new_v x[t, i] = new_x ```