Пример #1
0
def P2G(): # Particle to grid (P2G)
    for p in x: 
        base = (x[p] * inv_dx - 0.5).cast(int)
        fx = x[p] * inv_dx - base.cast(float)
        # Bspline
        w = [0.5 * (1.5 - fx) ** 2, 0.75 - (fx - 1) ** 2, 0.5 * (fx - 0.5) ** 2]
        F[p] = (ti.Matrix.identity(float, 2) + dt * C[p]) @ F[p] # deformation gradient update
        h = max(0.1, min(5, ti.exp(10 * (1.0 - Jp[p])))) # Hardening coefficient
        mu, la = mu_0 * h, lambda_0 * h
        mu = 0.0
        U, sig, V = ti.svd(F[p])
        J = 1.0
        for d in ti.static(range(2)):
            new_sig = sig[d, d]
            if material[p] == 2: # snow
                new_sig = min(max(sig[d, d], 1 - 2.5e-2), 1 + 4.5e-3)  # plasticity
            Jp[p] *= sig[d, d] / new_sig
            sig[d, d] = new_sig
            J *= new_sig 
        F[p] = ti.Matrix.identity(float, 2) * ti.sqrt(J) # Reset deformation gradient
        stress = 2 * mu * (F[p] - U @ V.transpose()) @ F[p].transpose() + ti.Matrix.identity(float, 2) * la * J * (J - 1)
        stress = (-dt * p_vol * 4 * inv_dx * inv_dx) * stress
        affine = stress + p_mass * C[p]
        for i, j in ti.static(ti.ndrange(3, 3)): # Loop over 3x3 grid
            offset = ti.Vector([i, j])
            dpos = (offset.cast(float) - fx) * dx
            weight = w[i][0] * w[j][1]
            grid_v[base + offset] += weight * (p_mass * v[p] + affine @ dpos)
            grid_m[base + offset] += weight * p_mass
Пример #2
0
 def affine(self, i):
     self.system.F[i] = (ti.Matrix.identity(ti.f32, DIM) + self.system.dt *
                         self.system.C[i]) @ self.system.F[i]
     h = 0.3 if self.system.type[i] == 1 else ti.exp(
         10 * (1.0 - self.system.Jp[i]))
     la = self.lambda_0 * h
     U, sig, V = ti.svd(self.system.F[i])
     J = 1.0
     for d in ti.static(range(DIM)):
         new_sig = min(max(sig[d, d], 1 - 2.5e-2), 1 +
                       4.5e-3) if self.system.type[i] == 2 else sig[d, d]
         self.system.Jp[i] *= sig[d, d] / new_sig
         sig[d, d] = new_sig
         J *= new_sig
     if self.system.type[
             i] == 0:  # Reset deformation gradient to avoid numerical instability
         self.system.F[i] = ti.Matrix.identity(ti.f32, DIM)
         self.system.F[i][0, 0] = J
     elif self.system.type[i] == 2:
         self.system.F[i] = U @ sig @ V.transpose(
         )  # Reconstruct elastic deformation gradient after plasticity
     stress = ti.Matrix.identity(ti.f32, DIM) * la * J * (J - 1)
     if self.system.type[i] != 0:
         stress += 2 * self.mu_0 * h * (
             self.system.F[i] -
             U @ V.transpose()) @ self.system.F[i].transpose()
     stress = (-self.system.dt * self.vol * 4 * self.inv_dx *
               self.inv_dx) * stress
     return stress + self.mass * self.system.C[i]
Пример #3
0
def substep():
  for i, j in ti.ndrange(n_grid, n_grid):
    grid_v[i, j] = [0, 0]
    grid_m[i, j] = 0
  for p in range(n_particles): # Particle state update and scatter to grid (P2G)
    base = (x[p] * inv_dx - 0.5).cast(int)
    fx = x[p] * inv_dx - base.cast(float)
    # Quadratic kernels  [http://mpm.graphics   Eqn. 123, with x=fx, fx-1,fx-2]
    w = [0.5 * ti.sqr(1.5 - fx), 0.75 - ti.sqr(fx - 1), 0.5 * ti.sqr(fx - 0.5)]
    F[p] = (ti.Matrix.identity(ti.f32, 2) + dt * C[p]) @ F[p] # deformation gradient update
    h = ti.exp(10 * (1.0 - Jp[p])) # Hardening coefficient: snow gets harder when compressed
    if material[p] == 1: # jelly, make it softer
      h = 0.3
    mu, la = mu_0 * h, lambda_0 * h
    if material[p] == 0: # liquid
      mu = 0.0
    U, sig, V = ti.svd(F[p])
    J = 1.0
    for d in ti.static(range(2)):
      new_sig = sig[d, d]
      if material[p] == 2:  # Snow
        new_sig = min(max(sig[d, d], 1 - 2.5e-2), 1 + 4.5e-3)  # Plasticity
      Jp[p] *= sig[d, d] / new_sig
      sig[d, d] = new_sig
      J *= new_sig
    if material[p] == 0:  # Reset deformation gradient to avoid numerical instability
      F[p] = ti.Matrix.identity(ti.f32, 2) * ti.sqrt(J)
    elif material[p] == 2:
      F[p] = U @ sig @ V.T() # Reconstruct elastic deformation gradient after plasticity
    stress = 2 * mu * (F[p] - U @ V.T()) @ F[p].T() + ti.Matrix.identity(ti.f32, 2) * la * J * (J - 1)
    stress = (-dt * p_vol * 4 * inv_dx * inv_dx) * stress
    affine = stress + p_mass * C[p]
    for i, j in ti.static(ti.ndrange(3, 3)): # Loop over 3x3 grid node neighborhood
      offset = ti.Vector([i, j])
      dpos = (offset.cast(float) - fx) * dx
      weight = w[i][0] * w[j][1]
      grid_v[base + offset] += weight * (p_mass * v[p] + affine @ dpos)
      grid_m[base + offset] += weight * p_mass
  for i, j in ti.ndrange(n_grid, n_grid):
    if grid_m[i, j] > 0: # No need for epsilon here
      grid_v[i, j] = (1 / grid_m[i, j]) * grid_v[i, j] # Momentum to velocity
      grid_v[i, j][1] -= dt * 50 # gravity
      if i < 3 and grid_v[i, j][0] < 0:          grid_v[i, j][0] = 0 # Boundary conditions
      if i > n_grid - 3 and grid_v[i, j][0] > 0: grid_v[i, j][0] = 0
      if j < 3 and grid_v[i, j][1] < 0:          grid_v[i, j][1] = 0
      if j > n_grid - 3 and grid_v[i, j][1] > 0: grid_v[i, j][1] = 0
  for p in range(n_particles): # grid to particle (G2P)
    base = (x[p] * inv_dx - 0.5).cast(int)
    fx = x[p] * inv_dx - base.cast(float)
    w = [0.5 * ti.sqr(1.5 - fx), 0.75 - ti.sqr(fx - 1.0), 0.5 * ti.sqr(fx - 0.5)]
    new_v = ti.Vector.zero(ti.f32, 2)
    new_C = ti.Matrix.zero(ti.f32, 2, 2)
    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_v[base + ti.Vector([i, j])]
      weight = w[i][0] * w[j][1]
      new_v += weight * g_v
      new_C += 4 * inv_dx * weight * ti.outer_product(g_v, dpos)
    v[p], C[p] = new_v, new_C
    x[p] += dt * v[p] # advection
