def polar(): R, S = ti.polar_decompose(m[None], dt) r[None] = R s[None] = S m[None] = R @ S I[None] = R @ ti.transposed(R) D[None] = S - ti.transposed(S)
def p2g(f: ti.i32): for p in range(0, n_particles): new_Jp = Jp[f, p] 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 * ti.sqr(1.5 - fx), 0.75 - ti.sqr(fx - 1.0), 0.5 * ti.sqr(fx - 0.5) ] # quadratic kernels new_F = (ti.Matrix.diag(dim=dim, val=1) + dt * C[f, p]) @ F[f, p] # deformation gradient update h = max(0.1, min(5, ti.exp(10 * (1.0 - new_Jp)))) if particle_type[p] == 1: # jelly, make it softer h = 0.3 mu, la = mu_0 * h, lambda_0 * h if particle_type[p] == 0: # liquid mu = 0.0 U, sig, V = ti.svd(new_F) J = 1.0 for d in ti.static(range(2)): new_sig = sig[d, d] if particle_type[p] == 2: # Snow new_sig = min(max(sig[d, d], 1 - 2.5e-2), 1 + 4.5e-3) # Plasticity new_Jp *= sig[d, d] / new_sig sig[d, d] = new_sig J *= new_sig if particle_type[ p] == 0: # Reset deformation gradient to avoid numerical instability new_F = ti.Matrix.diag(dim=dim, val=1) * ti.sqrt(J) elif particle_type[p] == 2: new_F = U @ sig @ ti.transposed( V) # Reconstruct elastic deformation gradient after plasticity F[f + 1, p] = new_F Jp[f + 1, p] = new_Jp stress = 2 * mu * (new_F - U @ ti.transposed(V)) @ ti.transposed( new_F) + ti.Matrix.diag(dim=dim, val=1) * la * J * (J - 1) stress = (-dt * p_vol * 4 * inv_dx * inv_dx) * stress affine = stress + p_mass * C[f, p] # loop over 3x3 node neighborhood for i in ti.static(range(3)): for j in ti.static(range(3)): offset = ti.Vector([i, j]) dpos = (ti.cast(ti.Vector([i, j]), ti.f32) - fx) * dx weight = w[i](0) * w[j](1) ti.atomic_add(grid_v_in[base + offset], weight * (p_mass * v[f, p] + affine @ dpos)) ti.atomic_add(grid_m[base + offset], weight * p_mass)
def p2g(f: ti.i32): for p in range(0, 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 * ti.sqr(1.5 - fx), 0.75 - ti.sqr(fx - 1), 0.5 * ti.sqr(fx - 0.5) ] new_F = (ti.Matrix.diag(dim=dim, val=1) + dt * C[f, p]) @ F[f, p] J = ti.determinant(new_F) if particle_type[p] == 0: # fluid sqrtJ = ti.sqrt(J) # TODO: need pow(x, 1/3) new_F = ti.Matrix([[sqrtJ, 0, 0], [0, sqrtJ, 0], [0, 0, 1]]) F[f + 1, p] = new_F # r, s = ti.polar_decompose(new_F) act_id = actuator_id[p] act = actuation[f, ti.max(0, act_id)] * act_strength if act_id == -1: act = 0.0 # ti.print(act) A = ti.Matrix([[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 1.0] ]) * act cauchy = ti.Matrix(zero_matrix()) mass = 0.0 ident = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]] if particle_type[p] == 0: mass = 4 cauchy = ti.Matrix(ident) * (J - 1) * E else: mass = 1 cauchy = mu * (new_F @ ti.transposed(new_F)) + ti.Matrix(ident) * ( la * ti.log(J) - mu) cauchy += new_F @ A @ ti.transposed(new_F) stress = -(dt * p_vol * 4 * inv_dx * inv_dx) * cauchy affine = stress + mass * C[f, p] for i in ti.static(range(3)): for j in ti.static(range(3)): for k in ti.static(range(3)): offset = ti.Vector([i, j, k]) dpos = (ti.cast(ti.Vector([i, j, k]), real) - fx) * dx weight = w[i](0) * w[j](1) * w[k](2) grid_v_in[base + offset].atomic_add( weight * (mass * v[f, p] + affine @ dpos)) grid_m_in[base + offset].atomic_add(weight * mass)
def grid_op(): for i, j in grid_m_in: inv_m = 1 / (grid_m_in[i, j] + 1e-10) v_out = inv_m * grid_v_in[i, j] v_out[1] -= dt * gravity if i < bound and v_out[0] < 0: v_out[0] = 0 v_out[1] = 0 if i > n_grid - bound and v_out[0] > 0: v_out[0] = 0 v_out[1] = 0 if j < bound and v_out[1] < 0: v_out[0] = 0 v_out[1] = 0 normal = ti.Vector([0.0, 1.0]) lsq = ti.sqr(normal).sum() if lsq > 0.5: if ti.static(coeff < 0): v_out(0).val = 0 v_out(1).val = 0 else: lin = (ti.transposed(v_out) @ normal)(0) if lin < 0: vit = v_out - lin * normal lit = vit.norm() + 1e-10 if lit + coeff * lin <= 0: v_out(0).val = 0 v_out(1).val = 0 else: v_out = (1 + coeff * lin / lit) * vit if j > n_grid - bound and v_out[1] > 0: v_out[0] = 0 v_out[1] = 0 grid_v_out[i, j] = v_out
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 * ti.sqr(1.5 - fx), 0.75 - ti.sqr(fx - 1), 0.5 * ti.sqr(fx - 0.5)] new_F = (ti.Matrix.diag(dim=2, val=1) + dt * C[f, p]) @ F[f, p] J = ti.determinant(new_F) if particle_type[p] == 0: # fluid sqrtJ = ti.sqrt(J) new_F = ti.Matrix([[sqrtJ, 0], [0, sqrtJ]]) F[f + 1, p] = new_F r, s = ti.polar_decompose(new_F) act_id = actuator_id[p] act = actuation[f, ti.max(0, act_id)] * act_strength if act_id == -1: act = 0.0 # ti.print(act) A = ti.Matrix([[0.0, 0.0], [0.0, 1.0]]) * act cauchy = ti.Matrix([[0.0, 0.0], [0.0, 0.0]]) mass = 0.0 if particle_type[p] == 0: mass = 4 cauchy = ti.Matrix([[1.0, 0.0], [0.0, 0.1]]) * (J - 1) * E else: mass = 1 cauchy = 2 * mu * (new_F - r) @ ti.transposed(new_F) + \ ti.Matrix.diag(2, la * (J - 1) * J) cauchy += new_F @ A @ ti.transposed(new_F) stress = -(dt * p_vol * 4 * inv_dx * inv_dx) * cauchy affine = stress + mass * C[f, p] for i in ti.static(range(3)): for j in ti.static(range(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[base + offset] += weight * (mass * v[f, p] + affine @ dpos) grid_m_in[base + offset] += weight * mass
def p2g(f: ti.i32): for p in range(0, 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 * ti.sqr(1.5 - fx), 0.75 - ti.sqr(fx - 1), 0.5 * ti.sqr(fx - 0.5)] new_F = (ti.Matrix.diag(dim=2, val=1) + dt * C[f, p]) @ F[f, p] F[f + 1, p] = new_F J = ti.determinant(new_F) r, s = ti.polar_decompose(new_F) cauchy = 2 * mu * (new_F - r) @ ti.transposed(new_F) + \ 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] for i in ti.static(range(3)): for j in ti.static(range(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_in[f, base + offset] += weight * p_mass
def transpose(): mat = ti.transposed(m[None]) m[None] = mat
def run(): U[None], sigma[None], V[None] = ti.svd(A[None], dt) UtU[None] = ti.transposed(U[None]) @ U[None] VtV[None] = ti.transposed(V[None]) @ V[None] A_reconstructed[None] = U[None] @ sigma[None] @ ti.transposed(V[None])
def polar_decompose3d(A, dt): U, sig, V = ti.svd(A, dt) return U @ ti.transposed(V), V @ sig @ ti.transposed(V)