def compute_total_energy(): for i in range(n_elements): currentT = compute_T(i) F = currentT @ restT[i].inverse() # NeoHookean I1 = (F @ ti.Matrix.transposed(F)).trace() J = ti.Matrix.determinant(F) element_energy = 0.5 * mu * (I1 - 2) - mu * ti.log(J) + 0.5 * la * ti.log(J) ** 2 ti.atomic_add(total_energy[None], element_energy * 1e-3)
def compute_total_energy(): for i in range(n_elements): currentT = compute_T(i) F = currentT @ restT[i].inverse() # NeoHookean I1 = (F @ F.transpose()).trace() J = F.determinant() element_energy = 0.5 * mu * ( I1 - 2) - mu * ti.log(J) + 0.5 * la * ti.log(J)**2 total_energy[None] += E * element_energy * dx * dx
def compute_total_energy(): for i in range(n_elements): D = compute_D(i) F = D @ B[i] # NeoHookean I1 = (F @ F.transpose()).trace() J = max(0.2, F.determinant()) element_energy_density = 0.5 * mu * ( I1 - dim) - mu * ti.log(J) + 0.5 * la * ti.log(J)**2 total_energy[None] += element_energy_density * element_V
def GTR1(self, NDotH, a): ret = 1.0 / M_PIf if (a < 1.0): a2 = a * a t = 1.0 + (a2 - 1.0) * NDotH * NDotH ret = (a2 - 1.0) / (M_PIf * ti.log(a2) * t) return ret
def fill_color_s(sf: ti.template()): for i, j in sf: s = ti.log(sf[i, j] * 0.25 + 1.0) s3 = s * s * s color_buffer[i, j] = ti.Vector( [abs(1.5 * s), abs(1.5 * s3), abs(s3 * s3)])
def barrier_energy(self, p, q): rt = 0.0 d1, d2, d3 = 0.0, 0.0, 0.0 # points: p pt = self.node[p] # triangle t1 = self.node[self.element[q][0]] t2 = self.node[self.element[q][1]] t3 = self.node[self.element[q][2]] # point - triangle pair, find point - triangle shortest dist # 2d problem: point - line segment shortest dist ab1 = t2 - t1 ac1 = pt - t1 bc1 = pt - t2 a1 = ab1.dot(ac1) abd1 = ab1.norm_sqr() if a1 < 0: d1 = ac1.norm() elif a1 > abd1: d1 = bc1.norm() else: d1 = ti.abs((t2.y - t1.y) * pt.x - (t2.x - t1.x) * pt.y + t2.x * t1.y - t2.y * t1.x) / (t2 - t1).norm() ab2 = t3 - t2 ac2 = pt - t2 bc2 = pt - t3 a2 = ab2.dot(ac2) abd2 = ab2.norm_sqr() if a2 < 0: d2 = ac2.norm() elif a2 > abd2: d2 = bc2.norm() else: d2 = ti.abs((t3.y - t2.y) * pt.x - (t3.x - t2.x) * pt.y + t3.x * t2.y - t3.y * t2.x) / (t3 - t2).norm() ab3 = t1 - t3 ac3 = pt - t3 bc3 = pt - t1 a3 = ab3.dot(ac2) abd3 = ab3.norm_sqr() if a3 < 0: d3 = ac3.norm() elif a3 > abd3: d3 = bc3.norm() else: d3 = ti.abs((t1.y - t3.y) * pt.x - (t1.x - t3.x) * pt.y + t1.x * t3.y - t1.y * t3.x) / (t1 - t3).norm() dist = min(d1, d2, d3) # b_C2 function if dist < self.bar_d: rt = -self.k * ((dist - self.bar_d)**2) * ti.log(dist / self.bar_d) else: rt = 0 return rt
def test_unary(): import time t = time.time() grad_test(lambda x: ti.sqrt(x), lambda x: np.sqrt(x)) grad_test(lambda x: ti.exp(x), lambda x: np.exp(x)) grad_test(lambda x: ti.log(x), lambda x: np.log(x)) ti.core.print_profile_info() print("Total time {:.3f}s".format(time.time() - t))
def sampleScattering(u: ti.f32, maxDistance: ti.f32): # remap u to account for finite max distance ## f32 minU = ti.exp(-SIGMA * maxDistance) a = u * (1.0 - minU) + minU # sample with pdf proportional to exp(-sig*d) dist = -ti.log(a) / SIGMA pdf = SIGMA * a / (1.0 - minU) return (dist, pdf)
def log2(x): """Return the base 2 logarithm of `x`, so that if :math:`2^y=x`, then :math:`y=\\log2(x)`. This is equivalent to the `log2` function is GLSL. Args: x (:class:`~taichi.Matrix`): The input value. Returns: The base 2 logarithm of `x`. Example:: >>> v = ti.Vector([1., 2., 3.]) >>> ti.log2(x) [0.000000, 1.000000, 1.584962] """ return ti.log(x) / ti.static(ti.log(2.0))
def erfinv(x): sgn = -1 if x < 0 else 1 x = (1 - x) * (1 + x) lnx = ti.log(x) tt1 = 2 / (ti.pi * 0.147) + 0.5 * lnx tt2 = 1 / 0.147 * lnx return sgn * ti.sqrt(-tt1 + ti.sqrt(tt1**2 - tt2))
def compute_von_mises(self, F, U, sig, V, yield_stress, mu): #epsilon = ti.Vector([0., 0., 0.], dt=self.dtype) epsilon = ti.Vector.zero(self.dtype, self.dim) sig = ti.max(sig, 0.05) # add this to prevent NaN in extrem cases if ti.static(self.dim == 2): epsilon = ti.Vector([ti.log(sig[0, 0]), ti.log(sig[1, 1])]) else: epsilon = ti.Vector( [ti.log(sig[0, 0]), ti.log(sig[1, 1]), ti.log(sig[2, 2])]) epsilon_hat = epsilon - (epsilon.sum() / self.dim) epsilon_hat_norm = self.norm(epsilon_hat) delta_gamma = epsilon_hat_norm - yield_stress / (2 * mu) if delta_gamma > 0: # Yields epsilon -= (delta_gamma / epsilon_hat_norm) * epsilon_hat sig = self.make_matrix_from_diag(ti.exp(epsilon)) F = U @ sig @ V.transpose() return F
def randn(dt): ''' Generates a random number from standard normal distribution using the Box-Muller transform. ''' assert dt == ti.f32 or dt == ti.f64 u1 = ti.random(dt) u2 = ti.random(dt) r = ti.sqrt(-2 * ti.log(u1)) c = ti.cos(math.tau * u2) return r * c
def radius(theta): r = 0.0 t = 3.4 b = 10.0 a = 1 / (ti.log(b) * pow(b, t * np.pi)) th = t * np.pi r0 = (th - a * (pow(b, th) - 1)) / 50 if theta < th: r = (theta - a * (pow(b, theta) - 1)) / 50 else: r = r0 return r
def U3(self, i): # bounding contact potential t = 0.0 dist_x_l = self.node[i].x - boundary[0][0] dist_x_u = boundary[0][1] - self.node[i].x dist_y_l = self.node[i].y - boundary[1][0] dist_y_u = boundary[1][1] - self.node[i].y if dist_x_l <= self.bar_d: t += -self.k * ( (dist_x_l - self.bar_d)**2) * ti.log(dist_x_l / self.bar_d) if dist_x_u <= self.bar_d: t += -self.k * ( (dist_x_u - self.bar_d)**2) * ti.log(dist_x_u / self.bar_d) if dist_y_l <= self.bar_d: t += -self.k * ( (dist_y_l - self.bar_d)**2) * ti.log(dist_y_l / self.bar_d) if dist_y_u <= self.bar_d: t += -self.k * ( (dist_y_u - self.bar_d)**2) * ti.log(dist_y_u / self.bar_d) return t
def compute_sdf(z, c): md2 = 1.0 mz2 = dot(z, z) for _ in range(iters): md2 *= max_norm * mz2 z = quat_mul(z, z) + c mz2 = z.dot(z) if mz2 > max_norm: break return 0.25 * ti.sqrt(mz2 / md2) * ti.log(mz2)
def fourier(): # parallel for k, l in results: v = ti.Vector([0.0, 0.0]) for i in range(size): for j in range(size): center = size // 2 kk = (k + center) % size ll = (l + center) % size angle = -2.0 * pi * (kk * i + ll * j) / float(size) p = ti.Vector([ti.cos(angle), ti.sin(angle)]) v += pixels[i, j] * p center = size // 2 results[k, l] = ti.log(1.0 + v.norm())
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 update_U(): for i in range(NF): ia, ib, ic = f2v[i] a, b, c = pos[ia], pos[ib], pos[ic] V[i] = abs((a - c).cross(b - c)) D_i = ti.Matrix.cols([a - c, b - c]) F[i] = D_i @ B[i] for i in range(NF): F_i = F[i] log_J_i = ti.log(F_i.determinant()) phi_i = mu / 2 * ((F_i.transpose() @ F_i).trace() - 2) phi_i -= mu * log_J_i phi_i += lam / 2 * log_J_i**2 phi[i] = phi_i U[None] += V[i] * phi_i
def func(): xi[0] = -yi[None] xi[1] = ~yi[None] xi[2] = ti.logical_not(yi[None]) xi[3] = ti.abs(yi[None]) xf[0] = -yf[None] xf[1] = ti.abs(yf[None]) xf[2] = ti.sqrt(yf[None]) xf[3] = ti.sin(yf[None]) xf[4] = ti.cos(yf[None]) xf[5] = ti.tan(yf[None]) xf[6] = ti.asin(yf[None]) xf[7] = ti.acos(yf[None]) xf[8] = ti.tanh(yf[None]) xf[9] = ti.floor(yf[None]) xf[10] = ti.ceil(yf[None]) xf[11] = ti.exp(yf[None]) xf[12] = ti.log(yf[None])
def logistic_iterate(burnin: ti.i32, sample_len: ti.i32, amin: ti.f64, amax: ti.f64, da: ti.f64, n_threads: ti.i32): for tid in range(n_threads): x = ti.random(ti.f64) a = amin + tid * da for i in range(burnin): x = a * x * (1 - x) summed_lyapunov_exp = 0.0 for i in range(sample_len): summed_lyapunov_exp += ti.log(abs(a * (1 - 2 * x))) x = a * x * (1 - x) px_row = int((x - xmin) / xrange * float(NUM_A)) pixels[tid, px_row] = ti.cast(1, ti.u8) points[tid * sample_len + i, 0] = a points[tid * sample_len + i, 1] = x lyapunov_exp[tid] = summed_lyapunov_exp / sample_len
def sand_projection(self, sigma, p): sigma_out = ti.Matrix.zero(ti.f32, self.dim, self.dim) epsilon = ti.Vector.zero(ti.f32, self.dim) for i in ti.static(range(self.dim)): epsilon[i] = ti.log(max(abs(sigma[i, i]), 1e-4)) sigma_out[i, i] = 1 tr = epsilon.sum() + self.Jp[p] epsilon_hat = epsilon - tr / self.dim epsilon_hat_norm = epsilon_hat.norm() + 1e-20 if tr >= 0.0: self.Jp[p] = tr else: self.Jp[p] = 0.0 delta_gamma = epsilon_hat_norm + ( self.dim * self.lambda_0 + 2 * self.mu_0) / (2 * self.mu_0) * tr * self.alpha for i in ti.static(range(self.dim)): sigma_out[i, i] = ti.exp(epsilon[i] - max(0, delta_gamma) / epsilon_hat_norm * epsilon_hat[i]) return sigma_out
def substep(): # set zero initial state for both water/sand grid for i, j in grid_sm: grid_sv[i, j], grid_wv[i, j] = [0, 0], [0, 0] grid_sm[i, j], grid_wm[i, j] = 0, 0 grid_sf[i, j], grid_wf[i, j] = [0, 0], [0, 0] # P2G (sand's part) for p in range(n_s_particles): base = (x_s[p] * inv_dx - 0.5).cast(int) if base[0] < 0 or base[1] < 0 or base[0] >= n_grid - 2 or base[ 1] >= n_grid - 2: continue fx = x_s[p] * inv_dx - base.cast(float) # Quadratic kernels [http://mpm.graphics Eqn. 123, with x=fx, fx-1,fx-2] w = [0.5 * (1.5 - fx)**2, 0.75 - (fx - 1)**2, 0.5 * (fx - 0.5)**2] U, sig, V = ti.svd(F_s[p]) inv_sig = sig.inverse() e = ti.Matrix([[ti.log(sig[0, 0]), 0], [0, ti.log(sig[1, 1])]]) stress = U @ (2 * mu_s * inv_sig @ e + lambda_s * e.trace() * inv_sig) @ V.transpose() # formula (25) stress = (-p_vol * 4 * inv_dx * inv_dx) * stress @ F_s[p].transpose() # stress *= h(e) # print(h(e)) affine = s_mass * C_s[p] for i, j in ti.static(ti.ndrange(3, 3)): offset = ti.Vector([i, j]) dpos = (offset.cast(float) - fx) * dx weight = w[i][0] * w[j][1] grid_sv[base + offset] += weight * (s_mass * v_s[p] + affine @ dpos) grid_sm[base + offset] += weight * s_mass grid_sf[base + offset] += weight * stress @ dpos # P2G (water's part): for p in range(n_w_particles): base = (x_w[p] * inv_dx - 0.5).cast(int) fx = x_w[p] * inv_dx - base.cast(float) # Quadratic kernels [http://mpm.graphics Eqn. 123, with x=fx, fx-1,fx-2] w = [0.5 * (1.5 - fx)**2, 0.75 - (fx - 1)**2, 0.5 * (fx - 0.5)**2] stress = w_k * (1 - 1 / (J_w[p]**w_gamma)) stress = (-p_vol * 4 * inv_dx * inv_dx) * stress * J_w[p] # stress = -4 * 400 * p_vol * (J_w[p] - 1) / dx ** 2 (special case when gamma equals to 1) affine = w_mass * C_w[p] # affine = ti.Matrix([[stress, 0], [0, stress]]) + w_mass * C_w[p] for i, j in ti.static(ti.ndrange(3, 3)): offset = ti.Vector([i, j]) dpos = (offset.cast(float) - fx) * dx weight = w[i][0] * w[j][1] grid_wv[base + offset] += weight * (w_mass * v_w[p] + affine @ dpos) grid_wm[base + offset] += weight * w_mass grid_wf[base + offset] += weight * stress * dpos # Update Grids Momentum for i, j in grid_sm: if grid_sm[i, j] > 0: grid_sv[i, j] = ( 1 / grid_sm[i, j]) * grid_sv[i, j] # Momentum to velocity if grid_wm[i, j] > 0: grid_wv[i, j] = (1 / grid_wm[i, j]) * grid_wv[i, j] # Momentum exchange cE = (n * n * w_rho * gravity[1]) / k_hat # drag coefficient if grid_sm[i, j] > 0 and grid_wm[i, j] > 0: sm, wm = grid_sm[i, j], grid_wm[i, j] sv, wv = grid_sv[i, j], grid_wv[i, j] d = cE * sm * wm M = ti.Matrix([[sm, 0], [0, wm]]) D = ti.Matrix([[-d, d], [d, -d]]) V = ti.Matrix.rows([grid_sv[i, j], grid_wv[i, j]]) G = ti.Matrix.rows([gravity, gravity]) F = ti.Matrix.rows([grid_sf[i, j], grid_wf[i, j]]) A = M + dt * D B = M @ V + dt * (M @ G + F) X = A.inverse() @ B grid_sv[i, j], grid_wv[i, j] = ti.Vector( [X[0, 0], X[0, 1]]), ti.Vector([X[1, 0], X[1, 1]]) elif grid_sm[i, j] > 0: grid_sv[i, j] += dt * (gravity + grid_sf[i, j] / grid_sm[i, j] ) # Update explicit force elif grid_wm[i, j] > 0: grid_wv[i, j] += dt * (gravity + grid_wf[i, j] / grid_wm[i, j]) normal = ti.Vector.zero(float, 2) if grid_sm[i, j] > 0: if i < 3 and grid_sv[i, j][0] < 0: normal = ti.Vector([1, 0]) if i > n_grid - 3 and grid_sv[i, j][0] > 0: normal = ti.Vector([-1, 0]) if j < 3 and grid_sv[i, j][1] < 0: normal = ti.Vector([0, 1]) if j > n_grid - 3 and grid_sv[i, j][1] > 0: normal = ti.Vector([0, -1]) if not (normal[0] == 0 and normal[1] == 0): # Apply friction s = grid_sv[i, j].dot(normal) if s <= 0: v_normal = s * normal v_tangent = grid_sv[ i, j] - v_normal # divide velocity into normal and tangential parts vt = v_tangent.norm() if vt > 1e-12: grid_sv[i, j] = v_tangent - ( vt if vt < -mu_b * s else -mu_b * s) * ( v_tangent / vt) # The Coulomb friction law if grid_wm[i, j] > 0: if i < 3 and grid_wv[i, j][0] < 0: grid_wv[i, j][0] = 0 # Boundary conditions if i > n_grid - 3 and grid_wv[i, j][0] > 0: grid_wv[i, j][0] = 0 if j < 3 and grid_wv[i, j][1] < 0: grid_wv[i, j][1] = 0 if j > n_grid - 3 and grid_wv[i, j][1] > 0: grid_wv[i, j][1] = 0 # G2P (water's part) for p in range(n_w_particles): base = (x_w[p] * inv_dx - 0.5).cast(int) fx = x_w[p] * inv_dx - base.cast(float) w = [0.5 * (1.5 - fx)**2, 0.75 - (fx - 1.0)**2, 0.5 * (fx - 0.5)**2] new_v = ti.Vector.zero(float, 2) new_C = ti.Matrix.zero(float, 2, 2) for i, j in ti.static(ti.ndrange(3, 3)): dpos = ti.Vector([i, j]).cast(float) - fx g_v = grid_wv[base + ti.Vector([i, j])] weight = w[i][0] * w[j][1] new_v += weight * g_v new_C += 4 * inv_dx * weight * g_v.outer_product(dpos) J_w[p] = (1 + dt * new_C.trace()) * J_w[p] v_w[p], C_w[p] = new_v, new_C x_w[p] += dt * v_w[p] # G2P (sand's part) for p in range(n_s_particles): base = (x_s[p] * inv_dx - 0.5).cast(int) if base[0] < 0 or base[1] < 0 or base[0] >= n_grid - 2 or base[ 1] >= n_grid - 2: continue fx = x_s[p] * inv_dx - base.cast(float) w = [0.5 * (1.5 - fx)**2, 0.75 - (fx - 1.0)**2, 0.5 * (fx - 0.5)**2] new_v = ti.Vector.zero(float, 2) new_C = ti.Matrix.zero(float, 2, 2) phi_s[p] = 0.0 # Saturation for i, j in ti.static(ti.ndrange( 3, 3)): # loop over 3x3 grid node neighborhood dpos = ti.Vector([i, j]).cast(float) - fx g_v = grid_sv[base + ti.Vector([i, j])] weight = w[i][0] * w[j][1] new_v += weight * g_v new_C += 4 * inv_dx * weight * g_v.outer_product(dpos) if grid_sm[base + ti.Vector([i, j])] > 0 and grid_wm[ base + ti.Vector([i, j])] > 0: phi_s[p] += weight # formula (24) F_s[p] = (ti.Matrix.identity(float, 2) + dt * new_C) @ F_s[p] v_s[p], C_s[p] = new_v, new_C x_s[p] += dt * v_s[p] U, sig, V = ti.svd(F_s[p]) e = ti.Matrix([[ti.log(sig[0, 0]), 0], [0, ti.log(sig[1, 1])]]) new_e, dq = project(e, p) hardening(dq, p) new_F = U @ ti.Matrix([[ti.exp(new_e[0, 0]), 0], [0, ti.exp(new_e[1, 1])]]) @ V.transpose() vc_s[p] += -ti.log(new_F.determinant()) + ti.log( F_s[p].determinant()) # formula (26) F_s[p] = new_F
def Psi(self, i): # (strain) energy density F = self.F(i) J = max(F.determinant(), 0.01) return self.mu / 2 * ( (F @ F.transpose()).trace() - self.dim) - self.mu * ti.log(J) + self.la / 2 * ti.log(J)**2
def test_unary(): grad_test(lambda x: ti.sqrt(x), lambda x: np.sqrt(x)) grad_test(lambda x: ti.exp(x), lambda x: np.exp(x)) grad_test(lambda x: ti.log(x), lambda x: np.log(x))
def func(): for i in range(N): u = ti.log(x[i]) y[i] = u
def xent(): for i in range(n_output): loss.atomic_add(-gt[i] * ti.log(output_softmax[i]) + (gt[i] - 1) * ti.log(1 - output_softmax[i]))
@pytest.mark.parametrize('tifunc', [ lambda x: 1 / x, lambda x: (x + 1) / (x - 1), lambda x: (x + 1) * (x + 2) / ((x - 1) * (x + 3)), ]) @if_has_autograd @test_utils.test() def test_frac(tifunc): grad_test(tifunc) @pytest.mark.parametrize('tifunc,npfunc', [ (lambda x: ti.sqrt(x), lambda x: np.sqrt(x)), (lambda x: ti.exp(x), lambda x: np.exp(x)), (lambda x: ti.log(x), lambda x: np.log(x)), ]) @if_has_autograd @test_utils.test() def test_unary(tifunc, npfunc): grad_test(tifunc, npfunc) @pytest.mark.parametrize('tifunc,npfunc', [ (lambda x: ti.min(x, 0), lambda x: np.minimum(x, 0)), (lambda x: ti.min(x, 1), lambda x: np.minimum(x, 1)), (lambda x: ti.min(0, x), lambda x: np.minimum(0, x)), (lambda x: ti.min(1, x), lambda x: np.minimum(1, x)), (lambda x: ti.max(x, 0), lambda x: np.maximum(x, 0)), (lambda x: ti.max(x, 1), lambda x: np.maximum(x, 1)), (lambda x: ti.max(0, x), lambda x: np.maximum(0, x)),
def compute_loss(t : ti.i32): ti.atomic_add(loss[None], ti.log(t) * (ti.cos(x[t, 1] - math.pi) + 0.01 * v[t, 1]**2))
def p2g(self, dt: ti.f32): ti.no_activate(self.particle) ti.block_dim(256) ti.block_local(*self.grid_v.entries) ti.block_local(self.grid_m) for I in ti.grouped(self.pid): p = self.pid[I] base = ti.floor(self.x[p] * self.inv_dx - 0.5).cast(int) for D in ti.static(range(self.dim)): base[D] = ti.assume_in_range(base[D], I[D], 0, 1) fx = self.x[p] * self.inv_dx - base.cast(float) # Quadratic kernels [http://mpm.graphics Eqn. 123, with x=fx, fx-1,fx-2] w = [0.5 * (1.5 - fx)**2, 0.75 - (fx - 1)**2, 0.5 * (fx - 0.5)**2] # deformation gradient update self.F[p] = (ti.Matrix.identity(ti.f32, self.dim) + dt * self.C[p]) @ self.F[p] # Hardening coefficient: snow gets harder when compressed h = ti.exp(10 * (1.0 - self.Jp[p])) if self.material[ p] == self.material_elastic: # jelly, make it softer h = 0.3 mu, la = self.mu_0 * h, self.lambda_0 * h if self.material[p] == self.material_water: # liquid mu = 0.0 U, sig, V = ti.svd(self.F[p]) J = 1.0 if self.material[p] != self.material_sand: for d in ti.static(range(self.dim)): new_sig = sig[d, d] if self.material[p] == self.material_snow: # Snow new_sig = min(max(sig[d, d], 1 - 2.5e-2), 1 + 4.5e-3) # Plasticity self.Jp[p] *= sig[d, d] / new_sig sig[d, d] = new_sig J *= new_sig if self.material[p] == self.material_water: # Reset deformation gradient to avoid numerical instability new_F = ti.Matrix.identity(ti.f32, self.dim) new_F[0, 0] = J self.F[p] = new_F elif self.material[p] == self.material_snow: # Reconstruct elastic deformation gradient after plasticity self.F[p] = U @ sig @ V.transpose() stress = ti.Matrix.zero(ti.f32, self.dim, self.dim) if self.material[p] != self.material_sand: stress = 2 * mu * ( self.F[p] - U @ V.transpose()) @ self.F[p].transpose( ) + ti.Matrix.identity(ti.f32, self.dim) * la * J * (J - 1) else: sig = self.sand_projection(sig, p) self.F[p] = U @ sig @ V.transpose() log_sig_sum = 0.0 center = ti.Matrix.zero(ti.f32, self.dim, self.dim) for i in ti.static(range(self.dim)): log_sig_sum += ti.log(sig[i, i]) center[i, i] = 2.0 * self.mu_0 * ti.log( sig[i, i]) * (1 / sig[i, i]) for i in ti.static(range(self.dim)): center[i, i] += self.lambda_0 * log_sig_sum * (1 / sig[i, i]) stress = U @ center @ V.transpose() @ self.F[p].transpose() stress = (-dt * self.p_vol * 4 * self.inv_dx**2) * stress affine = stress + self.p_mass * self.C[p] # Loop over 3x3 grid node neighborhood for offset in ti.static(ti.grouped(self.stencil_range())): dpos = (offset.cast(float) - fx) * self.dx weight = 1.0 for d in ti.static(range(self.dim)): weight *= w[offset[d]][d] self.grid_v[base + offset] += weight * (self.p_mass * self.v[p] + affine @ dpos) self.grid_m[base + offset] += weight * self.p_mass
def kernel1(a: ti.i32, b: ti.i32, c: ti.f32) -> ti.f32: return a / b + c * b - c + a**2 + ti.log(c)