Пример #4
0
    def makePD(self, M: ti.template()):
        U, sigma, V = ti.svd(M)
        if sigma[0, 0] >= 0: return
        D = ti.Matrix(self.dim, self.real, self.real)
        for i in range(self.dim):
            if sigma[i, i] < 0: D[i, i] = 0
            else: D[i, i] = sigma[i, i]

        M = sigma @ D @ sigma.transpose()
Пример #5
0
def particle_to_grid():

    for k in particle_position:

        p = particle_position[k]
        grid = p * inv_dx
        base = int(grid - 0.5)
        fx = grid - base

        w = [0.5 * (1.5 - fx)**2, 0.75 - (fx - 1)**2,
             0.5 * (fx - 0.5)**2]  # B-spline
        # stress = -dt * 4 * E * p_vol * (particle_J[k] - 1) * (inv_dx**2)
        # affine = ti.Matrix([[stress, 0], [0, stress]]) + p_mass * particle_C[k]

        particle_F[k] = (ti.Matrix.identity(float, 2) +
                         dt * particle_C[k]) @ particle_F[k]

        # hardening coefficient
        h = ti.exp(10 * (1.0 - particle_J[k]))
        if particle_material[k] == 1:  # jelly
            h = 0.3

        mu, la = mu_0 * h, lambda_0 * h
        if particle_material[k] == 0:  # fluid
            mu = 0.0

        U, sig, V = ti.svd(particle_F[k])
        J = 1.0
        for d in ti.static(range(2)):
            new_sig = sig[d, d]
            if particle_material[k] == 2:  # snow
                new_sig = min(max(sig[d, d], 1 - 2.5e-2),
                              1 + 4.5e-3)  # plasticity
            particle_J[k] *= sig[d, d] / new_sig
            sig[d, d] = new_sig
            J *= new_sig

        if particle_material[
                k] == 0:  # Fluid: Reset deformation gradient to avoid numerical instability
            particle_F[k] = ti.Matrix.identity(float, 2) * ti.sqrt(J)
        elif particle_material[k] == 2:
            particle_F[k] = U @ sig @ V.transpose(
            )  # Snow: Reconstruct elastic deformation gradient after plasticity

        stress = 2 * mu * (particle_F[k] - U @ V.transpose()) @ particle_F[
            k].transpose() + ti.Matrix.identity(float, 2) * la * J * (J - 1)
        stress = (-dt * p_vol * 4 * inv_dx * inv_dx) * stress
        affine = stress + p_mass * particle_C[k]

        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]
                dpos = (offset - fx) * dx
                grid_velocity[base + offset] += weight * (
                    p_mass * particle_velocity[k] + affine @ dpos)
                grid_weight[base + offset] += weight * p_mass
Пример #6
0
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)
Пример #7
0
    def p2g(self, dt: ti.f32):
        for p in self.x:
            base = (self.x[p] * self.inv_dx - 0.5).cast(int)
            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 * ti.sqr(1.5 - fx), 0.75 - ti.sqr(fx - 1),
                0.5 * ti.sqr(fx - 0.5)
            ]
            # 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
            for d in ti.static(range(self.dim)):
                new_sig = sig[d, d]
                #section 7 of snow paper ... dt being too big
                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.T()

            stress = 2 * mu * (self.F[p] - U @ V.T()) @ self.F[p].T(
            ) + ti.Matrix.identity(ti.f32, self.dim) * la * J * (J - 1)
            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
Пример #8
0
def ssvd(F):
    U, sig, V = ti.svd(F)
    if U.determinant() < 0:
        for i in ti.static(range(3)):
            U[i, 2] *= -1
        sig[2, 2] = -sig[2, 2]
    if V.determinant() < 0:
        for i in ti.static(range(3)):
            V[i, 2] *= -1
        sig[2, 2] = -sig[2, 2]
    return U, sig, V
Пример #9
0
def polar_decompose3d(A, dt):
    """Perform polar decomposition (A=UP) for 3x3 matrix.

    Mathematical concept refers to https://en.wikipedia.org/wiki/Polar_decomposition.

    Args:
        A (ti.Matrix(3, 3)): input 3x3 matrix `A`.
        dt (DataType): date type of elements in matrix `A`, typically accepts ti.f32 or ti.f64.

    Returns:
        Decomposed 3x3 matrices `U` and `P`.
    """
    U, sig, V = ti.svd(A, dt)
    return U @ V.transpose(), V @ sig @ V.transpose()
Пример #10
0
def local_solve_build_bp_for_all_constraints():
    for i in range(NF):
        # Construct strain constraints:
        # Construct Current F_i:
        ia, ib, ic = f2v[i]
        a, b, c = pos_new[ia], pos_new[ib], pos_new[ic]
        D_i = ti.Matrix.cols([b - a, c - a])
        F_i = ti.cast(D_i @ B[i], ti.f64)
        F[i] = F_i
        # Use current F_i construct current 'B * p' or Ri
        U, sigma, V = ti.svd(F_i, ti.f64)
        Bp[i] = U @ V.transpose()

        # Construct volume preservation constraints:
        x, y, max_it, tol = 10.0, 10.0, 80, 1e-6
        for t in range(max_it):
            aa, bb = x + sigma[0, 0], y + sigma[1, 1]
            f = aa * bb - 1
            g1, g2 = bb, aa
            bot = g1 * g1 + g2 * g2
            if abs(bot) < tol:
                break
            top = x * g1 + y * g2 - f
            div = top / bot
            x0, y0 = x, y
            x = div * g1
            y = div * g2
            _dx, _dy = x - x0, y - y0
            if _dx * _dx + _dy * _dy < tol * tol:
                break
        PP = ti.Matrix.rows([[x + sigma[0, 0], 0.0], [0.0, sigma[1, 1] + y]])
        Bp[NF + i] = U @ PP @ V.transpose()

    # Calculate Phi for all the elements:
    for i in range(NF):
        Bp_i_strain = Bp[i]
        Bp_i_volume = Bp[NF + i]
        F_i = F[i]
        energy1 = mu * volume * ((F_i - Bp_i_strain).norm()**2)
        energy2 = 0.5 * lam * volume * ((F_i - Bp_i_volume).trace()**2)
        phi[i] = energy1 + energy2
