干货其他 干货详情

Deepseek手把手教学编写有限元代码

活着48
有限元法有限元分析数值模拟计算力学弹性力学结构分析应力分析位移分析网格划分四边形单元刚度矩阵边界条件开孔板结构强度分析均布载荷

   用deepseek直接从造轮子开始实现一个有限元的全过程的分析。学过有限元或者弹性力学的朋友肯定对带开孔的矩形板的受力分析并不陌生,这一次我们以这个问题来进行测试。

图1 有限元模型示意图

一、提问问题如下:

二、deepseek的思考过程

   deepseek经过了391秒的思考,(这里省去复杂的中间思考过程),得出了如下主要步骤:

  • 现在,我需要将这些步骤组织成一个类结构,类名为fea,包含各个方法。例如:
  • - __init__方法初始化参数,如几何尺寸、材料属性、网格划分参数等。
  • - generate_nodes方法生成节点,并处理孔的存在,排除孔内的节点。
  • - generate_elements方法生成单元,排除那些包含孔内节点的单元。
  • - 或者,另一种方式,先生成所有可能的节点,然后生成单元时检查单元是否在孔内,如果在则排除。
  • - 然后,计算每个单元的刚度矩阵,并组装到总刚度矩阵。
  • - 处理边界条件,如固定左端的x方向位移。
  • - 组装载荷向量,施加均布力到右端节点。
  • - 求解方程组得到位移。
  • - 计算应力。
  • - 绘制变形和应力云图。

   再次进一步思考每个方法的实现细节,主要技术特征包括了:

  • 将整个板划分为细小的四边形单元
  • 移除中心孔区域的单元,
  • x方向从0到10,y方向从0到5,步长dx和 dy根据划分的数目确定
  • 如此使用若干个四边形来近似模拟开孔圆形再次省略deepseek的分析过程,直接给出生成的代码:

