Skip to content

Commit c3ff981

Browse files
committed
subspace simulation
1 parent 5c05ce4 commit c3ff981

3 files changed

Lines changed: 66 additions & 7 deletions

File tree

9_reduced_DOF/simulator.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import pygame # pygame for visualization
55
pygame.init()
66

7+
import utils
78
import square_mesh # square mesh
89
import time_integrator
910

@@ -33,6 +34,8 @@
3334
# ANCHOR: contact_area
3435
contact_area = [side_len / n_seg] * len(x) # perimeter split to each node
3536
# ANCHOR_END: contact_area
37+
# compute reduced basis using 0: polynomial functions or 1: modal reduction
38+
reduced_basis = utils.compute_reduced_basis(x, e, l2, k, is_DBC, method=0, order=3)
3639

3740
# simulation with visualization
3841
resolution = np.array([900, 900])
@@ -64,7 +67,7 @@ def screen_projection(x):
6467
pygame.display.flip() # flip the display
6568

6669
# step forward simulation and wait for screen refresh
67-
[x, v] = time_integrator.step_forward(x, e, v, m, l2, k, y_ground, contact_area, is_DBC, h, 1e-2)
70+
[x, v] = time_integrator.step_forward(x, e, v, m, l2, k, y_ground, contact_area, is_DBC, reduced_basis, h, 1e-2)
6871
time_step += 1
6972
pygame.time.wait(int(h * 1000))
7073
square_mesh.write_to_file(time_step, x, n_seg)

9_reduced_DOF/time_integrator.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@
1111
import GravityEnergy
1212
import BarrierEnergy
1313

14-
def step_forward(x, e, v, m, l2, k, y_ground, contact_area, is_DBC, h, tol):
14+
def step_forward(x, e, v, m, l2, k, y_ground, contact_area, is_DBC, reduced_basis, h, tol):
1515
x_tilde = x + v * h # implicit Euler predictive position
1616
x_n = copy.deepcopy(x)
1717

1818
# Newton loop
1919
iter = 0
2020
E_last = IP_val(x, e, x_tilde, m, l2, k, y_ground, contact_area, h)
21-
p = search_dir(x, e, x_tilde, m, l2, k, y_ground, contact_area, is_DBC, h)
21+
p = search_dir(x, e, x_tilde, m, l2, k, y_ground, contact_area, is_DBC, reduced_basis, h)
2222
while LA.norm(p, inf) / h > tol:
2323
print('Iteration', iter, ':')
2424
print('residual =', LA.norm(p, inf) / h)
@@ -33,7 +33,7 @@ def step_forward(x, e, v, m, l2, k, y_ground, contact_area, is_DBC, h, tol):
3333

3434
x += alpha * p
3535
E_last = IP_val(x, e, x_tilde, m, l2, k, y_ground, contact_area, h)
36-
p = search_dir(x, e, x_tilde, m, l2, k, y_ground, contact_area, is_DBC, h)
36+
p = search_dir(x, e, x_tilde, m, l2, k, y_ground, contact_area, is_DBC, reduced_basis, h)
3737
iter += 1
3838

3939
v = (x - x_n) / h # implicit Euler velocity update
@@ -56,7 +56,7 @@ def IP_hess(x, e, x_tilde, m, l2, k, y_ground, contact_area, h):
5656
H = sparse.coo_matrix((IJV[2], (IJV[0], IJV[1])), shape=(len(x) * 2, len(x) * 2)).tocsr()
5757
return H
5858

59-
def search_dir(x, e, x_tilde, m, l2, k, y_ground, contact_area, is_DBC, h):
59+
def search_dir(x, e, x_tilde, m, l2, k, y_ground, contact_area, is_DBC, reduced_basis, h):
6060
projected_hess = IP_hess(x, e, x_tilde, m, l2, k, y_ground, contact_area, h)
6161
reshaped_grad = IP_grad(x, e, x_tilde, m, l2, k, y_ground, contact_area, h).reshape(len(x) * 2, 1)
6262
# eliminate DOF by modifying gradient and Hessian for DBC:
@@ -66,4 +66,6 @@ def search_dir(x, e, x_tilde, m, l2, k, y_ground, contact_area, is_DBC, h):
6666
for i in range(0, len(x)):
6767
if is_DBC[i]:
6868
reshaped_grad[i * 2] = reshaped_grad[i * 2 + 1] = 0.0
69-
return spsolve(projected_hess, -reshaped_grad).reshape(len(x), 2)
69+
reduced_hess = reduced_basis.T.dot(projected_hess.dot(reduced_basis)) # applying chain rule
70+
reduced_grad = reduced_basis.T.dot(reshaped_grad) # applying chain rule
71+
return (reduced_basis.dot(spsolve(reduced_hess, -reduced_grad))).reshape(len(x), 2) # transform to full space after the solve