Пример #11
0
def substep():
    for K in ti.static(range(16)):
        for p in x:
            base = (x[p] * inv_dx - 0.5).cast(int)
            fx = x[p] * inv_dx - base.cast(float)
            w = [
                0.5 * ti.sqr(1.5 - fx), 0.75 - ti.sqr(fx - 1),
                0.5 * ti.sqr(fx - 0.5)
            ]
            F[p] = (ti.Matrix.identity(ti.f32, 2) + dt * C[p]) @ F[p]
            h = ti.exp(10 * (1.0 - Jp[p]))
            if material[p] == 1:
                h = 0.3
            mu, la = mu_0 * h, lambda_0 * h
            if material[p] == 0:  # liquid
                mu = 0.0
            U, sig, V = ti.svd(F[p])
            J = 1.0
            for d in ti.static(range(2)):
                new_sig = sig[d, d]
                if material[p] == 2:  # Snow
                    new_sig = min(max(sig[d, d], 1 - 2.5e-2), 1 + 4.5e-3)
                Jp[p] *= sig[d, d] / new_sig
                sig[d, d] = new_sig
                J *= new_sig
            if material[p] == 0:
                F[p] = ti.Matrix.identity(ti.f32, 2) * ti.sqrt(J)
            elif material[p] == 2:
                F[p] = U @ sig @ V.T()
            stress = 2 * mu * (F[p] - U @ V.T()) @ F[p].T(
            ) + ti.Matrix.identity(ti.f32, 2) * la * J * (J - 1)
            stress = (-dt * p_vol * 4 * inv_dx * inv_dx) * stress
            affine = stress + p_mass * C[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_v[base +
                       offset] += weight * (p_mass * v[p] + affine @ dpos)
                grid_m[base + offset] += weight * p_mass
Пример #12
0
def update_vdv():
    for p in range(num_particles[None]):
        dv[p] = ti.Vector.zero(float, dim)
        # anisotropic filtering parameters
        C = ti.Matrix.zero(float, dim, dim)  # anisotropic covariance
        w = 0.0
        pf = ti.Vector.zero(float, dim)  # total pairwise force
        for j in range(num_neighbors[p]):
            q = neighbors[p, j]
            r = x[p] - x[q]
            norm = ti.max(r.norm(), 1e-5)  # compute distance and it's norm
            if is_fluid(p) == 1:
                dv[p] += viscosity_force(
                    p, q, r, norm)  # compute Viscosity force contribution
                dv[p] += pressure_force(
                    p, q, r, norm)  # compute pressure force contribution
                w += W(norm, dh)
                C += r.outer_product(r) * w
                pf += pairwise_force(
                    p, q, r, norm)  # compute surface tension contribution

        # add body force and filtered pairwise force
        if is_fluid(p) == 1:
            dv[p] += g

            C /= w
            U, sig, V = ti.svd(C)
            for i in ti.static(range(dim)):
                sig[i, i] = ti.max(sig[i, i], eps_min)

            G = U @ sig.inverse() @ V.transpose()
            T = (1 - eta) * ti.Matrix.identity(
                float, dim) + eta * G / G.determinant()
            dv[p] += T @ pf

    for p in range(num_particles[None]):
        v[p] += (dt / 2) * dv[p]
Пример #13
0
 def update_dt(self):
     # update velocity
     for p in self.x:
         if self.flag[p] == 0:
             self.v[p] += self.dt * (self.a_G[p] + self.a_other[p] +
                                     self.a_lambda[p] + self.a_friction[p])
     # update fe
     for p in self.x:
         if self.flag[p] == 0:
             grad_v = ti.Matrix([[0., 0.], [0., 0.]])
             for nbr in range(self.nbrs_num[p]):
                 i = self.nbrs_list[p, nbr]
                 if self.flag[i] == 0:
                     grad_v += self.v[i].outer_product(self.dW(i, p))
             self.F_E[p] += self.dt * grad_v @ self.F_E[p]
             U, Sigma, V = ti.svd(self.F_E[p], ti.f32)
             for n in ti.static(range(self.dim)):
                 Sigma[n, n] = min(1 + self.theta_s,
                                   max(1 - self.theta_c, Sigma[n, n]))
             self.F_E[p] = V @ Sigma @ V.transpose()
     # update position
     for p in self.x:
         if self.flag[p] == 0:
             self.x[p] += self.dt * self.v[p]
Пример #14
0
 def run():
   for i in range(1):
     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])