import numpy as npimport matplotlib.pyplot as pltfrom matplotlib.collections import PolyCollectionclass FEA:    def __init__(self):        # 几何参数        self.L = 10.0       # 长度 (mm)        self.W = 5.0        # 宽度 (mm)        self.thickness = 1  # 厚度 (mm)        self.hole_r = 0.5   # 孔半径 (mm)        # 材料参数        self.E = 1e10       # 弹性模量 (Pa)        self.nu = 0.2       # 泊松比        # 载荷参数        self.pressure = 10  # 均布载荷 (N/mm)        # 网格参数        self.nx = 41        # X方向网格数        self.ny = 21        # Y方向网格数        # 初始化数据结构        self.nodes = []        self.elements = []        self.dofs = 2       # 每个节点自由度        self.K = None       # 全局刚度矩阵        self.F = None       # 载荷向量        self.U = None       # 位移向量    def generate_mesh(self):        """生成带孔的网格"""        # 生成原始网格        x = np.linspace(0, self.L, self.nx)        y = np.linspace(0, self.W, self.ny)        nodes = np.array(np.meshgrid(x, y)).T.reshape(-1, 2)        # 过滤孔区域节点        center = np.array([self.L/2, self.W/2])        distances = np.linalg.norm(nodes - center, axis=1)        valid_nodes = nodes[distances > self.hole_r]        self.node_indices = {tuple(pos):i for i, pos in enumerate(valid_nodes)}        # 生成四边形单元        elements = []        for j in range(self.ny-1):            for i in range(self.nx-1):                n1 = (x[i], y[j])                n2 = (x[i+1], y[j])                n3 = (x[i+1], y[j+1])                n4 = (x[i], y[j+1])                try:                    e = [self.node_indices[tuple(n1)],                          self.node_indices[tuple(n2)],                         self.node_indices[tuple(n3)],                          self.node_indices[tuple(n4)]]                    elements.append(e)                except KeyError:                    continue        self.nodes = valid_nodes        self.elements = np.array(elements)        self.num_nodes = len(self.nodes)    def create_D_matrix(self):        """创建弹性矩阵"""        E = self.E        nu = self.nu        self.D = E/(1-nu**2) * np.array([            [1,  nu, 0],            [nu, 1,  0],            [0,  0,  (1-nu)/2]        ])    def quad_shape_func(self, xi, eta):        """四边形单元形函数"""        return 0.25*np.array([            (1-xi)*(1-eta),            (1+xi)*(1-eta),            (1+xi)*(1+eta),            (1-xi)*(1+eta)        ])    def quad_B_matrix(self, xi, eta, coord):        """计算B矩阵"""        dN = np.array([            [eta-1, 1-eta, 1+eta, -eta-1],            [xi-1, -xi-1, xi+1, 1-xi]        ]) / 4        J = dN @ coord        invJ = np.linalg.inv(J)        dN_dx = invJ @ dN        B = np.zeros((3, 8))        for i in range(4):            B[0, 2*i] = dN_dx[0, i]            B[1, 2*i+1] = dN_dx[1, i]            B[2, 2*i] = dN_dx[1, i]            B[2, 2*i+1] = dN_dx[0, i]        return B, np.linalg.det(J)    def element_stiffness(self, element):        """计算单元刚度矩阵"""        ke = np.zeros((8, 8))        coord = self.nodes[element]        gauss_points = [(-1/np.sqrt(3), -1/np.sqrt(3), 1),                        (1/np.sqrt(3), -1/np.sqrt(3), 1),                        (1/np.sqrt(3), 1/np.sqrt(3), 1),                        (-1/np.sqrt(3), 1/np.sqrt(3), 1)]        for gp in gauss_points:            B, detJ = self.quad_B_matrix(gp[0], gp[1], coord)            ke += B.T @ self.D @ B * detJ * gp[2] * self.thickness        return ke    def assemble_global_stiffness(self):        """组装全局刚度矩阵"""        n_dof = self.num_nodes * self.dofs        self.K = np.zeros((n_dof, n_dof))        for e in self.elements:            ke = self.element_stiffness(e)            for i in range(4):                ni = e[i]                for j in range(4):                    nj = e[j]                    for di in range(2):                        for dj in range(2):                            row = 2*ni + di                            col = 2*nj + dj                            self.K[row, col] += ke[2*i+di, 2*j+dj]    def apply_boundary_conditions(self):        """施加边界条件"""        # 找到左侧边界节点        left_nodes = [i for i, n in enumerate(self.nodes) if n[0] == 0]        for n in left_nodes:            dof = 2*n            self.K[dof, :] = 0            self.K[:, dof] = 0            self.K[dof, dof] = 1    def apply_loads(self):        """施加载荷"""        self.F = np.zeros(self.num_nodes*2)        right_nodes = [i for i, n in enumerate(self.nodes) if n[0] == self.L]        dx = self.L / (self.nx-1)        for n in right_nodes:            self.F[2*n] = self.pressure * dx    def solve(self):        """求解系统方程"""        self.U = np.linalg.solve(self.K, self.F)    def plot_results(self):        """结果可视化"""        # 变形云图        plt.figure(figsize=(12, 5))        plt.subplot(121)        disp = np.linalg.norm(self.U.reshape(-1, 2), axis=1)        plt.tricontourf(self.nodes[:,0], self.nodes[:,1], disp, levels=20)        plt.axis("equal")     # X和Y轴缩放比例一致        plt.colorbar(label='Displacement Magnitude (mm)')        plt.title('Deformation Cloud Map')        # 应力云图        plt.subplot(122)        stress = []        for e in self.elements:            coord = self.nodes[e]            B, _ = self.quad_B_matrix(0, 0, coord)  # 单元中心            u = self.U[[2*e[i]+j for i in range(4) for j in [0,1]]]            stress.append(np.linalg.norm(self.D @ B @ u))        poly = PolyCollection([self.nodes[e][:,[0,1]] for e in self.elements],                             array=stress, cmap='jet')        plt.gca().add_collection(poly)        plt.colorbar(poly, label='Stress (Pa)')        plt.xlim(0, self.L)        plt.ylim(0, self.W)        plt.title('Stress Cloud Map')        plt.tight_layout()        plt.axis("equal")     # X和Y轴缩放比例一致        plt.show()def main():    model = FEA()    model.generate_mesh()    model.create_D_matrix()    model.assemble_global_stiffness()    model.apply_loads()    model.apply_boundary_conditions()    model.solve()    model.plot_results()if __name__ == "__main__":    main()

   代码经过运行,仅174行缺少“]”,补充该括弧后,代码可直接运行。那么结果如何?我们看看:

图2 deepseek提供代码生成的位移云图

图3 deepseek提供代码生成的应力云图

   可以看出代码得出了结果,并且趋势上也比较熟悉。那么这个代码可以将网格划分加密么?当然可以,仅需调整网格密度,我们提高一下__init__()函数中的变量即可,比如加密4倍:

        # 网格参数        self.nx = 161        # X方向网格数        self.ny = 81        # Y方向网格数

   同时在云图输出的时候,控制x与y轴比例一致,即增加如下代码:

plt.axis("equal")     # X和Y轴缩放比例一致

   接下来看改进代码后的输出结果:

图4 deepseek提供代码生成的位移云图(加密网格)

图5 deepseek提供代码生成的应力云图(加密网格)

三、关于此次测评的点评

    deepseek能够给出详细的分析过程,包括了比较完整的有限元求解步骤,可以根据这一步骤理解有限元求解的基本思想和过程;能够根据提问的要求,将代码进行了封装,代码模块分工清晰;对于复杂的几何形状,采用了矩形网格去无穷逼近外形的思路。当然也有相对不足之处,比如位移云图中开孔变形未能处理等等。

    那么这个计算结果靠谱么?感兴趣的朋友可以拿起你的工具来验证一下吧。

   

评论

0/1000发布评论
全部评论

活着

关注
TA的主页

干货推荐