9_reduced_DOF/utils.py

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,63 @@
11
import numpy as np
22
import numpy.linalg as LA
33

4+
import scipy.sparse as sparse
5+
from scipy.sparse.linalg import eigsh
6+
7+
import MassSpringEnergy
8+
49
def make_PSD(hess):
510
[lam, V] = LA.eigh(hess) # Eigen decomposition on symmetric matrix
611
# set all negative Eigenvalues to 0
712
for i in range(0, len(lam)):
813
lam[i] = max(0, lam[i])
9-
return np.matmul(np.matmul(V, np.diag(lam)), np.transpose(V))
14+
return np.matmul(np.matmul(V, np.diag(lam)), np.transpose(V))
15+
16+
def compute_reduced_basis(x, e, l2, k, is_DBC, method, order):
17+
if method == 0: # polynomial basis
18+
if order == 1: # linear basis, or affine basis
19+
basis = np.zeros((len(x) * 2, 6)) # 1, x, y for both x- and y-displacements
20+
for i in range(len(x)):
21+
for d in range(2):
22+
if not is_DBC[i]: # ignore the floor DOF
23+
basis[i * 2 + d][d * 3] = 1
24+
basis[i * 2 + d][d * 3 + 1] = x[i][0]
25+
basis[i * 2 + d][d * 3 + 2] = x[i][1]
26+
elif order == 2: # quadratic polynomial basis
27+
basis = np.zeros((len(x) * 2, 12)) # 1, x, y, x^2, xy, y^2 for both x- and y-displacements
28+
for i in range(len(x)):
29+
for d in range(2):
30+
if not is_DBC[i]: # ignore the floor DOF
31+
basis[i * 2 + d][d * 6] = 1
32+
basis[i * 2 + d][d * 6 + 1] = x[i][0]
33+
basis[i * 2 + d][d * 6 + 2] = x[i][1]
34+
basis[i * 2 + d][d * 6 + 3] = x[i][0] * x[i][0]
35+
basis[i * 2 + d][d * 6 + 4] = x[i][0] * x[i][1]
36+
basis[i * 2 + d][d * 6 + 5] = x[i][1] * x[i][1]
37+
elif order == 3: # cubic polynomial basis
38+
basis = np.zeros((len(x) * 2, 20)) # 1, x, y, x^2, xy, y^2, x^3, x^2y, xy^2, y^3 for both x- and y-displacements
39+
for i in range(len(x)):
40+
for d in range(2):
41+
if not is_DBC[i]: # ignore the floor DOF
42+
basis[i * 2 + d][d * 10] = 1
43+
basis[i * 2 + d][d * 10 + 1] = x[i][0]
44+
basis[i * 2 + d][d * 10 + 2] = x[i][1]
45+
basis[i * 2 + d][d * 10 + 3] = x[i][0] * x[i][0]
46+
basis[i * 2 + d][d * 10 + 4] = x[i][0] * x[i][1]
47+
basis[i * 2 + d][d * 10 + 5] = x[i][1] * x[i][1]
48+
basis[i * 2 + d][d * 10 + 6] = x[i][0] * x[i][0] * x[i][0]
49+
basis[i * 2 + d][d * 10 + 7] = x[i][0] * x[i][0] * x[i][1]
50+
basis[i * 2 + d][d * 10 + 8] = x[i][0] * x[i][1] * x[i][1]
51+
basis[i * 2 + d][d * 10 + 9] = x[i][1] * x[i][1] * x[i][1]
52+
else:
53+
print("unsupported order of polynomial basis for reduced DOF")
54+
exit()
55+
return basis
56+
else: # modal-order reduction
57+
IJV = MassSpringEnergy.hess(x, e, l2, k) #TODO: no SPD projection, switch to NH
58+
H = sparse.coo_matrix((IJV[2], (IJV[0], IJV[1])), shape=(len(x) * 2, len(x) * 2)).tocsr()
59+
for i, j in zip(*H.nonzero()):
60+
if is_DBC[int(i / 2)] | is_DBC[int(j / 2)]:
61+
H[i, j] = (i == j) # ignore the floor DOF
62+
eigenvalues, eigenvectors = eigsh(H, k=order, which='SM') # get 'order' eigenvectors with smallest eigenvalues
63+
return eigenvectors

0 commit comments

Comments
 (0)