Пример #15
0
def substep(f: ti.i32):
    for i, j in grid_m:
        grid_v[i, j] = [0, 0]
        grid_m[i, j] = 0
    for p in x:  # Particle state update and scatter to grid (P2G)
        base = (x[p] * inv_dx - 0.5).cast(int)
        fx = x[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]
        F[p] = (ti.Matrix.identity(ti.f32, 2) +
                dt * C[p]) @ F[p]  # deformation gradient update
        h = ti.exp(
            10 *
            (1.0 -
             Jp[p]))  # Hardening coefficient: snow gets harder when compressed
        if material[p] == 1:  # jelly, make it softer
            h = 0.3
        mu, la = mu_0 * h, lambda_0 * h
        U, sig, V = ti.svd(F[p])
        J = 1.0
        for d in ti.static(range(2)):
            new_sig = sig[d, d]
            Jp[p] *= sig[d, d] / new_sig
            sig[d, d] = new_sig
            J *= new_sig
        stress = 2 * mu * (F[p] - U @ V.T()) @ F[p].T() + ti.Matrix.identity(
            ti.f32, 2) * la * J * (J - 1)

        ## ADD THE ACTUATION STRESS FOR SOLID MATERIAL
        if material[p] == 2:  # actuated material only
            act = 0.0
            act = ti.sin(actuation_omega * f * dt)
            A = ti.Matrix.identity(ti.f32, 2) * act * act_strength
            stress += F[p] @ A @ F[p].T()
        ##

        stress = (-dt * p_vol * 4 * inv_dx * inv_dx) * stress
        affine = stress + p_mass * C[p]
        for i, j in ti.static(ti.ndrange(
                3, 3)):  # Loop over 3x3 grid node neighborhood
            offset = ti.Vector([i, j])
            dpos = (offset.cast(float) - fx) * dx
            weight = w[i][0] * w[j][1]
            grid_v[base + offset] += weight * (p_mass * v[p] + affine @ dpos)
            grid_m[base + offset] += weight * p_mass
    for i, j in grid_m:
        if grid_m[i, j] > 0:  # No need for epsilon here
            grid_v[i,
                   j] = (1 / grid_m[i, j]) * grid_v[i,
                                                    j]  # Momentum to velocity
            grid_v[i, j][1] -= dt * 50  # gravity
            if i < 3 and grid_v[i, j][0] < 0:
                grid_v[i, j][0] = 0  # Boundary conditions
            if i > n_grid - 3 and grid_v[i, j][0] > 0: grid_v[i, j][0] = 0
            if j < 3 and grid_v[i, j][1] < 0: grid_v[i, j][1] = 0
            if j > n_grid - 3 and grid_v[i, j][1] > 0: grid_v[i, j][1] = 0
    for p in x:  # grid to particle (G2P)
        base = (x[p] * inv_dx - 0.5).cast(int)
        fx = x[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(ti.f32, 2)
        new_C = ti.Matrix.zero(ti.f32, 2, 2)
        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_v[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)
        v[p], C[p] = new_v, new_C
        x[p] += dt * v[p]  # advection
Пример #16
0
 def run():
     U[None], sigma[None], V[None] = ti.svd(A[None])
Пример #17
0
    def psi(self, F):  # strain energy density function Ψ(F)
        U, sig, V = ti.svd(F)

        # fixed corotated model, you can replace it with any constitutive model
        return self.mu_0 * (F - U @ V.transpose()).norm(
        )**2 + self.lambda_0 / 2 * (F.determinant() - 1)**2
Пример #18
0
def substep():
    line = ti.Vector([x_le[0] - x_ls[0], x_le[1] - x_ls[1]]).normalized()
    for p in x_r:
        x_r[p] = x_r[p] - line * dt * r_v
    for p in x_rp:
        x_rp[p] = x_rp[p] - line * dt * r_v

    # CDF
    for i, j in grid_A:
        grid_A[i, j] = 0
        grid_T[i, j] = 0
        grid_d[i, j] = 0.0

    for p in x_rp:
        ba = x_r[p + 1] - x_r[p]
        base = (x_rp[p] * inv_dx - 0.5).cast(int)
        for i, j in ti.static(ti.ndrange(
                3, 3)):  # Loop over 3x3 grid node neighborhood
            offset = ti.Vector([i, j])
            pa = (offset + base).cast(float) * dx - x_r[p]
            h = pa.dot(ba) / (ba.dot(ba))

            if h <= 1 and h >= 0:
                grid_d[base + offset] = (pa - h * ba).norm()
                grid_A[base + offset] = 1
                outer = pa[0] * ba[1] - pa[1] * ba[0]
                #print(grid_d[base + offset])
                if outer > 0:
                    grid_T[base + offset] = 1
                else:
                    grid_T[base + offset] = -1

    for p in x:
        p_A[p] = 0
        p_T[p] = 0
        p_d[p] = 0.0

        base = (x[p] * inv_dx - 0.5).cast(int)
        fx = x[p] * inv_dx - base.cast(float)
        w = [0.5 * (1.5 - fx)**2, 0.75 - (fx - 1)**2, 0.5 * (fx - 0.5)**2]
        Tpr = 0.0
        for i, j in ti.static(ti.ndrange(
                3, 3)):  # Loop over 3x3 grid node neighborhood
            offset = ti.Vector([i, j])
            if grid_A[base + offset] == 1:
                p_A[p] = 1

            weight = w[i][0] * w[j][1]
            Tpr += weight * grid_d[base + offset] * grid_T[base + offset]
        if p_A[p] == 1:
            if Tpr > 0:
                p_T[p] = 1
            else:
                p_T[p] = -1
            p_d[p] = abs(Tpr)
            #print(p_d[p])

    for i, j in grid_m:
        grid_v[i, j] = [0, 0]
        grid_m[i, j] = 0

    # P2G
    for p in x:  # Particle state update and scatter to grid (P2G)
        # p is a scalar
        base = (x[p] * inv_dx - 0.5).cast(int)
        fx = x[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]
        F[p] = (ti.Matrix.identity(float, 2) +
                dt * C[p]) @ F[p]  # deformation gradient update
        #h = ti.exp(10 * (1.0 - Jp[p])) # Hardening coefficient: snow gets harder when compressed
        #if material[p] == 1: # jelly, make it softer
        #  h = 0.5
        #mu, la = mu_0 * h, lambda_0 * h
        #if material[p] == 0: # liquid
        #  mu = 0.0
        h = 0.5
        mu, la = mu_0 * h, lambda_0 * h
        U, sig, V = ti.svd(F[p])
        J = 1.0
        for d in ti.static(range(2)):
            new_sig = sig[d, d]
            #if material[p] == 2:  # Snow
            #  new_sig = min(max(sig[d, d], 1 - 2.5e-2), 1 + 4.5e-3)  # Plasticity
            #Jp[p] *= sig[d, d] / new_sig
            #sig[d, d] = new_sig
            J *= new_sig
        #if material[p] == 0:  # Reset deformation gradient to avoid numerical instability
        #  F[p] = ti.Matrix.identity(float, 2) * ti.sqrt(J)
        #elif material[p] == 2:
        #  F[p] = U @ sig @ V.transpose() # Reconstruct elastic deformation gradient after plasticity
        stress = 2 * mu * (F[p] - U @ V.transpose()) @ F[p].transpose(
        ) + ti.Matrix.identity(float, 2) * la * J * (J - 1)
        stress = (-dt * p_vol * 4 * inv_dx * inv_dx) * stress
        affine = stress + p_mass * C[p]
        for i, j in ti.static(ti.ndrange(
                3, 3)):  # Loop over 3x3 grid node neighborhood
            offset = ti.Vector([i, j])
            if p_T[p] * grid_T[base + offset] == -1:
                #continue
                pass
            else:
                dpos = (offset.cast(float) - fx) * dx
                weight = w[i][0] * w[j][1]
                grid_v[base +
                       offset] += weight * (p_mass * v[p] + affine @ dpos)
                grid_m[base + offset] += weight * p_mass

    # grid operation
    for i, j in grid_m:
        if grid_m[i, j] > 0:  # No need for epsilon here
            grid_v[i,
                   j] = (1 / grid_m[i, j]) * grid_v[i,
                                                    j]  # Momentum to velocity
            grid_v[i, j][1] -= dt * gravity  # gravity

            if j > n_grid - 3: grid_v[i, j] = [0, 0]

            if i < 3 and grid_v[i, j][0] < 0:
                grid_v[i, j][0] = 0  # Boundary conditions
            if i > n_grid - 3 and grid_v[i, j][0] > 0: grid_v[i, j][0] = 0
            if j < 3 and grid_v[i, j][1] < 0: grid_v[i, j][1] = 0
            #if j > n_grid - 3 and grid_v[i, j][1] > 0: grid_v[i, j][1] = 0

    # G2P
    for p in x:  # grid to particle (G2P)
        base = (x[p] * inv_dx - 0.5).cast(int)
        fx = x[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)):  # loop over 3x3 grid node neighborhood
            g_v = ti.Vector([0.0, 0.0])
            if p_T[p] * grid_T[base + ti.Vector([i, j])] == -1:
                #slip boundary
                #line = ti.Vector([x_le[0]-x_ls[0],x_le[1]-x_ls[1]]).normalized()
                #g_v = v[p].dot(line) * line

                line = ti.Vector([x_le[0] - x_ls[0],
                                  x_le[1] - x_ls[1]]).normalized()
                pa = ti.Vector([x[p][0] - x_ls[0], x[p][1] - x_ls[1]])
                np = (pa - pa.dot(line) * line).normalized()
                sg = v[p].dot(np)
                if sg > 0:
                    g_v = v[p]
                else:
                    g_v = v[p].dot(line) * line

            else:
                g_v = grid_v[base + ti.Vector([i, j])]

            dpos = ti.Vector([i, j]).cast(float) - fx
            weight = w[i][0] * w[j][1]
            new_v += weight * g_v
            new_C += 4 * inv_dx * weight * g_v.outer_product(dpos)
        v[p], C[p] = new_v, new_C
        x[p] += dt * v[p]  # advection
