diff --git a/README.md b/README.md index 2a527c7..6c2bdb4 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ numpy==1.21.5 seaborn==0.12.2 ``` ## Usage -### Single Terahedron +### Single Triangle ```shell python main_single_2d.py ``` @@ -55,7 +55,7 @@ Args -### Multiple Terahedrons +### Multiple Cubes ```shell python main_multiple_2d.py ``` diff --git a/fem_2d.py b/fem_2d.py index 074cc44..2708a88 100644 --- a/fem_2d.py +++ b/fem_2d.py @@ -1,5 +1,4 @@ from matplotlib import pyplot as plt -from matplotlib.patches import Wedge from matplotlib import cm import numpy as np import imageio @@ -8,7 +7,7 @@ model_list = {0:'Linear', 1:'STVK', 2:'Co-rotated', 3: 'Neohookean'} -def rect_collision(point, rect_pos): +def judge(point, rect_pos): if point[0] < rect_pos[0][0]: return False if point[1] < rect_pos[0][1]: @@ -21,19 +20,14 @@ def rect_collision(point, rect_pos): # Mesh def get_mesh(args): - # Element的索引,每一个element包含三个点的索引 element_idx = np.zeros((args.element_num,3),dtype = int) - - # 为每个节点构建对应的index cnt = 0 for j in range(args.N_y-1): for i in range(args.N_x-1): - # 第1个三角形,下三角 idx = j * args.N_x + i element_idx[cnt,0] = idx element_idx[cnt,1] = idx + 1 element_idx[cnt,2] = idx + args.N_x - # 第2个三角形,上三角 cnt += 1 element_idx[cnt,0] = idx + 1 element_idx[cnt,1] = idx + args.N_x + 1 @@ -72,11 +66,6 @@ def get_area(points): return 0.5 * (points[0,0] * (points[1,1] - points[2,1]) + points[1,0] * (points[2,1] - points[0,1]) + points[2,0] * (points[0,1] - points[1,1])) - -def vec_to_array(vec, array): - for i in range(vec.shape[0]): - array[:,i] = vec[i].flatten("F") - return array # Linear elasity def linear_elasity(F,mu,lam): @@ -96,7 +85,7 @@ def linear_elasity(F,mu,lam): def stvk(F,mu,lam): # Green strain tensor e = 1/2(F^TF-I) strain = (np.dot(F.T,F) - np.identity(2)) * 0.5 - # Calculate Energy density function for updating position + # Calculate Energy density doubleInner = strain[0,0]*strain[0,0] + strain[1,0]*strain[1,0] + strain[0,1]*strain[0,1] + strain[1,1]*strain[1,1] # Ψ(F) = µe:e + λ/2 x trace^2(strain) energy = doubleInner * mu + lam * 0.5 * (strain[0,0] + strain[1,1]) ** 2 @@ -160,6 +149,7 @@ def implicit_euler_stvk(args, B_m, F, strain, nodal_force,nodal_vel, multiple=Fa # Implicit Euler # ∂D_s/∂x,其中D_s是一个2x2的矩阵,x是一个3x2的矩阵(仨点俩维度), 因此计算出来是一个2x2x3x2的四阶张量 # 使用向量化进行计算的话可以得到每一列,向量化后D_s是一个长度为4的向量,x是一个长度为6的向量,求Jacobin,分子主序,最后矩阵是4x6 + # 为了方便起见,分开来算 dDsdx = np.zeros((6,2,2)) dDsdx[0,:,:] = np.array([[-1,-1],[0,0]]) @@ -187,16 +177,10 @@ def implicit_euler_stvk(args, B_m, F, strain, nodal_force,nodal_vel, multiple=Fa if multiple: return dH # K - array_H = vec_to_array(vec=dH,array=np.zeros((4,6))) - K = np.zeros((6,6)) - # 3 个顶点 for n in range(3): - # 2 个维度 for d in range(2): - # 第 idx 列,每列3 x 2 个元素 idx = n * 2 + d - # 先填写第一第二个顶点,第零个顶点之后填 K[2,idx] += dH[idx,0,0] K[3,idx] += dH[idx,1,0] K[4,idx] += dH[idx,0,1] @@ -206,11 +190,13 @@ def implicit_euler_stvk(args, B_m, F, strain, nodal_force,nodal_vel, multiple=Fa K[1,idx] += - dH[idx,1,0] - dH[idx,1,1] # A A = args.mass * np.identity(6) - K * args.dt * args.dt + # b b = np.zeros((6)) for n in range(3): for d in range(2): b[n*2+d] = args.mass * nodal_vel[n,d] + args.dt * nodal_force[n,d] + # X X = np.dot(np.linalg.inv(A), b) for n in range(3): @@ -224,8 +210,6 @@ def implicit_euler_stvk(args, B_m, F, strain, nodal_force,nodal_vel, multiple=Fa # Implict Euler method for Neohookean def implicit_euler_neohookean(args, B_m, F, nodal_force,nodal_vel,multiple=False): # Implicit Euler - # ∂D_s/∂x,其中D_s是一个2x2的矩阵,x是一个3x2的矩阵(仨点俩维度), 因此计算出来是一个2x2x3x2的四阶张量 - # 使用向量化进行计算的话可以得到每一列,向量化后D_s是一个长度为4的向量,x是一个长度为6的向量,求Jacobin,分子主序,最后矩阵是4x6 dDsdx = np.zeros((6,2,2)) dDsdx[0,:,:] = np.array([[-1,-1],[0,0]]) @@ -253,16 +237,10 @@ def implicit_euler_neohookean(args, B_m, F, nodal_force,nodal_vel,multiple=False if multiple: return dH # K - array_H = vec_to_array(vec=dH,array=np.zeros((4,6))) - K = np.zeros((6,6)) - # 3 个顶点 for n in range(3): - # 2 个维度 for d in range(2): - # 第 idx 列,每列3 x 2 个元素 idx = n * 2 + d - # 先填写第一第二个顶点,第零个顶点之后填 K[2,idx] += dH[idx,0,0] K[3,idx] += dH[idx,1,0] K[4,idx] += dH[idx,0,1] @@ -272,11 +250,13 @@ def implicit_euler_neohookean(args, B_m, F, nodal_force,nodal_vel,multiple=False K[1,idx] += - dH[idx,1,0] - dH[idx,1,1] # A A = args.mass * np.identity(6) - K * args.dt * args.dt + # b b = np.zeros((6)) for n in range(3): for d in range(2): b[n*2+d] = args.mass * nodal_vel[n,d] + args.dt * nodal_force[n,d] + # X X = np.dot(np.linalg.inv(A), b) for n in range(3): @@ -287,7 +267,7 @@ def implicit_euler_neohookean(args, B_m, F, nodal_force,nodal_vel,multiple=False return nodal_vel -# Make Ax=b for Implict Euler method +# Naive Solver for Implict Euler method def solver(args,A,K,b,node_vel,node_force): for i in range(args.krow): for j in range(args.krow): @@ -302,7 +282,7 @@ def solver(args,A,K,b,node_vel,node_force): X = np.dot(np.linalg.inv(A),b) return X -# Simulation for a single tetrahedron +# Simulation for a single triangle def run_fem_simulation_single(args): # Dir for saving imgs try: @@ -337,7 +317,7 @@ def run_fem_simulation_single(args): # Position after deformation x = Afine_2D(args.ref_node_pos, args.theta, args.tx, args.ty, args.sx, args.sy) - # Plot the triangular after deformation + # Plot the triangle after deformation plt.fill(x[:,0], x[:,1], 'b') plt.text(0.01, 6.8, f'Init Area: {init_area}', fontsize=15, color='red') plt.text(1.8, 6.8, f'mu: {round(mu,2)}, lamda: {round(lam,2)}', fontsize=15, color='blue') @@ -482,10 +462,7 @@ def run_fem_simulation_single(args): imageio.mimsave(f'./gifs/single_2D_{model_list[args.model]}_implicit_{args.implict}.gif', images, duration=0.01) shutil.rmtree("./images") - - # Simulation - -# Simulation for multiple tetrahedrons +# Simulation for multiple cubes def run_fem_simulation_multiple(args): # Make dirs try: @@ -512,10 +489,13 @@ def run_fem_simulation_multiple(args): # Nodes # Init position,left down coroner init_x, init_y = 0.0, 4.0 + # dx cube_len = 1.0 + # Number of nodes (x-axis) N_x = args.voxelx+1 + # Number of nodes (y-axis) N_y = args.voxely+1 args.N_x = N_x @@ -530,7 +510,7 @@ def run_fem_simulation_multiple(args): # Mesh element_idx = get_mesh(args) - # Position,velosity and force + # Position, velosity and force node_pos = np.zeros((node_num,2),dtype = float) node_vel = np.zeros((node_num,2),dtype = float) nodal_force = np.zeros((node_num,2),dtype = float) @@ -610,10 +590,13 @@ def run_fem_simulation_multiple(args): raise H = -init_area * np.dot(piola,D_m[ie].transpose()) + # Force 1, the first column of H f1 = np.array([H[0,0],H[1,0]]) + # Force 2, the second column of H f2 = np.array([H[0,1],H[1,1]]) + # Force 0, balanced force f0 = - f1 - f2 @@ -625,8 +608,10 @@ def run_fem_simulation_multiple(args): if args.model == 1: dH = implicit_euler_stvk(args, D_m[ie], F, strain, nodal_force, node_vel, multiple=True) elif args.model == 3: - dH = implicit_euler_neohookean(args, D_m[ie], F, nodal_force, node_vel,multiple=True) + dH = implicit_euler_neohookean(args, D_m[ie], F, nodal_force, node_vel, multiple=True) + # 3 nodes per element for n in range(3): + # node index nidx = element_idx[ie,n] for d in range(2): kidx = nidx * 2 + d @@ -644,7 +629,7 @@ def run_fem_simulation_multiple(args): idx = element_idx[ie,0] * 2 + 1 K[idx,kidx] += - dH[didx,1,0] - dH[didx,1,1] - '''Step 6. Update positions of three points''' + '''Step 6. Update position''' # Update if not args.implict: for i in range(node_num): @@ -655,13 +640,14 @@ def run_fem_simulation_multiple(args): # Add Gravity for i in range(node_num): nodal_force[i] += Gravity + # Implict Euler X = solver(args,A,K,b,node_vel,nodal_force) for i in range(node_num): node_vel[i,0] = X[i * 2 + 0] node_vel[i,1] = X[i * 2 + 1] node_pos[i,:] += node_vel[i,:] * args.dt - node_vel[i] *= np.exp(-args.dt * 5.0) + # node_vel[i] *= np.exp(-args.dt * 1.0) # bend if args.mode == "bend": @@ -672,20 +658,13 @@ def run_fem_simulation_multiple(args): # Boundary Condition for i in range(node_num): - # Ball boundary condition - # 圆心指向点的向量 + # Ball disp = node_pos[i] - args.ball_pos - # 点距圆心的距离 disp2 = np.sum(np.square(disp)) - # 如果小于半径,说明有碰撞 if disp2 <= args.ball_radius**2: - # 内积,获得速度在该向量上的投影 NoV = node_vel[i].dot(disp) - # <0,说明在挤压这个圆 if NoV < 0: - # 把速度掰成相切的 node_vel[i] -= NoV * disp / disp2 - # Rectangle boundary condition # if rect_collision(node_pos[i], args.rectangle_pos): # if args.rectangle_pos[0][0] < node_pos[i][0] < (args.rectangle_pos[0][0]+args.rectangle_pos[1][0]): diff --git a/gifs/Multiple_2D_Neohookean_implicit_True_fall_2.gif b/gifs/Multiple_2D_Neohookean_implicit_True_fall_2.gif new file mode 100644 index 0000000..cb8af83 Binary files /dev/null and b/gifs/Multiple_2D_Neohookean_implicit_True_fall_2.gif differ diff --git a/gifs/Multiple_2D_STVK_implicit_False_fall_8.gif b/gifs/Multiple_2D_STVK_implicit_False_fall_8.gif index e8befb3..249a06d 100644 Binary files a/gifs/Multiple_2D_STVK_implicit_False_fall_8.gif and b/gifs/Multiple_2D_STVK_implicit_False_fall_8.gif differ diff --git a/gifs/Multiple_2D_STVK_implicit_True_fall_0.gif b/gifs/Multiple_2D_STVK_implicit_True_fall_0.gif new file mode 100644 index 0000000..1d7f73a Binary files /dev/null and b/gifs/Multiple_2D_STVK_implicit_True_fall_0.gif differ diff --git a/main_multiple_2d.py b/main_multiple_2d.py index a55a58b..b8aac66 100644 --- a/main_multiple_2d.py +++ b/main_multiple_2d.py @@ -1,7 +1,7 @@ +from fem_2d import run_fem_simulation_multiple from matplotlib import pyplot as plt -import seaborn as sns from matplotlib.patches import Wedge -from fem_2d import run_fem_simulation_multiple +import seaborn as sns import numpy as np import argparse @@ -27,9 +27,9 @@ help='Empty voxels:[],[2,3,6,7,10,11,14,15],[2,3,6,7]') # Parameters of FEM - parser.add_argument('--mode', type=str, default="bend", + parser.add_argument('--mode', type=str, default="fall", help='Mode: bend,fall') - parser.add_argument('--model', type=int, default=0, + parser.add_argument('--model', type=int, default=3, help='Constitutive model, 0:Linear, 1:STVK, 2:Co-rotated, 3: Neohookean') parser.add_argument('--Y', type=int, default=10000, help='Youngs modulus') @@ -41,7 +41,7 @@ help='Iteration') parser.add_argument('--mass', type=float, default=1.0, help='Mass of the point') - parser.add_argument('--implict', action='store_true', + parser.add_argument('--implict', action="store_true", help='Implict Euler Method, for STVK and Neohookean') args = parser.parse_args()