Пример #19
0
 def run():
     U[None], sigma[None], V[None] = ti.svd(A[None], dt)
     UtU[None] = U[None].transpose() @ U[None]
     VtV[None] = V[None].transpose() @ V[None]
     A_reconstructed[None] = U[None] @ sigma[None] @ V[None].transpose()
Пример #20
0
 def dpsi_dF(self, F):  # first Piola-Kirchoff stress P(F), i.e. ∂Ψ/∂F
     U, sig, V = ti.svd(F)
     J = F.determinant()
     R = U @ V.transpose()
     return 2 * self.mu_0 * (F - R) + self.lambda_0 * (
         J - 1) * J * F.inverse().transpose()
Пример #21
0
def polar_decompose3d(A, dt):
    U, sig, V = ti.svd(A, dt)
    return U @ V.transpose(), V @ sig @ V.transpose()
Пример #22
0
 def svd(self):
     for p in range(0, self.n_particles):
         self.U[p], self.sig[p], self.V[p] = ti.svd(self.F_tmp[p])
Пример #23
0
 def firstPiolaDifferential(self, p, F, dF):
     U, sig, V = ti.svd(F)
     D = U.transpose() @ dF @ V
     K = ti.Matrix.zero(self.real, self.dim, self.dim)
     self.dPdFOfSigmaContractProjected(p, D, K)
     return U @ K @ V.transpose()
Пример #24
0
def substep(g_x: float, g_y: float, g_z: float):
    for I in ti.grouped(grid_m):
        grid_v[I] = ti.zero(grid_v[I])
        grid_m[I] = 0
    ti.block_dim(n_grid)
    for p in x:
        if used[p] == 0:
            continue
        Xp = x[p] / dx
        base = int(Xp - 0.5)
        fx = Xp - base
        w = [0.5 * (1.5 - fx)**2, 0.75 - (fx - 1)**2, 0.5 * (fx - 0.5)**2]

        F[p] = (ti.Matrix.identity(float, 3) +
                dt * C[p]) @ F[p]  # deformation gradient update

        h = ti.exp(
            10 *
            (1.0 -
             Jp[p]))  # Hardening coefficient: snow gets harder when compressed
        if materials[p] == JELLY:  # jelly, make it softer
            h = 0.3
        mu, la = mu_0 * h, lambda_0 * h
        if materials[p] == WATER:  # liquid
            mu = 0.0

        U, sig, V = ti.svd(F[p])
        J = 1.0
        for d in ti.static(range(3)):
            new_sig = sig[d, d]
            if materials[p] == SNOW:  # Snow
                new_sig = min(max(sig[d, d], 1 - 2.5e-2),
                              1 + 4.5e-3)  # Plasticity
            Jp[p] *= sig[d, d] / new_sig
            sig[d, d] = new_sig
            J *= new_sig
        if materials[
                p] == WATER:  # Reset deformation gradient to avoid numerical instability
            new_F = ti.Matrix.identity(float, 3)
            new_F[0, 0] = J
            F[p] = new_F
        elif materials[p] == SNOW:
            F[p] = U @ sig @ V.transpose(
            )  # Reconstruct elastic deformation gradient after plasticity
        stress = 2 * mu * (F[p] - U @ V.transpose()) @ F[p].transpose(
        ) + ti.Matrix.identity(float, 3) * la * J * (J - 1)
        stress = (-dt * p_vol * 4) * stress / dx**2
        affine = stress + p_mass * C[p]

        for offset in ti.static(ti.grouped(ti.ndrange(*neighbour))):
            dpos = (offset - fx) * dx
            weight = 1.0
            for i in ti.static(range(dim)):
                weight *= w[offset[i]][i]
            grid_v[base + offset] += weight * (p_mass * v[p] + affine @ dpos)
            grid_m[base + offset] += weight * p_mass
    for I in ti.grouped(grid_m):
        if grid_m[I] > 0:
            grid_v[I] /= grid_m[I]
        grid_v[I] += dt * ti.Vector([g_x, g_y, g_z])
        cond = I < bound and grid_v[I] < 0 or I > n_grid - bound and grid_v[
            I] > 0
        grid_v[I] = 0 if cond else grid_v[I]
    ti.block_dim(n_grid)
    for p in x:
        if used[p] == 0:
            continue
        Xp = x[p] / dx
        base = int(Xp - 0.5)
        fx = Xp - base
        w = [0.5 * (1.5 - fx)**2, 0.75 - (fx - 1)**2, 0.5 * (fx - 0.5)**2]
        new_v = ti.zero(v[p])
        new_C = ti.zero(C[p])
        for offset in ti.static(ti.grouped(ti.ndrange(*neighbour))):
            dpos = (offset - fx) * dx
            weight = 1.0
            for i in ti.static(range(dim)):
                weight *= w[offset[i]][i]
            g_v = grid_v[base + offset]
            new_v += weight * g_v
            new_C += 4 * weight * g_v.outer_product(dpos) / dx**2
        v[p] = new_v
        x[p] += dt * v[p]
        C[p] = new_C
Пример #25
0
    def updateIsotropicHelper(self, p, F):
        self.reinitializeIsotropicHelper(p)
        if ti.static(self.dim == 2):
            U, sigma, V = ti.svd(F)
            J = sigma[0, 0] * sigma[1, 1]
            _2mu = self.mu_0 * 2
            _lambda = self.lambda_0 * (J - 1)
            Sprod = ti.Vector([sigma[1, 1], sigma[0, 0]])
            self.psi0[p] = _2mu * (sigma[0, 0] - 1) + _lambda * Sprod[0]
            self.psi1[p] = _2mu * (sigma[1, 1] - 1) + _lambda * Sprod[1]
            self.psi00[p] = _2mu + self.lambda_0 * Sprod[0] * Sprod[0]
            self.psi11[p] = _2mu + self.lambda_0 * Sprod[1] * Sprod[1]
            self.psi01[p] = _lambda + self.lambda_0 * Sprod[0] * Sprod[1]

            # (psi0-psi1)/(sigma0-sigma1)
            self.m01[p] = _2mu - _lambda

            # (psi0+psi1)/(sigma0+sigma1)
            self.p01[p] = (self.psi0[p] +
                           self.psi1[p]) / self.clamp_small_magnitude(
                               sigma[0, 0] + sigma[1, 1], 1e-6)

            self.Aij[p] = ti.Matrix([[self.psi00[p], self.psi01[p]],
                                     [self.psi01[p], self.psi11[p]]])
            self.B01[p] = ti.Matrix([[(self.m01[p] + self.p01[p]) * 0.5,
                                      (self.m01[p] - self.p01[p]) * 0.5],
                                     [(self.m01[p] - self.p01[p]) * 0.5,
                                      (self.m01[p] + self.p01[p]) * 0.5]])

            # proj A
            self.makePD(self.Aij[p])
            # proj B
            self.makePD2d(self.B01[p])
        if ti.static(self.dim == 3):
            U, sigma, V = ti.svd(F)
            J = sigma[0, 0] * sigma[1, 1] * sigma[2, 2]
            _2mu = self.mu_0 * 2
            _lambda = self.lambda_0 * (J - 1)
            Sprod = ti.Vector([
                sigma[1, 1] * sigma[2, 2], sigma[0, 0] * sigma[2, 2],
                sigma[0, 0] * sigma[1, 1]
            ])
            self.psi0[p] = _2mu * (sigma[0, 0] - 1) + _lambda * Sprod[0]
            self.psi1[p] = _2mu * (sigma[1, 1] - 1) + _lambda * Sprod[1]
            self.psi2[p] = _2mu * (sigma[2, 2] - 1) + _lambda * Sprod[2]
            self.psi00[p] = _2mu + self.lambda_0 * Sprod[0] * Sprod[0]
            self.psi11[p] = _2mu + self.lambda_0 * Sprod[1] * Sprod[1]
            self.psi22[p] = _2mu + self.lambda_0 * Sprod[2] * Sprod[2]
            self.psi01[p] = _lambda * sigma[
                2, 2] + self.lambda_0 * Sprod[0] * Sprod[1]
            self.psi02[p] = _lambda * sigma[
                1, 1] + self.lambda_0 * Sprod[0] * Sprod[2]
            self.psi12[p] = _lambda * sigma[
                0, 0] + self.lambda_0 * Sprod[1] * Sprod[2]

            # (psiA-psiB)/(sigmaA-sigmaB)
            self.m01[p] = _2mu - _lambda * sigma[2, 2]  # i[p] = 0
            self.m02[p] = _2mu - _lambda * sigma[1, 1]  # i[p] = 2
            self.m12[p] = _2mu - _lambda * sigma[0, 0]  # i[p] = 1

            # (psiA+psiB)/(sigmaA+sigmaB)
            self.p01[p] = (self.psi0[p] +
                           self.psi1[p]) / self.clamp_small_magnitude(
                               sigma[0, 0] + sigma[1, 1], 1e-6)
            self.p02[p] = (self.psi0[p] +
                           self.psi2[p]) / self.clamp_small_magnitude(
                               sigma[0, 0] + sigma[2, 2], 1e-6)
            self.p12[p] = (self.psi1[p] +
                           self.psi2[p]) / self.clamp_small_magnitude(
                               sigma[1, 1] + sigma[2, 2], 1e-6)

            self.Aij[p] = ti.Matrix(
                [[self.psi00[p], self.psi01[p], self.psi02[p]],
                 [self.psi01[p], self.psi11[p], self.psi12[p]],
                 [self.psi02[p], self.psi12[p], self.psi22[p]]])
            self.B01[p] = ti.matrix([[(self.m01[p] + self.p01[p]) * 0.5,
                                      (self.m01[p] - self.p01[p]) * 0.5],
                                     [(self.m01[p] - self.p01[p]) * 0.5,
                                      (self.m01[p] + self.p01[p]) * 0.5]])
            self.B12[p] = ti.matrix([[(self.m12[p] + self.p12[p]) * 0.5,
                                      (self.m12[p] - self.p12[p]) * 0.5],
                                     [(self.m12[p] - self.p12[p]) * 0.5,
                                      (self.m12[p] + self.p12[p]) * 0.5]])
            self.B20[p] = ti.matrix([[(self.m02[p] + self.p02[p]) * 0.5,
                                      (self.m02[p] - self.p02[p]) * 0.5],
                                     [(self.m02[p] - self.p02[p]) * 0.5,
                                      (self.m02[p] + self.p02[p]) * 0.5]])

            # proj A
            self.makePD(self.Aij[p])
            # proj B
            self.makePD2d(self.B01[p])
            self.makePD2d(self.B12[p])
            self.makePD2d(self.B20[p])
Пример #26
0
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
Пример #27
0
def substep():
  #re-initialize grid quantities
  for i, j in grid_m:
    grid_v[i, j] = [0, 0]
    grid_m[i, j] = 0

  # Particle state update and scatter to grid (P2G)
  for p in x: 
    
    #for particle p, compute base index
    base = (x[p] * inv_dx - 0.5).cast(int)
    fx = x[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]
    dw = [fx - 1.5, -2.0 * (fx - 1), fx - 0.5]

    mu, la = mu_0, lambda_0 #opportunity here to modify these to model other materials

    U, sig, V = ti.svd(F[p])
    J = 1.0

    for d in ti.static(range(2)):
      new_sig = sig[d, d]
      Jp[p] *= sig[d, d] / new_sig
      sig[d, d] = new_sig
      J *= new_sig
    
    #Compute Kirchoff Stress
    kirchoff = kirchoff_FCR(F[p], [email protected](), J, mu, la)

    #P2G for velocity and mass AND Force Update!
    for i, j in ti.static(ti.ndrange(3, 3)): # Loop over 3x3 grid node neighborhood
      offset = ti.Vector([i, j])
      dpos = (offset.cast(float) - fx) * dx
      weight = w[i][0] * w[j][1]
      
      dweight = ti.Vector.zero(float,2)
      dweight[0] = inv_dx * dw[i][0] * w[j][1]
      dweight[1] = inv_dx * w[i][0] * dw[j][1]
      
      force = -p_vol * kirchoff @ dweight

      grid_v[base + offset] += p_mass * weight * (v[p] + C[p] @ dpos) #momentum transfer
      grid_m[base + offset] += weight * p_mass #mass transfer

      grid_v[base + offset] += dt * force #add force to update velocity, don't divide by mass bc this is actually updating MOMENTUM
  
  # Gravity and Boundary Collision
  for i, j in grid_m:
    if grid_m[i, j] > 0: # No need for epsilon here
      grid_v[i, j] = (1 / grid_m[i, j]) * grid_v[i, j] # Momentum to velocity
      
      grid_v[i, j] += dt * gravity[None] * 30 # gravity
      
      #add force from mouse
      dist = attractor_pos[None] - dx * ti.Vector([i, j])
      grid_v[i, j] += dist / (0.01 + dist.norm()) * attractor_strength[None] * dt * 100
      
      #wall collisions
      if i < 3 and grid_v[i, j][0] < 0:          grid_v[i, j][0] = 0 # Boundary conditions
      if i > n_grid - 3 and grid_v[i, j][0] > 0: grid_v[i, j][0] = 0
      if j < 3 and grid_v[i, j][1] < 0:          grid_v[i, j][1] = 0
      if j > n_grid - 3 and grid_v[i, j][1] > 0: grid_v[i, j][1] = 0
  
  # grid to particle (G2P)
  for p in x: 
    base = (x[p] * inv_dx - 0.5).cast(int)
    fx = x[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]
    dw = [fx - 1.5, -2.0 * (fx - 1), fx - 0.5]
    new_v = ti.Vector.zero(float, 2)
    new_C = ti.Matrix.zero(float, 2, 2)
    new_F = ti.Matrix.zero(float, 2, 2)
    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_v[base + ti.Vector([i, j])]
      weight = w[i][0] * w[j][1]

      dweight = ti.Vector.zero(float,2)
      dweight[0] = inv_dx * dw[i][0] * w[j][1]
      dweight[1] = inv_dx * w[i][0] * dw[j][1]

      new_v += weight * g_v
      new_C += 4 * inv_dx * weight * g_v.outer_product(dpos)
      new_F += g_v.outer_product(dweight)
    v[p], C[p] = new_v, new_C
    x[p] += dt * v[p] # advection
    F[p] = (ti.Matrix.identity(float, 2) + (dt * new_F)) @ F[p] #updateF (explicitMPM way)
Пример #28
0
def substep():
    for i, j in grid_m:
        grid_v[i, j] = [0, 0]
        grid_m[i, j] = 0
        grid_assit[i, j] = [0, 0]
    for p in x:  # Particle state update and scatter to grid (P2G)
        base = (x[p] * inv_dx - 0.5).cast(int)
        fx = x[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]
        F[p] = (ti.Matrix.identity(ti.f32, 2) +
                dt * C[p]) @ F[p]  # deformation gradient update
        h = ti.exp(
            10 *
            (1.0 -
             Jp[p]))  # Hardening coefficient: snow gets harder when compressed
        if material[p] == 1:  # jelly, make it softer
            h = 1.0
        mu, la = mu_0 * h, lambda_0 * h
        if material[p] == 0:  # liquid
            mu = 0.0
        U, sig, V = ti.svd(F[p])
        J = 1.0
        for d in ti.static(range(2)):
            new_sig = sig[d, d]
            if material[p] == 2:  # Snow
                new_sig = min(max(sig[d, d], 1 - 2.5e-2),
                              1 + 4.5e-3)  # Plasticity
            Jp[p] *= sig[d, d] / new_sig
            sig[d, d] = new_sig
            J *= new_sig
        if material[
                p] == 0:  # Reset deformation gradient to avoid numerical instability
            F[p] = ti.Matrix.identity(ti.f32, 2) * ti.sqrt(J)
        elif material[p] == 2:
            F[p] = U @ sig @ V.T(
            )  # Reconstruct elastic deformation gradient after plasticity
        stress = 2 * mu * (F[p] - U @ V.T()) @ F[p].T() + ti.Matrix.identity(
            ti.f32, 2) * la * J * (J - 1)
        stress = (-dt * p_vol * 4 * inv_dx * inv_dx) * stress
        affine = stress + p_mass * C[p]
        for i, j in ti.static(ti.ndrange(
                3, 3)):  # Loop over 3x3 grid node neighborhood
            offset = ti.Vector([i, j])
            dpos = (offset.cast(float) - fx) * dx
            weight = w[i][0] * w[j][1]
            grid_assit[base + offset] += weight * (affine @ dpos)
            grid_v[base + offset] += weight * (p_mass * v[p] + affine @ dpos)
            grid_m[base + offset] += weight * p_mass

    for p in windmill_x:
        windmill_C = ti.Matrix.zero(ti.f32, 2, 2)
        windmill_F = ti.Matrix.identity(ti.f32,
                                        2)  #ti.Matrix([1., 0.],[0., 1.])

        base = (windmill_x[p] * inv_dx - 0.5).cast(int)
        fx = windmill_x[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]
        # F[p] = (ti.Matrix.identity(ti.f32, 2) + dt * C[p]) @ F[p] # deformation gradient update
        h = 1.0  # Hardening coefficient: snow gets harder when compressed
        mu, la = mu_0 * h, lambda_0 * h
        U, sig, V = ti.svd(windmill_F)
        J = 1.0
        stress = 2 * mu * (windmill_F - U @ V.T()) @ windmill_F.T(
        ) + ti.Matrix.identity(ti.f32, 2) * la * J * (J - 1)
        stress = (-dt * p_vol * 4 * inv_dx * inv_dx) * stress
        affine = stress + p_mass * windmill_C
        for i, j in ti.static(ti.ndrange(
                3, 3)):  # Loop over 3x3 grid node neighborhood
            offset = ti.Vector([i, j])
            dpos = (offset.cast(float) - fx) * dx
            weight = w[i][0] * w[j][1]
            wr = ti.Vector([
                center[None][1] - windmill_x[p][1],
                windmill_x[p][0] - center[None][0]
            ])
            wr = wr / wr.norm()
            wr = wr * omega[None] / 180. * pi
            grid_v[base + offset] += weight * (p_mass * wr + affine @ dpos)
            grid_m[base + offset] += weight * p_mass

    # torque[None] = 0.
    # for p in windmill_x:
    #   base = (windmill_x[p] * inv_dx - 0.5).cast(int)
    #   fx = windmill_x[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]
    #   for i, j in ti.static(ti.ndrange(3, 3)): # Loop over 3x3 grid node neighborhood
    #     offset = ti.Vector([i, j])
    #     dpos = (offset.cast(float) - fx) * dx
    #     weight = w[i][0] * w[j][1]
    #     torque[None] += weight * grid_assit[base + offset].dot(windmill_x[p]-center[None])
    # print(torque[None])

    for i, j in grid_m:
        if grid_m[i, j] > 0:  # No need for epsilon here
            grid_v[i,
                   j] = (1 / grid_m[i, j]) * grid_v[i,
                                                    j]  # Momentum to velocity
            grid_v[i, j][1] -= dt * 50  # gravity
            if i < 3 and grid_v[i, j][0] < 0:
                grid_v[i, j][0] = 0  # Boundary conditions
            if i > n_grid - 3 and grid_v[i, j][0] > 0: grid_v[i, j][0] = 0
            if j < 3 and grid_v[i, j][1] < 0: grid_v[i, j][1] = 0
            if j > n_grid - 3 and grid_v[i, j][1] > 0: grid_v[i, j][1] = 0
    for p in x:  # grid to particle (G2P)
        base = (x[p] * inv_dx - 0.5).cast(int)
        fx = x[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(ti.f32, 2)
        new_C = ti.Matrix.zero(ti.f32, 2, 2)
        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_v[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)
        v[p], C[p] = new_v, new_C
        x[p] += dt * v[p]  # advection

    torque[None] = 0.
    for p in windmill_x:
        base = (x[p] * inv_dx - 0.5).cast(int)
        fx = x[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(ti.f32, 2)
        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_v[base + ti.Vector([i, j])]
            weight = w[i][0] * w[j][1]
            new_v += weight * g_v
        wr = ti.Vector([
            center[None][1] - windmill_x[p][1],
            windmill_x[p][0] - center[None][0]
        ])
        wr = wr * omega[None] / 180. * pi
        new_v = new_v - wr
        torque[None] += new_v.dot(windmill_x[p] - center[None])
    print(torque[None])
Пример #29
0
    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
Пример #30
0
def substep():
    for i, j in grid_m:
        grid_v[i, j] = [0, 0]
        grid_m[i, j] = 0
    for p in x:
        base = (x[p] * inv_dx - 0.5).cast(int)
        fx = x[p] * inv_dx - base.cast(float)
        w = [
            0.5 * ti.sqr(1.5 - fx), 0.75 - ti.sqr(fx - 1),
            0.5 * ti.sqr(fx - 0.5)
        ]
        F[p] = (ti.Matrix.identity(ti.f32, 2) + dt * C[p]) @ F[p]
        h = ti.exp(10 * (1.0 - Jp[p]))
        if material[p] == 0:
            h = 0.3
        mu, la = mu_0 * h, lambda_0 * h
        U, sig, V = ti.svd(F[p])
        J = 1.0
        for d in ti.static(range(2)):
            new_sig = sig[d, d]
            if material[p] == 1:
                new_sig = min(max(sig[d, d], 1 - 2.5e-2), 1 + 4.5e-3)
            Jp[p] *= sig[d, d] / new_sig
            sig[d, d] = new_sig
            J *= new_sig
        if material[p] == 1:
            F[p] = U @ sig @ V.T()
        stress = 2 * mu * (F[p] - U @ V.T()) @ F[p].T() + ti.Matrix.identity(
            ti.f32, 2) * la * J * (J - 1)
        stress = (-dt * p_vol * 4 * inv_dx * inv_dx) * stress
        affine = stress + p_mass * C[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_v[base + offset] += weight * (p_mass * v[p] + affine @ dpos)
            grid_m[base + offset] += weight * p_mass
    for i, j in grid_m:
        if grid_m[i, j] > 0:
            grid_v[i, j] = (1 / grid_m[i, j]) * grid_v[i, j]
            grid_v[i, j][1] -= dt * 50
            if i < 3 and grid_v[i, j][0] < 0: grid_v[i, j][0] = 0
            if i > n_grid - 3 and grid_v[i, j][0] > 0: grid_v[i, j][0] = 0
            if j < 3 and grid_v[i, j][1] < 0: grid_v[i, j][1] = 0
            if j > n_grid - 3 and grid_v[i, j][1] > 0: grid_v[i, j][1] = 0
    for p in x:
        base = (x[p] * inv_dx - 0.5).cast(int)
        fx = x[p] * inv_dx - base.cast(float)
        w = [
            0.5 * ti.sqr(1.5 - fx), 0.75 - ti.sqr(fx - 1.0),
            0.5 * ti.sqr(fx - 0.5)
        ]
        new_v = ti.Vector.zero(ti.f32, 2)
        new_C = ti.Matrix.zero(ti.f32, 2, 2)
        for i, j in ti.static(ti.ndrange(3, 3)):
            dpos = ti.Vector([i, j]).cast(float) - fx
            g_v = grid_v[base + ti.Vector([i, j])]
            weight = w[i][0] * w[j][1]
            new_v += weight * g_v
            new_C += 4 * inv_dx * weight * ti.outer_product(g_v, dpos)
        v[p], C[p] = new_v, new_C
        x[p] += dt * v[p]