From 4c0cd0a25fc04ac1e30905e4316905d4b8b7d185 Mon Sep 17 00:00:00 2001 From: "brighthe98@gmail.com" Date: Thu, 14 Nov 2024 15:09:22 +0800 Subject: [PATCH 01/63] modify test files --- app/soptx/linear_elasticity/basisi_test.py | 80 ---- .../linear_elasticity/exp_boundary_test.py | 176 --------- .../exp_gearx_node_load_all.py | 199 ---------- .../exp_gearx_node_load_part.py | 199 ---------- ..._1_0.py => exp_part_gearx_15_node_load.py} | 14 +- ... => exp_part_gearx_15_node_load_abaqus.py} | 10 +- ...t_4_0.py => exp_part_gearx_1_node_load.py} | 28 +- ...y => exp_part_gearx_1_node_load_abaqus.py} | 29 +- .../linear_elastic_integrator_data.py | 0 test/fem/test_linear_elastic_integrator.py | 341 ------------------ 10 files changed, 49 insertions(+), 1027 deletions(-) delete mode 100644 app/soptx/linear_elasticity/basisi_test.py delete mode 100644 app/soptx/linear_elasticity/exp_boundary_test.py delete mode 100644 app/soptx/linear_elasticity/exp_gearx_node_load_all.py delete mode 100644 app/soptx/linear_elasticity/exp_gearx_node_load_part.py rename app/soptx/linear_elasticity/{exp_gearx_node_load_part_1_0.py => exp_part_gearx_15_node_load.py} (95%) rename app/soptx/linear_elasticity/{exp_gearx_node_load_part_2_0.py => exp_part_gearx_15_node_load_abaqus.py} (92%) rename app/soptx/linear_elasticity/{exp_gearx_node_load_part_4_0.py => exp_part_gearx_1_node_load.py} (88%) rename app/soptx/linear_elasticity/{exp_gearx_node_load_part_3_0.py => exp_part_gearx_1_node_load_abaqus.py} (83%) rename {test/fem => app/soptx/linear_elasticity}/linear_elastic_integrator_data.py (100%) delete mode 100644 test/fem/test_linear_elastic_integrator.py diff --git a/app/soptx/linear_elasticity/basisi_test.py b/app/soptx/linear_elasticity/basisi_test.py deleted file mode 100644 index 3d24d4f8b..000000000 --- a/app/soptx/linear_elasticity/basisi_test.py +++ /dev/null @@ -1,80 +0,0 @@ -from fealpy.backend import backend_manager as bm - -from fealpy.mesh import HexahedronMesh -from fealpy.functionspace import LagrangeFESpace, TensorFunctionSpace - -import pickle -from app.gearx.gear import ExternalGear, InternalGear -from app.gearx.utils import * - -from fealpy.mesh import HexahedronMesh - -bm.set_backend('numpy') -nx, ny, nz = 1, 1, 1 -mesh_fealpy = HexahedronMesh.from_box(box=[0, 1, 0, 1, 0, 1], - nx=nx, ny=ny, nz=nz, device=bm.get_device('cpu')) -space_fealpy = LagrangeFESpace(mesh_fealpy, p=1, ctype='C') -node_fealpy = mesh_fealpy.entity('node') -cell_fealpy = mesh_fealpy.entity('cell') -cell2dof_fealpy = space_fealpy.cell_to_dof() -# ip = mesh_fealpy.interpolation_points(p=1) -# import matplotlib.pyplot as plt -# fig = plt.figure() -# axes = fig.add_subplot(111, projection='3d') -# mesh_fealpy.add_plot(axes) -# mesh_fealpy.find_node(axes, node=ip, showindex=True) -# mesh_fealpy.find_cell(axes, showindex=True) -# plt.show() - -q = 2 -qf = mesh_fealpy.quadrature_formula(q) -bcs_fealpy, _ = qf.get_quadrature_points_and_weights() -phi_fealpy = space_fealpy.basis(bcs_fealpy) # (1, NQ, ldof) -phi_fealpy_show = phi_fealpy[0] - -with open('/home/heliang/FEALPy_Development/fealpy/app/soptx/linear_elasticity/external_gear_data_part.pkl', 'rb') as f: - data = pickle.load(f) -parameters = data['parameters'] -hex_mesh = data['hex_mesh'] -hex_cell = hex_mesh.cell -hex_node = hex_mesh.node -mesh_gearx = HexahedronMesh(hex_node, hex_cell) - -space_gearx = LagrangeFESpace(mesh_gearx, p=1, ctype='C') - -cell_gearx = mesh_gearx.entity('cell') -cell2dof_gearx = space_gearx.cell_to_dof() - -u = parameters[..., 0] -v = parameters[..., 1] -w = parameters[..., 2] - -u = bm.clip(u, 0, 1) -v = bm.clip(v, 0, 1) -w = bm.clip(w, 0, 1) - -bcs_gearxs = [ - ( - # bm.tensor([[1 - u, u]]), - # bm.tensor([[1 - v, v]]), - # bm.tensor([[1 - w, w]]) - bm.tensor([[u, 1 - u]]), - bm.tensor([[v, 1 - v]]), - bm.tensor([[w, 1 - w]]) - ) - for u, v, w in zip(u, v, w) -] -for idx, (u_tensor, v_tensor, w_tensor) in enumerate(bcs_gearxs): - u_values = u_tensor.flatten() - v_values = v_tensor.flatten() - w_values = w_tensor.flatten() - print(f"载荷点 {idx + 1} 的重心坐标:\n u,1-u = {u_values}, v,1-v = {v_values}, w,1-w = {w_values}") - - -phi_gearxs = [] -for bcs_gearx in bcs_gearxs: - phi_gearx = space_gearx.basis(bcs_gearx) - phi_gearxs.append(phi_gearx) -for idx, phi_gearx in enumerate(phi_gearxs): - print(f"载荷点 {idx + 1} 处的基函数值:\n", phi_gearx.flatten()) -print("------------------------") \ No newline at end of file diff --git a/app/soptx/linear_elasticity/exp_boundary_test.py b/app/soptx/linear_elasticity/exp_boundary_test.py deleted file mode 100644 index d2c64bdc6..000000000 --- a/app/soptx/linear_elasticity/exp_boundary_test.py +++ /dev/null @@ -1,176 +0,0 @@ -from fealpy.backend import backend_manager as bm - -from fealpy.mesh import HexahedronMesh -from fealpy.material.elastic_material import LinearElasticMaterial -from fealpy.functionspace import LagrangeFESpace, TensorFunctionSpace -from fealpy.typing import TensorLike -from fealpy.decorator import cartesian -from fealpy.sparse import COOTensor -from fealpy.solver import cg - -class BoxDomainPolyLoaded3d(): - def domain(self): - return [0, 1, 0, 1, 0, 1] - - @cartesian - def source(self, points: TensorLike): - x = points[..., 0] - y = points[..., 1] - z = points[..., 2] - val = bm.zeros(points.shape, - dtype=points.dtype, device=bm.get_device(points)) - mu = 1 - factor1 = -400 * mu * (2 * y - 1) * (2 * z - 1) - term1 = 3 * (x ** 2 - x) ** 2 * (y ** 2 - y + z ** 2 - z) - term2 = (1 - 6 * x + 6 * x ** 2) * (y ** 2 - y) * (z ** 2 - z) - val[..., 0] = factor1 * (term1 + term2) - - factor2 = 200 * mu * (2 * x - 1) * (2 * z - 1) - term1 = 3 * (y ** 2 - y) ** 2 * (x ** 2 - x + z ** 2 - z) - term2 = (1 - 6 * y + 6 * y ** 2) * (x ** 2 - x) * (z ** 2 - z) - val[..., 1] = factor2 * (term1 + term2) - - factor3 = 200 * mu * (2 * x - 1) * (2 * y - 1) - term1 = 3 * (z ** 2 - z) ** 2 * (x ** 2 - x + y ** 2 - y) - term2 = (1 - 6 * z + 6 * z ** 2) * (x ** 2 - x) * (y ** 2 - y) - val[..., 2] = factor3 * (term1 + term2) - - return val - - @cartesian - def solution(self, points: TensorLike): - x = points[..., 0] - y = points[..., 1] - z = points[..., 2] - val = bm.zeros(points.shape, - dtype=points.dtype, device=bm.get_device(points)) - - mu = 1 - val[..., 0] = 200*mu*(x-x**2)**2 * (2*y**3-3*y**2+y) * (2*z**3-3*z**2+z) - val[..., 1] = -100*mu*(y-y**2)**2 * (2*x**3-3*x**2+x) * (2*z**3-3*z**2+z) - val[..., 2] = -100*mu*(z-z**2)**2 * (2*y**3-3*y**2+y) * (2*x**3-3*x**2+x) - - return val - - def dirichlet(self, points: TensorLike) -> TensorLike: - - return bm.zeros(points.shape, - dtype=points.dtype, device=bm.get_device(points)) - - -bm.set_backend('numpy') -nx, ny, nz = 4, 4, 4 -mesh = HexahedronMesh.from_box(box=[0, 1, 0, 1, 0, 1], - nx=nx, ny=ny, nz=nz, device=bm.get_device('cpu')) - -NC = mesh.number_of_cells() -cm = mesh.cell_volume() - -space = LagrangeFESpace(mesh, p=1, ctype='C') - -q = 2 -qf = mesh.quadrature_formula(q) -bcs, ws = qf.get_quadrature_points_and_weights() -phi = space.basis(bcs) # (1, NQ, ldof) -phi0 = phi[0] -gphi_u = mesh.grad_shape_function(bcs) # (NC, NQ, ldof, GD) -gphi = space.grad_basis(bc=bcs) # (NC, NQ, ldof, GD) - -tensor_space = TensorFunctionSpace(space, shape=(-1, 3)) -tgdof = tensor_space.number_of_global_dofs() -phi_tensor = tensor_space.basis(bcs) # (1, NQ, tldof, GD) -phi_tensor00 = phi_tensor[0, 0] -phi_tensor01 = phi_tensor[0, 1] -phi_tensor02 = phi_tensor[0, 2] -phi_tensor03 = phi_tensor[0, 3] -cell2tdof = tensor_space.cell_to_dof() # (NC, tldof) - -linear_elastic_material = LinearElasticMaterial(name='lam1_mu1', - lame_lambda=1, shear_modulus=1, - hypo='3D', device=bm.get_device(mesh)) - -B = linear_elastic_material.strain_matrix(dof_priority=True, - gphi=gphi, shear_order=['xy', 'yz', 'zx']) -D = linear_elastic_material.elastic_matrix(bcs) -KE = bm.einsum('q, c, cqki, cqkl, cqlj -> cij', ws, cm, B, D, B) - - -I = bm.broadcast_to(cell2tdof[:, :, None], shape=KE.shape) -J = bm.broadcast_to(cell2tdof[:, None, :], shape=KE.shape) -K = COOTensor( - indices = bm.empty((2, 0), dtype=bm.int32, device=bm.get_device(mesh)), - values = bm.empty((0, ), dtype=bm.float64, device=bm.get_device(mesh)), - spshape = (tgdof, tgdof)) -indices = bm.stack([I.ravel(), J.ravel()], axis=0) -K = K.add(COOTensor(indices, KE.reshape(-1), (tgdof, tgdof))) -K_before = K.to_dense() - -pde = BoxDomainPolyLoaded3d() -ps = mesh.bc_to_point(bc=bcs) -f = pde.source(ps) # (NC, NQ, GD) -f0 = f[0] -FE = bm.einsum('q, c, cqid, cqd -> ci', ws, cm, phi_tensor, f) # (NC, tldof) -FE0 = FE[0] - -F = COOTensor( - indices = bm.empty((1, 0), dtype=bm.int32, device=bm.get_device(mesh)), - values = bm.empty((0, ), dtype=bm.float64, device=bm.get_device(mesh)), - spshape = (tgdof, )) -indices = cell2tdof.reshape(1, -1) -F = F.add(COOTensor(indices, FE.reshape(-1), (tgdof, ))).to_dense() - -values = K.values() -# K_norm = bm.sqrt(bm.sum(values * values)) -K_norm = bm.sqrt(bm.sum(K.to_dense()*K.to_dense())) -F_norm = bm.sqrt(bm.sum(F * F)) -print(f"Matrix norm: {K_norm:.6f}") -print(f"Load vector norm: {F_norm:.6f}") - -isDDof = tensor_space.is_boundary_dof(threshold=None, method='interp') -kwargs = K.values_context() -# 1. 移除边界自由度相关的非零元素 -indices = K.indices() -remove_flag = bm.logical_or(isDDof[indices[0, :]], isDDof[indices[1, :]]) -retain_flag = bm.logical_not(remove_flag) -new_indices = indices[:, retain_flag] -new_values = K.values()[..., retain_flag] -K = COOTensor(new_indices, new_values, K.sparse_shape) - -# 2. 在边界自由度位置添加单位对角元素 -index = bm.nonzero(isDDof)[0] -shape = new_values.shape[:-1] + (len(index), ) -one_values = bm.ones(shape, **kwargs) -one_indices = bm.stack([index, index], axis=0) -K1 = COOTensor(one_indices, one_values, K.sparse_shape) -K = K.add(K1).coalesce() -K_after = K.to_dense() - -# 1. 边界插值 -uh_bd = bm.zeros(tensor_space.number_of_global_dofs(), - dtype=bm.float64, device=bm.get_device(mesh)) -uh_bd, isDDof = tensor_space.boundary_interpolate(gd=pde.dirichlet, uh=uh_bd, - threshold=None, method='interp') -# 2. 修改右端向量 -F = F - K.matmul(uh_bd) -F = bm.set_at(F, isDDof, uh_bd[isDDof]) - -# 矩阵和载荷向量的范数 -values = K.values() -# K_norm = bm.sqrt(bm.sum(values * values)) -K_norm = bm.sqrt(bm.sum(K.to_dense()*K.to_dense())) -F_norm = bm.sqrt(bm.sum(F * F)) -print(f"Matrix norm_after: {K_norm:.6f}") -print(f"Load vector norm_after: {F_norm:.6f}") - -# 载荷向量的范围 -F_min = bm.min(F) -F_max = bm.max(F) -print(f"F min: {F_min:.6f}") -print(f"F max: {F_max:.6f}") - -uh = tensor_space.function() -uh[:] = cg(K, F, maxiter=1000, atol=1e-14, rtol=1e-14) -u_exact = tensor_space.interpolate(pde.solution) -error_max = bm.max(uh - u_exact) -error = mesh.error(u=uh, v=pde.solution, q=tensor_space.p+3, power=2) -print("----------------------") diff --git a/app/soptx/linear_elasticity/exp_gearx_node_load_all.py b/app/soptx/linear_elasticity/exp_gearx_node_load_all.py deleted file mode 100644 index ea3592090..000000000 --- a/app/soptx/linear_elasticity/exp_gearx_node_load_all.py +++ /dev/null @@ -1,199 +0,0 @@ -from fealpy.backend import backend_manager as bm -from fealpy.functionspace import LagrangeFESpace, TensorFunctionSpace -from fealpy.sparse import COOTensor -from fealpy.fem.linear_elastic_integrator import LinearElasticIntegrator -from fealpy.material.elastic_material import LinearElasticMaterial -from fealpy.fem.bilinear_form import BilinearForm -from fealpy.fem.dirichlet_bc import DirichletBC -from fealpy.typing import TensorLike -from fealpy.solver import cg, spsolve - -import pickle -from app.gearx.gear import ExternalGear, InternalGear -from app.gearx.utils import * - -from fealpy.mesh import HexahedronMesh - - -with open('/home/heliang/FEALPy_Development/fealpy/app/soptx/linear_elasticity/external_gear_data_all.pkl', 'rb') as f: - data = pickle.load(f) - -hex_mesh = data['hex_mesh'] -helix_node = data['helix_node'] -# inner_node_idx = data['inner_node_idx'] - - -target_cell_idx = data['target_cell_idx'] -parameters = data['parameters'] -is_inner_node = data['is_inner_node'] - -hex_cell = hex_mesh.cell -hex_node = hex_mesh.node - -mesh = HexahedronMesh(hex_node, hex_cell) - -GD = mesh.geo_dimension() -NC = mesh.number_of_cells() -NN = mesh.number_of_nodes() -node = mesh.entity('node') -cell = mesh.entity('cell') - -load_values = bm.array([50.0, 60.0, 79.0, 78.0, 87.0, 95.0, 102.0, 109.0, 114.0, - 119.0, 123.0, 127.0, 129.0, 130.0, 131.0], dtype=bm.float64) - -u = parameters[..., 0] -v = parameters[..., 1] -w = parameters[..., 2] -bcs_list = [ - ( - bm.tensor([[1 - u, u]]), - bm.tensor([[1 - v, v]]), - bm.tensor([[1 - w, w]]) - ) - for u, v, w in parameters -] -# for idx, (u_tensor, v_tensor, w_tensor) in enumerate(bcs_list): -# u_values = u_tensor.flatten() -# v_values = v_tensor.flatten() -# w_values = w_tensor.flatten() -# print(f"载荷点 {idx + 1} 的重心坐标:\n u = {u_values}, v = {v_values}, w = {w_values}") - -space = LagrangeFESpace(mesh, p=1, ctype='C') -scalar_gdof = space.number_of_global_dofs() -tensor_space = TensorFunctionSpace(space, shape=(3, -1)) -tgdof = tensor_space.number_of_global_dofs() -tldof = tensor_space.number_of_local_dofs() -cell2tdof = tensor_space.cell_to_dof() - -# scalar_phi_loads = [] -# for bcs in bcs_list: -# scalar_phi = space.basis(bcs) -# scalar_phi_loads.append(scalar_phi) - -# for idx, scalar_phi in enumerate(scalar_phi_loads): -# print(f"载荷点 {idx + 1} 处的基函数值:\n", scalar_phi.flatten()) - -phi_loads = [] -for bcs in bcs_list: - phi = tensor_space.basis(bcs) - phi_loads.append(phi) - - -phi_loads_array = bm.concatenate(phi_loads, axis=1) # (1, NP, tldof, GD) - -FE_load = bm.einsum('p, cpld -> pl', load_values, phi_loads_array) # (NP, tldof) - -FE = bm.zeros((NC, tldof), dtype=bm.float64) -FE[target_cell_idx, :] = FE_load[:, :] # (NC, tldof) - -F = COOTensor(indices = bm.empty((1, 0), dtype=bm.int32, device=bm.get_device(space)), - values = bm.empty((0, ), dtype=bm.float64, device=bm.get_device(space)), - spshape = (tgdof, )) -indices = cell2tdof.reshape(1, -1) -F = F.add(COOTensor(indices, FE.reshape(-1), (tgdof, ))).to_dense() # (tgdof, ) - -linear_elastic_material = LinearElasticMaterial(name='lam1_mu1', - lame_lambda=1, shear_modulus=1, - hypo='3D', device=bm.get_device(mesh)) -integrator_K = LinearElasticIntegrator(material=linear_elastic_material, q=2) - -KE = integrator_K.assembly(space=tensor_space) -bform = BilinearForm(tensor_space) -bform.add_integrator(integrator_K) -K = bform.assembly(format='csr') - -# 矩阵和载荷向量的范数 -values = K.values() -K_norm = bm.sqrt(bm.sum(values * values)) -F_norm = bm.sqrt(bm.sum(F * F)) -print(f"Matrix norm: {K_norm:.6f}") -print(f"Load vector norm: {F_norm:.6f}") - -def dirichlet(points: TensorLike) -> TensorLike: - return bm.zeros(points.shape, dtype=points.dtype, device=bm.get_device(points)) - -scalar_is_bd_dof = is_inner_node -tensor_is_bd_dof = tensor_space.is_boundary_dof( - threshold=(scalar_is_bd_dof, scalar_is_bd_dof, scalar_is_bd_dof), - method='interp') - -dbc = DirichletBC(space=tensor_space, - gd=dirichlet, - threshold=tensor_is_bd_dof, - method='interp') -K, F = dbc.apply(A=K, f=F, check=True) - -# 1. 检查矩阵对称性 -def check_symmetry(K): - """ - 检查稀疏矩阵的对称性,使用CSRTensor类提供的方法 - """ - max_error = 0.0 - # 获取非零元素的行索引和列索引 - row_indices = K.row() # 使用CSRTensor提供的row()方法 - col_indices = K.col() # 使用CSRTensor提供的col()方法 - values = K.values() # 使用CSRTensor提供的values()方法 - - # 创建一个非零元素的位置字典 - nz_dict = {} - for i, (r, c, v) in enumerate(zip(row_indices, col_indices, values)): - nz_dict[(r, c)] = v - - # 检查对称性 - for r, c, v in zip(row_indices, col_indices, values): - if r <= c: # 只检查上三角部分 - # 检查对称位置的元素 - sym_val = nz_dict.get((c, r), 0.0) - error = abs(v - sym_val) - max_error = max(max_error, error) - - return max_error - -# 输出诊断信息 -print("\n7. Solving linear system...") - -# 1. 检查对称性 -max_symmetry_error = check_symmetry(K) -print(f"Max symmetry error: {max_symmetry_error:.6e}") - -# 2. 输出矩阵的一些基本信息 -# 矩阵规模 -nrow, ncol = K.shape -print(f"Matrix size: {nrow}x{ncol}") - -# # 非零元素个数 -# nnz = K.nnz -# print(f"Matrix non-zeros: {nnz}") -# sparsity = (nnz / (nrow * ncol)) * 100 -# print(f"Matrix sparsity: {sparsity:.2f}%") - -# 矩阵和载荷向量的范数 -values = K.values() -K_norm = bm.sqrt(bm.sum(values * values)) -F_norm = bm.sqrt(bm.sum(F * F)) -print(f"Matrix norm_after: {K_norm:.6f}") -print(f"Load vector norm_after: {F_norm:.6f}") - -# 载荷向量的范围 -F_min = bm.min(F) -F_max = bm.max(F) -print(f"F min: {F_min:.6f}") -print(f"F max: {F_max:.6f}") - -from fealpy import logger -logger.setLevel('INFO') - -uh = tensor_space.function() -uh[:] = cg(K, F, maxiter=10000, atol=1e-6, rtol=1e-6) -# uh[:] = spsolve(K, F, solver='mumps') - -# 计算残差向量和范数 -residual = K.matmul(uh[:]) - F # 使用 CSRTensor 的 matmul 方法 -residual_norm = bm.sqrt(bm.sum(residual * residual)) -print(f"Final residual norm: {residual_norm:.6e}") - -uh = uh.reshape(GD, NN).T - -mesh.nodedata['deform'] = uh[:] -mesh.to_vtk('/home/heliang/FEALPy_Development/fealpy/app/soptx/linear_elasticity/gearx_cg.vtu') -print("-----------") \ No newline at end of file diff --git a/app/soptx/linear_elasticity/exp_gearx_node_load_part.py b/app/soptx/linear_elasticity/exp_gearx_node_load_part.py deleted file mode 100644 index eca9f730d..000000000 --- a/app/soptx/linear_elasticity/exp_gearx_node_load_part.py +++ /dev/null @@ -1,199 +0,0 @@ -from fealpy.backend import backend_manager as bm -from fealpy.functionspace import LagrangeFESpace, TensorFunctionSpace -from fealpy.sparse import COOTensor -from fealpy.fem.linear_elastic_integrator import LinearElasticIntegrator -from fealpy.material.elastic_material import LinearElasticMaterial -from fealpy.fem.bilinear_form import BilinearForm -from fealpy.fem.dirichlet_bc import DirichletBC -from fealpy.typing import TensorLike -from fealpy.solver import cg, spsolve - -import pickle -from app.gearx.gear import ExternalGear, InternalGear -from app.gearx.utils import * - -from fealpy.mesh import HexahedronMesh - - -with open('/home/heliang/FEALPy_Development/fealpy/app/soptx/linear_elasticity/external_gear_data_part.pkl', 'rb') as f: - data = pickle.load(f) - -hex_mesh = data['hex_mesh'] -helix_node = data['helix_node'] -# inner_node_idx = data['inner_node_idx'] - - -target_cell_idx = data['target_cell_idx'] -parameters = data['parameters'] -is_inner_node = data['is_inner_node'] - -hex_cell = hex_mesh.cell -hex_node = hex_mesh.node - -mesh = HexahedronMesh(hex_node, hex_cell) - -GD = mesh.geo_dimension() -NC = mesh.number_of_cells() -NN = mesh.number_of_nodes() -node = mesh.entity('node') -cell = mesh.entity('cell') - -load_values = bm.array([50.0, 60.0, 79.0, 78.0, 87.0, 95.0, 102.0, 109.0, 114.0, - 119.0, 123.0, 127.0, 129.0, 130.0, 131.0], dtype=bm.float64) - -u = parameters[..., 0] -v = parameters[..., 1] -w = parameters[..., 2] -bcs_list = [ - ( - bm.tensor([[1 - u, u]]), - bm.tensor([[1 - v, v]]), - bm.tensor([[1 - w, w]]) - ) - for u, v, w in parameters -] -for idx, (u_tensor, v_tensor, w_tensor) in enumerate(bcs_list): - u_values = u_tensor.flatten() - v_values = v_tensor.flatten() - w_values = w_tensor.flatten() - print(f"载荷点 {idx + 1} 的重心坐标:\n u = {u_values}, v = {v_values}, w = {w_values}") -space = LagrangeFESpace(mesh, p=1, ctype='C') -scalar_gdof = space.number_of_global_dofs() -tensor_space = TensorFunctionSpace(space, shape=(3, -1)) -tgdof = tensor_space.number_of_global_dofs() -tldof = tensor_space.number_of_local_dofs() -cell2tdof = tensor_space.cell_to_dof() - -scalar_phi_loads = [] -for bcs in bcs_list: - scalar_phi = space.basis(bcs) - scalar_phi_loads.append(scalar_phi) - -for idx, scalar_phi in enumerate(scalar_phi_loads): - print(f"载荷点 {idx + 1} 处的基函数值:\n", scalar_phi.flatten()) - -phi_loads = [] -for bcs in bcs_list: - phi = tensor_space.basis(bcs) - phi_loads.append(phi) - - -phi_loads_array = bm.concatenate(phi_loads, axis=1) # (1, NP, tldof, GD) - -FE_load = bm.einsum('p, cpld -> pl', load_values, phi_loads_array) # (NP, tldof) - -FE = bm.zeros((NC, tldof), dtype=bm.float64) -FE[target_cell_idx, :] = FE_load[:, :] # (NC, tldof) - -F = COOTensor(indices = bm.empty((1, 0), dtype=bm.int32, device=bm.get_device(space)), - values = bm.empty((0, ), dtype=bm.float64, device=bm.get_device(space)), - spshape = (tgdof, )) -indices = cell2tdof.reshape(1, -1) -F = F.add(COOTensor(indices, FE.reshape(-1), (tgdof, ))).to_dense() # (tgdof, ) - - -linear_elastic_material = LinearElasticMaterial(name='lam1_mu1', - lame_lambda=1, shear_modulus=1, - hypo='3D', device=bm.get_device(mesh)) -integrator_K = LinearElasticIntegrator(material=linear_elastic_material, q=2) - -KE = integrator_K.assembly(space=tensor_space) -bform = BilinearForm(tensor_space) -bform.add_integrator(integrator_K) -K = bform.assembly(format='csr') - -# 矩阵和载荷向量的范数 -values = K.values() -K_norm = bm.sqrt(bm.sum(values * values)) -F_norm = bm.sqrt(bm.sum(F * F)) -print(f"Matrix norm: {K_norm:.6f}") -print(f"Load vector norm: {F_norm:.6f}") - -def dirichlet(points: TensorLike) -> TensorLike: - return bm.zeros(points.shape, dtype=points.dtype, device=bm.get_device(points)) - -scalar_is_bd_dof = is_inner_node -tensor_is_bd_dof = tensor_space.is_boundary_dof( - threshold=(scalar_is_bd_dof, scalar_is_bd_dof, scalar_is_bd_dof), - method='interp') - -dbc = DirichletBC(space=tensor_space, - gd=dirichlet, - threshold=tensor_is_bd_dof, - method='interp') -K, F = dbc.apply(A=K, f=F, check=True) - -# 1. 检查矩阵对称性 -def check_symmetry(K): - """ - 检查稀疏矩阵的对称性,使用CSRTensor类提供的方法 - """ - max_error = 0.0 - # 获取非零元素的行索引和列索引 - row_indices = K.row() # 使用CSRTensor提供的row()方法 - col_indices = K.col() # 使用CSRTensor提供的col()方法 - values = K.values() # 使用CSRTensor提供的values()方法 - - # 创建一个非零元素的位置字典 - nz_dict = {} - for i, (r, c, v) in enumerate(zip(row_indices, col_indices, values)): - nz_dict[(r, c)] = v - - # 检查对称性 - for r, c, v in zip(row_indices, col_indices, values): - if r <= c: # 只检查上三角部分 - # 检查对称位置的元素 - sym_val = nz_dict.get((c, r), 0.0) - error = abs(v - sym_val) - max_error = max(max_error, error) - - return max_error - -# 输出诊断信息 -print("\n7. Solving linear system...") - -# 1. 检查对称性 -max_symmetry_error = check_symmetry(K) -print(f"Max symmetry error: {max_symmetry_error:.6e}") - -# 2. 输出矩阵的一些基本信息 -# 矩阵规模 -nrow, ncol = K.shape -print(f"Matrix size: {nrow}x{ncol}") - -# # 非零元素个数 -# nnz = K.nnz -# print(f"Matrix non-zeros: {nnz}") -# sparsity = (nnz / (nrow * ncol)) * 100 -# print(f"Matrix sparsity: {sparsity:.2f}%") - -# 矩阵和载荷向量的范数 -values = K.values() -K_norm = bm.sqrt(bm.sum(values * values)) -F_norm = bm.sqrt(bm.sum(F * F)) -print(f"Matrix norm_after: {K_norm:.6f}") -print(f"Load vector norm_after: {F_norm:.6f}") - -# 载荷向量的范围 -F_min = bm.min(F) -F_max = bm.max(F) -print(f"F min: {F_min:.6f}") -print(f"F max: {F_max:.6f}") - -from fealpy import logger -logger.setLevel('INFO') - -uh = tensor_space.function() -uh[:] = cg(K, F, maxiter=10000, atol=1e-6, rtol=1e-6) -# uh[:] = spsolve(K, F, solver='mumps') - -# 计算残差向量和范数 -residual = K.matmul(uh[:]) - F # 使用 CSRTensor 的 matmul 方法 -residual_norm = bm.sqrt(bm.sum(residual * residual)) -print(f"Final residual norm: {residual_norm:.6e}") - -uh = uh.reshape(GD, NN).T - -mesh.nodedata['deform'] = uh[:] -mesh.to_vtk('/home/heliang/FEALPy_Development/fealpy/app/soptx/linear_elasticity/gearx_part_cg.vtu') -print("-----------") \ No newline at end of file diff --git a/app/soptx/linear_elasticity/exp_gearx_node_load_part_1_0.py b/app/soptx/linear_elasticity/exp_part_gearx_15_node_load.py similarity index 95% rename from app/soptx/linear_elasticity/exp_gearx_node_load_part_1_0.py rename to app/soptx/linear_elasticity/exp_part_gearx_15_node_load.py index 0a7740147..0a94144b5 100644 --- a/app/soptx/linear_elasticity/exp_gearx_node_load_part_1_0.py +++ b/app/soptx/linear_elasticity/exp_part_gearx_15_node_load.py @@ -20,7 +20,6 @@ hex_mesh = data['hex_mesh'] helix_node = data['helix_node'] - target_cell_idx = data['target_cell_idx'] parameters = data['parameters'] is_inner_node = data['is_inner_node'] @@ -44,18 +43,17 @@ load_values = bm.array([50.0, 60.0, 79.0, 78.0, 87.0, 95.0, 102.0, 109.0, 114.0, 119.0, 123.0, 127.0, 129.0, 130.0, 131.0], dtype=bm.float64) +cellnorm = mesh.cell_normal() # (NC, GD) + +target_cellnorm = cellnorm[target_cell_idx] # (15, GD) +P = bm.einsum('p, pd -> pd', load_values, target_cellnorm) # (15, GD) + u = parameters[..., 0] v = parameters[..., 1] w = parameters[..., 2] -# u = bm.clip(u, 0, 1) -# v = bm.clip(v, 0, 1) -# w = bm.clip(w, 0, 1) bcs_list = [ ( - # bm.tensor([[1 - u, u]]), - # bm.tensor([[1 - v, v]]), - # bm.tensor([[1 - w, w]]) bm.tensor([[u, 1 - u]]), bm.tensor([[v, 1 - v]]), bm.tensor([[w, 1 - w]]) @@ -77,7 +75,7 @@ phi_loads_array = bm.concatenate(phi_loads, axis=1) # (1, NP, tldof, GD) -FE_load = bm.einsum('p, cpld -> pl', load_values, phi_loads_array) # (NP, tldof) +FE_load = bm.einsum('pd, cpld -> pl', P, phi_loads_array) # (NP, 24) FE = bm.zeros((NC, tldof), dtype=bm.float64) FE[target_cell_idx, :] = FE_load[:, :] # (NC, tldof) diff --git a/app/soptx/linear_elasticity/exp_gearx_node_load_part_2_0.py b/app/soptx/linear_elasticity/exp_part_gearx_15_node_load_abaqus.py similarity index 92% rename from app/soptx/linear_elasticity/exp_gearx_node_load_part_2_0.py rename to app/soptx/linear_elasticity/exp_part_gearx_15_node_load_abaqus.py index 14f1786ef..2ea8c8c7c 100644 --- a/app/soptx/linear_elasticity/exp_gearx_node_load_part_2_0.py +++ b/app/soptx/linear_elasticity/exp_part_gearx_15_node_load_abaqus.py @@ -47,12 +47,14 @@ load_values = bm.array([50.0, 60.0, 79.0, 78.0, 87.0, 95.0, 1020.0, 1090.0, 1140.0, 1190.0, 1230.0, 1270.0, 1290.0, 1300.0, 1310.0], dtype=bm.float64) +cellnorm = mesh.cell_normal() # (NC, GD) + +target_cellnorm = cellnorm[target_cell_idx] # (15, GD) +P = bm.einsum('p, pd -> pd', load_values, target_cellnorm) # (15, GD) + u = parameters[..., 0] v = parameters[..., 1] w = parameters[..., 2] -u = bm.clip(u, 0, 1) -v = bm.clip(v, 0, 1) -w = bm.clip(w, 0, 1) bcs_list = [ ( @@ -80,7 +82,7 @@ phi_loads_array = bm.concatenate(phi_loads, axis=1) # (1, NP, tldof, GD) -FE_load = bm.einsum('p, cpld -> pl', load_values, phi_loads_array) # (NP, tldof) +FE_load = bm.einsum('pd, cpld -> pl', P, phi_loads_array) # (NP, 24) FE = bm.zeros((NC, tldof), dtype=bm.float64) FE[target_cell_idx, :] = FE_load[:, :] # (NC, tldof) diff --git a/app/soptx/linear_elasticity/exp_gearx_node_load_part_4_0.py b/app/soptx/linear_elasticity/exp_part_gearx_1_node_load.py similarity index 88% rename from app/soptx/linear_elasticity/exp_gearx_node_load_part_4_0.py rename to app/soptx/linear_elasticity/exp_part_gearx_1_node_load.py index 9225dfa04..3c11d5277 100644 --- a/app/soptx/linear_elasticity/exp_gearx_node_load_part_4_0.py +++ b/app/soptx/linear_elasticity/exp_part_gearx_1_node_load.py @@ -27,8 +27,8 @@ parameters = data['parameters'] is_inner_node = data['is_inner_node'] -parameter = parameters[-1] -target_cell_idx = target_cells_idx[-1] +parameter = parameters[-1:] # (1, 3) +target_cell_idx = target_cells_idx[-1:] # (1, ) hex_cell = hex_mesh.cell hex_node = hex_mesh.node @@ -41,18 +41,23 @@ cell = mesh.entity('cell') load_values = bm.array([131.0], dtype=bm.float64) # (1, ) -# cellnorm = mesh.cell_normal() # (NC, 3) -cellnorm = bm.zeros((NC, 3), dtype=bm.float64) -cellnorm[:, 1] = -1 +cellnorm = mesh.cell_normal() # (NC, 3) -target_cellnorm = cellnorm[target_cell_idx] # (3, ) -P = bm.einsum('p,d -> pd', load_values, target_cellnorm) # (1, 3) +target_cellnorm = cellnorm[target_cell_idx] # (1, 3) +P = bm.einsum('p, pd -> pd', load_values, target_cellnorm) # (1, 3) u = parameter[..., 0] v = parameter[..., 1] w = parameter[..., 2] -bcs = (bm.tensor([[u, 1 - u]]), bm.tensor([[v, 1 - v]]), bm.tensor([[w, 1 - w]])) +bcs_list = [ + ( + bm.tensor([[u, 1 - u]]), + bm.tensor([[v, 1 - v]]), + bm.tensor([[w, 1 - w]]) + ) + for u, v, w in zip(u, v, w) +] space = LagrangeFESpace(mesh, p=1, ctype='C') scalar_gdof = space.number_of_global_dofs() @@ -61,7 +66,12 @@ tldof = tensor_space.number_of_local_dofs() cell2tdof = tensor_space.cell_to_dof() -phi_loads_array = tensor_space.basis(bcs) # (1, 1, 24, 3) +phi_loads = [] +for bcs in bcs_list: + phi = tensor_space.basis(bcs) + phi_loads.append(phi) + +phi_loads_array = bm.concatenate(phi_loads, axis=1) # (1, NP, tldof, GD) FE_load = bm.einsum('pd, cpld -> pl', P, phi_loads_array) # (1, 24) diff --git a/app/soptx/linear_elasticity/exp_gearx_node_load_part_3_0.py b/app/soptx/linear_elasticity/exp_part_gearx_1_node_load_abaqus.py similarity index 83% rename from app/soptx/linear_elasticity/exp_gearx_node_load_part_3_0.py rename to app/soptx/linear_elasticity/exp_part_gearx_1_node_load_abaqus.py index 08a363ce7..cbcf71d25 100644 --- a/app/soptx/linear_elasticity/exp_gearx_node_load_part_3_0.py +++ b/app/soptx/linear_elasticity/exp_part_gearx_1_node_load_abaqus.py @@ -27,8 +27,8 @@ parameters = data['parameters'] is_inner_node = data['is_inner_node'] -parameter = parameters[-1] -target_cell_idx = target_cells_idx[-1] +parameter = parameters[-1:] # (1, 3) +target_cell_idx = target_cells_idx[-1:] # (1, ) hex_cell = hex_mesh.cell hex_node = hex_mesh.node @@ -46,23 +46,25 @@ # 点载荷值 load_values = bm.array([131.0], dtype=bm.float64) - # 单位外法向量 -# cellnorm = mesh.cell_normal() # (NC, 3) -cellnorm = bm.zeros((NC, 3), dtype=bm.float64) -cellnorm[:, 1] = -1 +cellnorm = mesh.cell_normal() # (NC, 3) # 点载荷向量 target_cellnorm = cellnorm[target_cell_idx] # (3, ) -P = bm.einsum('p,d -> pd', load_values, target_cellnorm) # (1, 3) - +P = bm.einsum('p, pd -> pd', load_values, target_cellnorm) # (1, 3) u = parameter[..., 0] v = parameter[..., 1] w = parameter[..., 2] -bcs = (bm.tensor([[u, 1 - u]]), bm.tensor([[v, 1 - v]]), bm.tensor([[w, 1 - w]])) - +bcs_list = [ + ( + bm.tensor([[u, 1 - u]]), + bm.tensor([[v, 1 - v]]), + bm.tensor([[w, 1 - w]]) + ) + for u, v, w in zip(u, v, w) +] space = LagrangeFESpace(mesh, p=1, ctype='C') scalar_gdof = space.number_of_global_dofs() tensor_space = TensorFunctionSpace(space, shape=(3, -1)) @@ -73,7 +75,12 @@ # 带有载荷的节点对应的全局自由度编号 dof_indices = bm.stack([scalar_gdof * d + load_node_indices for d in range(GD)], axis=1) # (1*8, 3) -phi_loads_array = tensor_space.basis(bcs) # (1, 1, 24, 3) +phi_loads = [] +for bcs in bcs_list: + phi = tensor_space.basis(bcs) + phi_loads.append(phi) + +phi_loads_array = bm.concatenate(phi_loads, axis=1) # (1, NP, tldof, GD) FE_load = bm.einsum('pd, cpld -> pl', P, phi_loads_array) # (1, 24) diff --git a/test/fem/linear_elastic_integrator_data.py b/app/soptx/linear_elasticity/linear_elastic_integrator_data.py similarity index 100% rename from test/fem/linear_elastic_integrator_data.py rename to app/soptx/linear_elasticity/linear_elastic_integrator_data.py diff --git a/test/fem/test_linear_elastic_integrator.py b/test/fem/test_linear_elastic_integrator.py deleted file mode 100644 index 92658d5a9..000000000 --- a/test/fem/test_linear_elastic_integrator.py +++ /dev/null @@ -1,341 +0,0 @@ -import pytest - -from fealpy.backend import backend_manager as bm -from fealpy.fem.linear_elastic_integrator import LinearElasticIntegrator -from fealpy.material.elastic_material import LinearElasticMaterial -from app.stopt.soptx.cases.material_properties import MaterialProperties -from fealpy.mesh import UniformMesh2d, TriangleMesh, UniformMesh3d, TetrahedronMesh -from fealpy.functionspace import LagrangeFESpace, TensorFunctionSpace - -from linear_elastic_integrator_data import * - -class TestLinearElasticIntegratorInterfaces: - - @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) - @pytest.mark.parametrize("elasticdata", stress_KE_uniform_mesh_2d_p1_data) - def test_assembly_2d(self, elasticdata, backend): - bm.set_backend(backend) - - extent, h, origin = elasticdata['extent'], elasticdata['h'], elasticdata['origin'] - mesh = UniformMesh2d(extent, h, origin) - NC = mesh.number_of_cells() - - p = 1 - space = LagrangeFESpace(mesh, p=p, ctype='C') - tensor_space = TensorFunctionSpace(space, shape=(-1, 2)) - tldof = tensor_space.number_of_local_dofs() - - linear_elastic_material_stress = LinearElasticMaterial( - name='LinearlElasticStressMaterial', - elastic_modulus=1, poisson_ratio=0.3, - hypo='plane_stress') - rho = bm.tensor([0.5]*NC, dtype=bm.float64) - top_isotropic_material_stress = MaterialProperties( - E0=1, Emin=1e-9, nu=0.3, penal=3, - hypo='plane_stress', rho=rho) - linear_elastic_material_strain = LinearElasticMaterial( - name='LinearlElasticStrainMaterial', - elastic_modulus=1, poisson_ratio=0.3, - hypo='plane_strain') - - integrator_linear_elastic_material_stress = LinearElasticIntegrator( - material=linear_elastic_material_stress, q=p+3) - integrator_top_isotropic_material_stress = LinearElasticIntegrator( - material=top_isotropic_material_stress, q=p+3) - integrator_linear_elastic_material_strain = LinearElasticIntegrator( - material=linear_elastic_material_strain, q=p+3) - - KK_linear_elastic_material_stress = integrator_linear_elastic_material_stress.assembly( - space=tensor_space) - KK_top_isotropic_material_stress = integrator_top_isotropic_material_stress.assembly( - space=tensor_space) - KK_linear_elastic_material_strain = integrator_linear_elastic_material_strain.assembly( - space=tensor_space) - - assert KK_linear_elastic_material_stress.shape == (NC, tldof, tldof), ( - f"Shape error in KK_linear_elastic_material_stress: expected {(NC, tldof, tldof)}, " - f"got {KK_linear_elastic_material_stress.shape}" - ) - assert KK_top_isotropic_material_stress.shape == (NC, tldof, tldof), ( - f"Shape error in KK_top_isotropic_material_stress: expected {(NC, tldof, tldof)}, " - f"got {KK_top_isotropic_material_stress.shape}" - ) - assert KK_linear_elastic_material_strain.shape == (NC, tldof, tldof), ( - f"Shape error in KK_linear_elastic_material_strain: expected {(NC, tldof, tldof)}, " - f"got {KK_linear_elastic_material_strain.shape}" - ) - - KK_linear_elastic_material_stress_true = elasticdata['yx_gd_priority'] - KK_top_isotropic_material_stress_true = elasticdata['yx_gd_priority'] * 0.5**3 - - np.testing.assert_almost_equal( - KK_linear_elastic_material_stress[0], - KK_linear_elastic_material_stress_true, decimal=7, - err_msg="Mismatch in numerical values for KK_linear_elastic_material_stress[0]: " - "The calculated stiffness matrix does not match the expected values." - ) - np.testing.assert_almost_equal( - KK_top_isotropic_material_stress[0], - KK_top_isotropic_material_stress_true, decimal=7, - err_msg="Mismatch in numerical values for KK_top_isotropic_material_stress[0]: " - "The calculated stiffness matrix with SIMP scaling does not match the expected values." - ) - - - @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) - @pytest.mark.parametrize("elasticdata", KE_uniform_mesh_3d_p1_data) - def test_assembly_3d(self, elasticdata, backend): - bm.set_backend(backend) - - extent, h, origin = elasticdata['extent'], elasticdata['h'], elasticdata['origin'] - mesh = UniformMesh3d(extent, h, origin) - NC = mesh.number_of_cells() - - p = 1 - space = LagrangeFESpace(mesh, p=p, ctype='C') - tensor_space = TensorFunctionSpace(space, shape=(-1, 3)) - tldof = tensor_space.number_of_local_dofs() - - linear_elastic_material = LinearElasticMaterial( - name='LinearlElasticStressMaterial', - elastic_modulus=1, poisson_ratio=0.3, - hypo='3D') - rho = bm.tensor([0.5]*NC, dtype=bm.float64) - top_isotropic_material = MaterialProperties( - E0=1, Emin=1e-9, nu=0.3, penal=3, - hypo='3D', rho=rho) - - integrator_linear_elastic_material = LinearElasticIntegrator( - material=linear_elastic_material, q=p+3) - integrator_top_isotropic_material = LinearElasticIntegrator( - material=top_isotropic_material, q=p+3) - - - KK_linear_elastic_material = integrator_linear_elastic_material.assembly( - space=tensor_space) - KK_top_isotropic_material = integrator_top_isotropic_material.assembly( - space=tensor_space) - - assert KK_linear_elastic_material.shape == (NC, tldof, tldof), ( - f"Shape error in KK_linear_elastic_material: expected {(NC, tldof, tldof)}, " - f"got {KK_linear_elastic_material.shape}" - ) - assert KK_top_isotropic_material.shape == (NC, tldof, tldof), ( - f"Shape error in KK_top_isotropic_material: expected {(NC, tldof, tldof)}, " - f"got {KK_top_isotropic_material.shape}" - ) - - - @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) - @pytest.mark.parametrize("elasticdata", strain_KE_triangle_p1_data) - def test_fast_assembly_strain(self, elasticdata, backend): - bm.set_backend(backend) - - box, nx, ny = elasticdata['box'], elasticdata['nx'], elasticdata['ny'] - mesh = TriangleMesh.from_box(box=box, nx=nx, ny=ny) - NC = mesh.number_of_cells() - - p = 1 - space = LagrangeFESpace(mesh, p=p, ctype='C') - - tensor_space_gd_priority = TensorFunctionSpace(space, shape=(-1, 2)) - tensor_space_dof_priority = TensorFunctionSpace(space, shape=(2, -1)) - - tldof = tensor_space_gd_priority.number_of_local_dofs() - - linear_elastic_material_strain = LinearElasticMaterial( - name='LinearlElasticStressMaterial', - elastic_modulus=1, poisson_ratio=0.3, - hypo='plane_strain') - - integrator_linear_elastic_material_strain = LinearElasticIntegrator( - material=linear_elastic_material_strain, q=p+3, method='fast_strain') - - KK_linear_elastic_material_strain_gd_priority = integrator_linear_elastic_material_strain.fast_assembly_strain( - space=tensor_space_gd_priority) - KK_linear_elastic_material_strain_dof_priority = integrator_linear_elastic_material_strain.fast_assembly_strain( - space=tensor_space_dof_priority) - - assert KK_linear_elastic_material_strain_gd_priority.shape == (NC, tldof, tldof), ( - f"Shape error in KK_linear_elastic_material_strain_gd_priority: expected {(NC, tldof, tldof)}, " - f"got {KK_linear_elastic_material_strain_gd_priority.shape}" - ) - assert KK_linear_elastic_material_strain_dof_priority.shape == (NC, tldof, tldof), ( - f"Shape error in KK_linear_elastic_material_strain_dof_priority: expected {(NC, tldof, tldof)}, " - f"got {KK_linear_elastic_material_strain_dof_priority.shape}" - ) - - KK_linear_elastic_material_strain_gd_priority_true = elasticdata['yx_gd_priority'] - KK_linear_elastic_material_strain_dof_priority_true = elasticdata['yx_dof_priority'] - - np.testing.assert_almost_equal( - bm.to_numpy(KK_linear_elastic_material_strain_gd_priority[0]), - KK_linear_elastic_material_strain_gd_priority_true, decimal=7, - err_msg="Mismatch in numerical values for KK_linear_elastic_material_strain_gd_priority[0]: " - "The calculated stiffness matrix with SIMP scaling does not match the expected values." - ) - np.testing.assert_almost_equal( - bm.to_numpy(KK_linear_elastic_material_strain_dof_priority[0]), - KK_linear_elastic_material_strain_dof_priority_true, decimal=7, - err_msg="Mismatch in numerical values for KK_linear_elastic_material_strain_dof_priority[0]: " - "The calculated stiffness matrix with SIMP scaling does not match the expected values." - ) - - - @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) - @pytest.mark.parametrize("elasticdata", stress_KE_triangle_p1_data) - def test_fast_assembly_stress(self, elasticdata, backend): - bm.set_backend(backend) - - box, nx, ny = elasticdata['box'], elasticdata['nx'], elasticdata['ny'] - mesh = TriangleMesh.from_box(box=box, nx=nx, ny=ny) - NC = mesh.number_of_cells() - - p = 1 - space = LagrangeFESpace(mesh, p=p, ctype='C') - - tensor_space_gd_priority = TensorFunctionSpace(space, shape=(-1, 2)) - tensor_space_dof_priority = TensorFunctionSpace(space, shape=(2, -1)) - - tldof = tensor_space_gd_priority.number_of_local_dofs() - - - linear_elastic_material_stress = LinearElasticMaterial( - name='LinearlElasticStressMaterial', - elastic_modulus=1, poisson_ratio=0.3, - hypo='plane_stress') - rho = bm.tensor([0.5]*NC, dtype=bm.float64) - top_isotropic_material_stress = MaterialProperties( - E0=1, Emin=1e-9, nu=0.3, penal=3, - hypo='plane_stress', rho=rho) - - integrator_linear_elastic_material_stress = LinearElasticIntegrator( - material=linear_elastic_material_stress, q=p+3, method='fast_stress') - integrator_top_isotropic_material_stress = LinearElasticIntegrator( - material=top_isotropic_material_stress, q=p+3, method='fast_stress') - - KK_linear_elastic_material_stress_gd_priority = integrator_linear_elastic_material_stress.fast_assembly_stress( - space=tensor_space_gd_priority) - KK_linear_elastic_material_stress_dof_priority = integrator_linear_elastic_material_stress.fast_assembly_stress( - space=tensor_space_dof_priority) - KK_top_isotropic_material_stress_gd_priority = integrator_top_isotropic_material_stress.fast_assembly_stress( - space=tensor_space_gd_priority) - KK_top_isotropic_material_stress_dof_priority = integrator_top_isotropic_material_stress.fast_assembly_stress( - space=tensor_space_dof_priority) - - assert KK_linear_elastic_material_stress_gd_priority.shape == (NC, tldof, tldof), ( - f"Shape error in KK_linear_elastic_material_stress_gd_priority: expected {(NC, tldof, tldof)}, " - f"got {KK_linear_elastic_material_stress_gd_priority.shape}" - ) - assert KK_linear_elastic_material_stress_dof_priority.shape == (NC, tldof, tldof), ( - f"Shape error in KK_linear_elastic_material_stress_dof_priority: expected {(NC, tldof, tldof)}, " - f"got {KK_linear_elastic_material_stress_dof_priority.shape}" - ) - assert KK_top_isotropic_material_stress_gd_priority.shape == (NC, tldof, tldof), ( - f"Shape error in KK_top_isotropic_material_stress_gd_priority: expected {(NC, tldof, tldof)}, " - f"got {KK_top_isotropic_material_stress_gd_priority.shape}" - ) - assert KK_top_isotropic_material_stress_dof_priority.shape == (NC, tldof, tldof), ( - f"Shape error in KK_top_isotropic_material_stress_dof_priority: expected {(NC, tldof, tldof)}, " - f"got {KK_top_isotropic_material_stress_dof_priority.shape}" - ) - - KK_linear_elastic_material_stress_gd_priority_true = elasticdata['yx_gd_priority'] - KK_linear_elastic_material_stress_dof_priority_true = elasticdata['yx_dof_priority'] - - np.testing.assert_almost_equal( - KK_linear_elastic_material_stress_gd_priority[0], - KK_linear_elastic_material_stress_gd_priority_true, decimal=7, - err_msg="Mismatch in numerical values for KK_linear_elastic_material_stress_gd_priority[0]: " - "The calculated stiffness matrix with SIMP scaling does not match the expected values." - ) - np.testing.assert_almost_equal( - KK_linear_elastic_material_stress_dof_priority[0], - KK_linear_elastic_material_stress_dof_priority_true, decimal=7, - err_msg="Mismatch in numerical values for KK_linear_elastic_material_stress_dof_priority[0]: " - "The calculated stiffness matrix with SIMP scaling does not match the expected values." - ) - np.testing.assert_almost_equal( - bm.to_numpy(KK_top_isotropic_material_stress_gd_priority[0]), - bm.to_numpy(KK_linear_elastic_material_stress_gd_priority[0]) * 0.5**3, decimal=7, - err_msg="Mismatch in numerical values for KK_top_isotropic_material_stress_gd_priority[0]: " - "The calculated stiffness matrix with SIMP scaling does not match the expected values." - ) - np.testing.assert_almost_equal( - bm.to_numpy(KK_top_isotropic_material_stress_dof_priority[0]), - bm.to_numpy(KK_linear_elastic_material_stress_dof_priority[0]) * 0.5**3, decimal=7, - err_msg="Mismatch in numerical values for KK_top_isotropic_material_stress_dof_priority[0]: " - "The calculated stiffness matrix with SIMP scaling does not match the expected values." - ) - - - @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) - @pytest.mark.parametrize("elasticdata", KE_tetrahedron_p1_data) - def test_fast_assembly(self, elasticdata, backend): - bm.set_backend(backend) - - box, nx, ny, nz = elasticdata['box'], elasticdata['nx'], elasticdata['ny'], elasticdata['nz'] - mesh = TetrahedronMesh.from_box(box=box, nx=nx, ny=ny, nz=nz) - NC = mesh.number_of_cells() - - p = 1 - space = LagrangeFESpace(mesh, p=p, ctype='C') - - tensor_space_gd_prioriy = TensorFunctionSpace(space, shape=(-1, 3)) - tensor_space_dof_prioriy = TensorFunctionSpace(space, shape=(3, -1)) - - tldof = tensor_space_gd_prioriy.number_of_local_dofs() - - linear_elastic_material = LinearElasticMaterial( - name='LinearlElasticStressMaterial', - elastic_modulus=1, poisson_ratio=0.3, - hypo='3D') - rho = bm.tensor([0.5]*NC, dtype=bm.float64) - top_isotropic_material = MaterialProperties( - E0=1, Emin=1e-9, nu=0.3, penal=3, - hypo='3D', rho=rho) - - integrator_linear_elastic_material = LinearElasticIntegrator( - material=linear_elastic_material, q=p+3, method='fast_3d') - integrator_top_isotropic_material = LinearElasticIntegrator( - material=top_isotropic_material, q=p+3, method='fast_3d') - - KK_linear_elastic_material_gd_priority = integrator_linear_elastic_material.fast_assembly( - space=tensor_space_gd_prioriy) - KK_linear_elastic_material_dof_priority = integrator_linear_elastic_material.fast_assembly( - space=tensor_space_dof_prioriy) - KK_top_isotropic_material_gd_priority = integrator_top_isotropic_material.fast_assembly( - space=tensor_space_gd_prioriy) - KK_top_isotropic_material_dof_priority = integrator_top_isotropic_material.fast_assembly( - space=tensor_space_dof_prioriy) - - assert KK_linear_elastic_material_gd_priority.shape == (NC, tldof, tldof), ( - f"Shape error in KK_linear_elastic_material_gd_priority: expected {(NC, tldof, tldof)}, " - f"got {KK_linear_elastic_material_gd_priority.shape}" - ) - assert KK_linear_elastic_material_dof_priority.shape == (NC, tldof, tldof), ( - f"Shape error in KK_linear_elastic_material_dof_priority: expected {(NC, tldof, tldof)}, " - f"got {KK_linear_elastic_material_dof_priority.shape}" - ) - assert KK_top_isotropic_material_gd_priority.shape == (NC, tldof, tldof), ( - f"Shape error in KK_top_isotropic_material_gd_priority: expected {(NC, tldof, tldof)}, " - f"got {KK_top_isotropic_material_gd_priority.shape}" - ) - assert KK_top_isotropic_material_dof_priority.shape == (NC, tldof, tldof), ( - f"Shape error in KK_top_isotropic_material_dof_priority: expected {(NC, tldof, tldof)}, " - f"got {KK_top_isotropic_material_dof_priority.shape}" - ) - - np.testing.assert_almost_equal( - bm.to_numpy(KK_top_isotropic_material_gd_priority[0]), - bm.to_numpy(KK_linear_elastic_material_gd_priority[0]) * 0.5**3, decimal=7, - err_msg="Mismatch in numerical values for KK_top_isotropic_material_gd_priority[0]: " - "The calculated stiffness matrix with SIMP scaling does not match the expected values." - ) - np.testing.assert_almost_equal( - bm.to_numpy(KK_top_isotropic_material_dof_priority[0]), - bm.to_numpy(KK_linear_elastic_material_dof_priority[0]) * 0.5**3, decimal=7, - err_msg="Mismatch in numerical values for KK_top_isotropic_material_dof_priority[0]: " - "The calculated stiffness matrix with SIMP scaling does not match the expected values." - ) - From 3f2944e9df137bea8bfa8d5784ba0a5ffbb39aed Mon Sep 17 00:00:00 2001 From: "brighthe98@gmail.com" Date: Sun, 17 Nov 2024 22:36:58 +0800 Subject: [PATCH 02/63] reconfiguration soptx --- app/soptx/soptx/filter/__init__.py | 15 +- app/soptx/soptx/filter/factory.py | 54 ---- app/soptx/soptx/filter/filter.py | 276 ++++++++++++++---- app/soptx/soptx/filter/filter_properties.py | 124 -------- app/soptx/soptx/filter/matrix.py | 102 ++++--- app/soptx/soptx/filter/types.py | 37 --- app/soptx/soptx/material/__init__.py | 7 +- app/soptx/soptx/material/elastic.py | 130 +++------ .../soptx/material/interpolation_scheme.py | 6 +- app/soptx/soptx/opt/__init__.py | 6 +- app/soptx/soptx/opt/base.py | 143 ++++++++- app/soptx/soptx/opt/compliance.py | 203 +++++++++---- app/soptx/soptx/opt/oc.py | 168 +++++++++++ app/soptx/soptx/opt/volume.py | 98 +++++++ app/soptx/soptx/pde/mbb_beam_2d.py | 12 +- app/soptx/soptx/solver/elastic_fem_solver.py | 270 +++++++++++------ app/soptx/soptx/solver/fem_solver.py | 123 -------- app/soptx/soptx/tests/test_compliance.py | 208 +++++++++++++ app/soptx/soptx/tests/test_oc.py | 158 ++++++++++ app/soptx/soptx/tests/test_solver.py | 58 ++-- app/soptx/soptx/tests/test_volume.py | 135 +++++++++ 21 files changed, 1592 insertions(+), 741 deletions(-) delete mode 100644 app/soptx/soptx/filter/factory.py delete mode 100644 app/soptx/soptx/filter/filter_properties.py delete mode 100644 app/soptx/soptx/filter/types.py create mode 100644 app/soptx/soptx/opt/oc.py delete mode 100644 app/soptx/soptx/solver/fem_solver.py create mode 100644 app/soptx/soptx/tests/test_compliance.py create mode 100644 app/soptx/soptx/tests/test_oc.py create mode 100644 app/soptx/soptx/tests/test_volume.py diff --git a/app/soptx/soptx/filter/__init__.py b/app/soptx/soptx/filter/__init__.py index 479845a33..dcc33bb29 100644 --- a/app/soptx/soptx/filter/__init__.py +++ b/app/soptx/soptx/filter/__init__.py @@ -1,12 +1,9 @@ -from .types import FilterType, FilterProperties -from .matrix import FilterMatrixBuilder -from .filter import TopologyFilter -from .factory import create_filter_properties +from .filter import FilterConfig, Filter +from .matrix import FilterMatrix + __all__ = [ - 'FilterType', - 'FilterProperties', - 'FilterMatrixBuilder', - 'TopologyFilter', - 'create_filter_properties' + 'FilterConfig', + 'Filter', + 'FilterMatrix', ] \ No newline at end of file diff --git a/app/soptx/soptx/filter/factory.py b/app/soptx/soptx/filter/factory.py deleted file mode 100644 index a10f2bcb3..000000000 --- a/app/soptx/soptx/filter/factory.py +++ /dev/null @@ -1,54 +0,0 @@ -from typing import Optional, Union -from fealpy.mesh.mesh_base import Mesh - -from .types import FilterType, FilterProperties -from .matrix import FilterMatrixBuilder - -def create_filter_properties( - mesh: Mesh, - filter_type: Optional[Union[int, str]] = None, - filter_rmin: Optional[float] = None -) -> Optional[FilterProperties]: - """创建过滤器属性 - - Args: - mesh: 网格对象 - filter_type: 过滤器类型(整数、字符串或None) - filter_rmin: 过滤半径 - - Returns: - FilterProperties or None: 过滤器属性对象,如果不需要过滤则返回None - - Raises: - ValueError: 当参数组合无效时 - TypeError: 当参数类型错误时 - """ - if filter_type is None: - if filter_rmin is not None: - raise ValueError("filter_rmin must be None when filter_type is None") - return None - - if filter_rmin is None: - raise ValueError("filter_rmin required when filter_type is specified") - - # 解析过滤器类型 - if isinstance(filter_type, str): - ft = FilterType.from_string(filter_type) - elif isinstance(filter_type, int): - try: - ft = FilterType(filter_type) - except ValueError: - raise ValueError(f"Invalid filter type integer: {filter_type}") - else: - raise TypeError("filter_type must be int, str, or None") - - # 构建过滤矩阵 - H, Hs = FilterMatrixBuilder.build(mesh, filter_rmin) - - return FilterProperties( - mesh=mesh, - rmin=filter_rmin, - filter_type=ft, - H=H, - Hs=Hs - ) \ No newline at end of file diff --git a/app/soptx/soptx/filter/filter.py b/app/soptx/soptx/filter/filter.py index e94cdd9b2..8019e2615 100644 --- a/app/soptx/soptx/filter/filter.py +++ b/app/soptx/soptx/filter/filter.py @@ -1,80 +1,228 @@ -from typing import Optional +from enum import IntEnum +from dataclasses import dataclass +from typing import Optional, Literal + from fealpy.backend import backend_manager as bm from fealpy.typing import TensorLike +from fealpy.sparse import COOTensor + +from .matrix import FilterMatrix + +class FilterType(IntEnum): + """滤波器类型枚举""" + SENSITIVITY = 0 # 灵敏度滤波 + DENSITY = 1 # 密度滤波 + HEAVISIDE = 2 # Heaviside投影滤波 -from .types import FilterProperties, FilterType +@dataclass +class FilterConfig: + """滤波器配置类""" + filter_type: FilterType # 使用枚举类型 + filter_radius: float -class TopologyFilter: - """拓扑优化过滤器 + def __post_init__(self): + if self.filter_radius <= 0: + raise ValueError("Filter radius must be positive") + if not isinstance(self.filter_type, (FilterType, int)): + raise ValueError("Filter type must be a FilterType enum or valid integer") + if isinstance(self.filter_type, int): + self.filter_type = FilterType(self.filter_type) + +class Filter: + """滤波器类,负责滤波矩阵计算和灵敏度修改""" - 实现各种过滤方案的统一接口,用于处理密度场和灵敏度。 - """ + def __init__(self, config: FilterConfig): + self.config = config + self._H: Optional[COOTensor] = None + self._Hs: Optional[TensorLike] = None + self._mesh = None + + self._rho_tilde = None # 存储中间密度场(对 Heaviside 投影有用) + + def initialize(self, mesh) -> None: + """根据网格初始化滤波矩阵""" + self._mesh = mesh + self._H, self._Hs = FilterMatrix.create_filter_matrix(mesh, self.config.filter_radius) + + @property + def get_intermediate_density(self) -> Optional[TensorLike]: + """获取中间密度场""" + return self._rho_tilde + + @property + def get_H(self) -> COOTensor: + """滤波矩阵""" + if self._H is None: + raise ValueError("Filter matrix is not initialized") + return self._H + + @property + def get_Hs(self) -> TensorLike: + """滤波矩阵行和向量""" + if self._Hs is None: + raise ValueError("Filter scaling vector is not initialized") + return self._Hs - def __init__(self, filter_props: Optional[FilterProperties] = None): - """初始化过滤器 + def get_physical_density(self, + density: TensorLike, + filter_params: Optional[dict] = None) -> TensorLike: + """ + 获取物理密度场。只有在 Heaviside 投影滤波时才会对密度进行变换, + 其他情况直接返回输入密度。 + + Parameters + ---------- + density : TensorLike + 原始密度场 + filter_params : Optional[dict] + 过滤器参数 (Heaviside 投影需要) + - beta: 投影参数 + + Returns + ------- + TensorLike + 物理密度场 + """ + if self.config.filter_type == FilterType.HEAVISIDE: + if filter_params is None or 'beta' not in filter_params: + raise ValueError("Heaviside projection requires 'beta' parameter") + + beta = filter_params['beta'] + # 计算并存储中间密度场 + self._rho_tilde = density + # 应用 Heaviside 投影 + physical_density = (1 - bm.exp(-beta * self._rho_tilde) + + self._rho_tilde * bm.exp(-beta)) + + return physical_density + else: + # 对于灵敏度滤波和密度滤波,初始物理密度等于设计密度 + return density + - Args: - filter_props: 过滤器属性,如果为None则不执行过滤 + def filter_sensitivity(self, + gradient: TensorLike, + design_vars: TensorLike, + gradient_type: Literal['objective', 'constraint'], + filter_params: Optional[dict] = None) -> TensorLike: """ - self.props = filter_props - - def filter_density(self, rho: TensorLike) -> TensorLike: - """过滤密度场 + 应用滤波器修改灵敏度 - Args: - rho: 原始密度场 + Parameters + ---------- + gradient : TensorLike + 原始梯度 + design_vars : TensorLike + 设计变量 + gradient_type : Literal['objective', 'constraint'] + 梯度类型,用于区分目标函数梯度和约束函数梯度 + filter_params : Optional[dict] + 过滤器参数 - Returns: - TensorLike: 过滤后的密度场 + Returns + ------- + TensorLike + 过滤后的梯度 """ - if self.props is None or self.props.H is None: - return rho + cell_measure = self._mesh.entity_measure('cell') + + if self.config.filter_type == FilterType.SENSITIVITY: + # 灵敏度滤波只修改目标函数的梯度 + if gradient_type == 'objective': + # 计算密度加权的灵敏度 + weighted_gradient = bm.einsum('c, c -> c', design_vars, gradient) + # 应用滤波矩阵 + filtered_gradient = self._H.matmul(weighted_gradient) + # 计算修正因子 + correction_factor = self._Hs * bm.maximum(0.001, design_vars) + # 返回最终修改的灵敏度 + modified_gradient = filtered_gradient / correction_factor + + return modified_gradient + else: + # 约束函数梯度不做修改 + return gradient + + elif self.config.filter_type == FilterType.DENSITY: + # 密度滤波对两种梯度都进行修改 + # 计算单元度量加权的灵敏度 + weighted_gradient = gradient * cell_measure + # 计算标准化因子 + normalization_factor = self._H.matmul(cell_measure) + # 应用滤波矩阵 + filtered_gradient = self._H.matmul(weighted_gradient / normalization_factor) + + return filtered_gradient - if self.props.filter_type == FilterType.DENSITY: - cell_measure = self.props.mesh.entity_measure('cell') - H = self.props.H - return H.matmul(rho * cell_measure) / H.matmul(cell_measure) + elif self.config.filter_type == FilterType.HEAVISIDE: + if self._rho_tilde is None: + raise ValueError("Intermediate density not available. Call get_physical_density first to initialize intermediate density field.") + + if not filter_params or 'beta' not in filter_params: + raise ValueError("Heaviside projection requires beta parameter") - return rho + beta = filter_params['beta'] + # 计算投影导数 + dx = beta * bm.exp(-beta * self._rho_tilde) + bm.exp(-beta) + # 修改梯度 + weighted_gradient = gradient * dx * cell_measure + filtered_gradient = self._H.matmul(weighted_gradient) + normalization_factor = self._H.matmul(cell_measure) + return filtered_gradient / normalization_factor - def filter_sensitivity( - self, - dce: TensorLike, - rho: Optional[TensorLike] = None, - beta: Optional[float] = None, - rho_tilde: Optional[TensorLike] = None - ) -> TensorLike: - """过滤灵敏度 - - Args: - dce: 原始灵敏度 - rho: 密度场(用于灵敏度过滤) - beta: Heaviside投影参数 - rho_tilde: 过滤后的密度场 - - Returns: - TensorLike: 过滤后的灵敏度 - - Raises: - ValueError: 当所需参数缺失时 + def filter_density(self, + density: TensorLike, + filter_params: Optional[dict] = None) -> TensorLike: """ - if self.props is None or self.props.H is None: - return dce - - H = self.props.H - cell_measure = self.props.mesh.entity_measure('cell') - - if self.props.filter_type == FilterType.SENSITIVITY: - if rho is None: - raise ValueError("Density field required for sensitivity filter") - rho_dce = bm.einsum('c, c -> c', rho, dce) - filtered_dce = H.matmul(rho_dce) - return filtered_dce / self.props.Hs / bm.maximum(0.001, rho) - - elif self.props.filter_type == FilterType.HEAVISIDE: - if beta is None or rho_tilde is None: - raise ValueError("Heaviside projection filter requires beta and rho_tilde") - dxe = beta * bm.exp(-beta * rho_tilde) + bm.exp(-beta) - return H.matmul(dce * dxe * cell_measure) / H.matmul(cell_measure) + 对密度进行滤波 + + Parameters + ---------- + density : TensorLike + 原始密度场 + filter_params : Optional[dict] + 过滤器参数,对于 Heaviside 滤波需要: + - beta: 投影参数 + + Returns + ------- + TensorLike + 过滤后的物理密度场 + """ + # 获取单元度量 + cell_measure = self._mesh.entity_measure('cell') + + if self.config.filter_type == FilterType.SENSITIVITY: + # 灵敏度滤波时,物理密度等于设计密度 + return density + + elif self.config.filter_type == FilterType.DENSITY: + # 计算加权密度 + weighted_density = density * cell_measure + # 应用滤波矩阵 + filtered_density = self._H.matmul(weighted_density) + # 计算标准化因子 + normalization_factor = self._H.matmul(cell_measure) + # 返回标准化后的密度 + physical_density = filtered_density / normalization_factor + + return physical_density + + elif self.config.filter_type == FilterType.HEAVISIDE: + if filter_params is None or 'beta' not in filter_params: + raise ValueError("Heaviside projection requires beta parameter") + + beta = filter_params['beta'] + + # 计算并存储中间密度场 + weighted_density = density * cell_measure + filtered_density = self._H.matmul(weighted_density) + normalization = self._H.matmul(cell_measure) + self._rho_tilde = filtered_density / normalization + + # 应用 Heaviside 投影 + physical_density = (1 - bm.exp(-beta * self._rho_tilde) + + self._rho_tilde * bm.exp(-beta)) - return dce \ No newline at end of file + return physical_density + diff --git a/app/soptx/soptx/filter/filter_properties.py b/app/soptx/soptx/filter/filter_properties.py deleted file mode 100644 index b9457e310..000000000 --- a/app/soptx/soptx/filter/filter_properties.py +++ /dev/null @@ -1,124 +0,0 @@ -from fealpy.backend import backend_manager as bm -from fealpy.typing import TensorLike, Tuple -from fealpy.sparse import COOTensor -from fealpy.mesh.uniform_mesh_2d import UniformMesh2d -from fealpy.mesh.uniform_mesh_3d import UniformMesh3d - -from math import ceil, sqrt - -class FilterProperties: - def __init__(self, mesh, rmin: float, ft: int): - """ - Initialize the filter properties based on the mesh. - """ - self.ft = ft - - if not isinstance(mesh, (UniformMesh2d, UniformMesh3d)): - raise TypeError("mesh must be an instance of UniformMesh2d or UniformMesh3d.") - - nx, ny = mesh.nx, mesh.ny - nz = getattr(mesh, 'nz', None) - - if nz is not None: - self.H, self.Hs = self._compute_filter_3d(nx, ny, nz, rmin) - else: - self.H, self.Hs = self._compute_filter_2d(nx, ny, rmin) - - def _compute_filter_2d(self, nx: int, ny: int, rmin: float) -> Tuple[TensorLike, TensorLike]: - """ - Compute the filter matrix for 2D topology optimization. - - Args: - nx (int): The number of elements in the x-direction of the mesh. - ny (int): The number of elements in the y-direction of the mesh. - rmin (float): The filter radius, which controls the minimum feature size. - - Returns: - Tuple[COOTensor, bm.ndarray]: A tuple containing the filter matrix (H) - in COO format and the scaling vector (Hs). - """ - if nx <= 0 or ny <= 0: - raise ValueError("The number of elements in both x and y directions (nx, ny) must be positive integers.") - if rmin <= 0: - raise ValueError("The filter radius (rmin) must be a positive integer.") - - nfilter = int(nx * ny * ((2 * (ceil(rmin) - 1) + 1) ** 2)) - iH = bm.zeros(nfilter, dtype=bm.int32) - jH = bm.zeros(nfilter, dtype=bm.int32) - sH = bm.ones(nfilter, dtype=bm.float64) - cc = 0 - - for i in range(nx): - for j in range(ny): - row = i * ny + j - kk1 = int(max(i - (ceil(rmin) - 1), 0)) - kk2 = int(min(i + ceil(rmin), nx)) - ll1 = int(max(j - (ceil(rmin) - 1), 0)) - ll2 = int(min(j + ceil(rmin), ny)) - for k in range(kk1, kk2): - for l in range(ll1, ll2): - col = k * ny + l - fac = rmin - sqrt((i - k) ** 2 + (j - l) ** 2) - iH[cc] = row - jH[cc] = col - sH[cc] = max(0.0, fac) - cc += 1 - H = COOTensor(indices=bm.astype(bm.stack((iH[:cc], jH[:cc]), axis=0), bm.int32), - values=sH[:cc], - spshape=(nx * ny, nx * ny)) - Hs = H @ bm.ones(H.shape[1], dtype=bm.float64) - - return H, Hs - - def _compute_filter_3d(self, nx: int, ny: int, nz: int, rmin: float) -> Tuple[TensorLike, TensorLike]: - """ - Compute the filter matrix for 3D topology optimization. - - Args: - nx (int): The number of elements in the x-direction of the mesh. - ny (int): The number of elements in the y-direction of the mesh. - nz (int): The number of elements in the z-direction of the mesh. - rmin (float): The filter radius, which controls the minimum feature size. - - Returns: - Tuple[COOTensor, bm.ndarray]: A tuple containing the filter matrix (H) - in COO format and the scaling vector (Hs). - """ - if nx <= 0 or ny <= 0 or nz <= 0: - raise ValueError("The number of elements in all directions (nx, ny, nz) must be positive integers.") - if rmin <= 0: - raise ValueError("The filter radius (rmin) must be a positive number.") - - ceil_rmin = int(ceil(rmin)) - nfilter = nx * ny * nz * ((2 * (ceil_rmin - 1) + 1) ** 3) - iH = bm.zeros(nfilter, dtype=bm.int32) - jH = bm.zeros(nfilter, dtype=bm.int32) - sH = bm.zeros(nfilter, dtype=bm.float64) - cc = 0 - - for i in range(nx): - for j in range(ny): - for k in range(nz): - row = i * ny * nz + j * nz + k - ii1 = max(i - (ceil_rmin - 1), 0) - ii2 = min(i + ceil_rmin, nx) - jj1 = max(j - (ceil_rmin - 1), 0) - jj2 = min(j + ceil_rmin, ny) - kk1 = max(k - (ceil_rmin - 1), 0) - kk2 = min(k + ceil_rmin, nz) - for ii in range(ii1, ii2): - for jj in range(jj1, jj2): - for kk in range(kk1, kk2): - col = ii * ny * nz + jj * nz + kk - fac = rmin - sqrt((i - ii)**2 + (j - jj)**2 + (k - kk)**2) - iH[cc] = row - jH[cc] = col - sH[cc] = max(0.0, fac) - cc += 1 - - H = COOTensor(indices=bm.astype(bm.stack((iH[:cc], jH[:cc]), axis=0), bm.int32), - values=sH[:cc], - spshape=(nx * ny * nz, nx * ny * nz)) - Hs = H @ bm.ones(H.shape[1], dtype=bm.float64) - - return H, Hs diff --git a/app/soptx/soptx/filter/matrix.py b/app/soptx/soptx/filter/matrix.py index 1f765f78f..a5182c707 100644 --- a/app/soptx/soptx/filter/matrix.py +++ b/app/soptx/soptx/filter/matrix.py @@ -1,50 +1,47 @@ -from math import ceil, sqrt from typing import Tuple +from math import ceil, sqrt + +from fealpy.backend import backend_manager as bm from fealpy.typing import TensorLike from fealpy.sparse import COOTensor -from fealpy.backend import backend_manager as bm -from fealpy.mesh.uniform_mesh_2d import UniformMesh2d -from fealpy.mesh.uniform_mesh_3d import UniformMesh3d -from fealpy.mesh.mesh_base import Mesh +from fealpy.mesh import UniformMesh2d, UniformMesh3d -class FilterMatrixBuilder: - """滤波矩阵构建器""" +class FilterMatrix: + """滤波矩阵计算工具类""" @staticmethod - def build(mesh: Mesh, rmin: float) -> Tuple[TensorLike, TensorLike]: - """构建滤波矩阵 + def create_filter_matrix(mesh, filter_radius: float) -> Tuple[COOTensor, TensorLike]: + """ + 根据网格创建滤波矩阵 - Args: - mesh: 网格对象 - rmin: 过滤半径 - - Returns: - Tuple[COOTensor, TensorLike]: 过滤矩阵和缩放向量 + Parameters + ---------- + mesh : UniformMesh2d or UniformMesh3d + 计算网格 + radius : float + 滤波半径 - Raises: - TypeError: 当网格类型不支持时 - ValueError: 当参数无效时 + Returns + ------- + H : 滤波矩阵 + Hs : 滤波矩阵行和向量 """ - if not isinstance(mesh, (UniformMesh2d, UniformMesh3d)): - raise TypeError("mesh must be an instance of UniformMesh2d or UniformMesh3d") - - if rmin <= 0: - raise ValueError("Filter radius (rmin) must be positive") - - builder = FilterMatrixBuilder() if isinstance(mesh, UniformMesh2d): - return builder._build_2d(mesh.nx, mesh.ny, rmin) + return FilterMatrix._compute_filter_2d(mesh.nx, mesh.ny, filter_radius) + elif isinstance(mesh, UniformMesh3d): + return FilterMatrix._compute_filter_3d(mesh.nx, mesh.ny, mesh.nz, filter_radius) else: - return builder._build_3d(mesh.nx, mesh.ny, mesh.nz, rmin) + raise TypeError("Mesh must be UniformMesh2d or UniformMesh3d") - def _build_2d(self, nx: int, ny: int, rmin: float) -> Tuple[TensorLike, TensorLike]: - """构建2D滤波矩阵""" + @staticmethod + def _compute_filter_2d(nx: int, ny: int, rmin: float) -> Tuple[COOTensor, TensorLike]: + """计算2D滤波矩阵""" nfilter = int(nx * ny * ((2 * (ceil(rmin) - 1) + 1) ** 2)) iH = bm.zeros(nfilter, dtype=bm.int32) jH = bm.zeros(nfilter, dtype=bm.int32) sH = bm.ones(nfilter, dtype=bm.float64) cc = 0 - + for i in range(nx): for j in range(ny): row = i * ny + j @@ -52,7 +49,6 @@ def _build_2d(self, nx: int, ny: int, rmin: float) -> Tuple[TensorLike, TensorLi kk2 = int(min(i + ceil(rmin), nx)) ll1 = int(max(j - (ceil(rmin) - 1), 0)) ll2 = int(min(j + ceil(rmin), ny)) - for k in range(kk1, kk2): for l in range(ll1, ll2): col = k * ny + l @@ -62,18 +58,27 @@ def _build_2d(self, nx: int, ny: int, rmin: float) -> Tuple[TensorLike, TensorLi jH[cc] = col sH[cc] = fac cc += 1 + + H = COOTensor( + indices=bm.astype(bm.stack((iH[:cc], jH[:cc]), axis=0), bm.int32), + values=sH[:cc], + spshape=(nx * ny, nx * ny) + ) + Hs = H @ bm.ones(H.shape[1], dtype=bm.float64) - return self._create_matrix(iH[:cc], jH[:cc], sH[:cc], nx * ny) - - def _build_3d(self, nx: int, ny: int, nz: int, rmin: float) -> Tuple[TensorLike, TensorLike]: - """构建3D滤波矩阵""" + return H, Hs + + @staticmethod + def _compute_filter_3d(nx: int, ny: int, nz: int, rmin: float) -> Tuple[TensorLike, TensorLike]: + """计算 3D 滤波矩阵""" + ceil_rmin = int(ceil(rmin)) nfilter = nx * ny * nz * ((2 * (ceil_rmin - 1) + 1) ** 3) iH = bm.zeros(nfilter, dtype=bm.int32) jH = bm.zeros(nfilter, dtype=bm.int32) sH = bm.zeros(nfilter, dtype=bm.float64) cc = 0 - + for i in range(nx): for j in range(ny): for k in range(nz): @@ -84,26 +89,19 @@ def _build_3d(self, nx: int, ny: int, nz: int, rmin: float) -> Tuple[TensorLike, jj2 = min(j + ceil_rmin, ny) kk1 = max(k - (ceil_rmin - 1), 0) kk2 = min(k + ceil_rmin, nz) - for ii in range(ii1, ii2): for jj in range(jj1, jj2): for kk in range(kk1, kk2): col = ii * ny * nz + jj * nz + kk fac = rmin - sqrt((i - ii)**2 + (j - jj)**2 + (k - kk)**2) - if fac > 0: - iH[cc] = row - jH[cc] = col - sH[cc] = fac - cc += 1 - - return self._create_matrix(iH[:cc], jH[:cc], sH[:cc], nx * ny * nz) - - def _create_matrix(self, iH: TensorLike, jH: TensorLike, sH: TensorLike, size: int) -> Tuple[TensorLike, TensorLike]: - """创建过滤矩阵和缩放向量""" - H = COOTensor( - indices=bm.astype(bm.stack((iH, jH), axis=0), bm.int32), - values=sH, - spshape=(size, size) - ) + iH[cc] = row + jH[cc] = col + sH[cc] = max(0.0, fac) + cc += 1 + + H = COOTensor(indices=bm.astype(bm.stack((iH[:cc], jH[:cc]), axis=0), bm.int32), + values=sH[:cc], + spshape=(nx * ny * nz, nx * ny * nz)) Hs = H @ bm.ones(H.shape[1], dtype=bm.float64) + return H, Hs \ No newline at end of file diff --git a/app/soptx/soptx/filter/types.py b/app/soptx/soptx/filter/types.py deleted file mode 100644 index 519594a8e..000000000 --- a/app/soptx/soptx/filter/types.py +++ /dev/null @@ -1,37 +0,0 @@ -from enum import Enum -from dataclasses import dataclass -from typing import Optional -from fealpy.typing import TensorLike -from fealpy.mesh.mesh_base import Mesh - -class FilterType(Enum): - """过滤器类型枚举""" - NONE = -1 - SENSITIVITY = 0 - DENSITY = 1 - HEAVISIDE = 2 - - @classmethod - def from_string(cls, filter_type: str) -> 'FilterType': - """从字符串创建过滤器类型""" - mapping = { - 'sens': cls.SENSITIVITY, - 'dens': cls.DENSITY, - 'heaviside': cls.HEAVISIDE - } - ft = mapping.get(filter_type.lower()) - if ft is None: - raise ValueError( - f"Invalid filter type string: '{filter_type}'. " - f"Valid options are: {list(mapping.keys())}" - ) - return ft - -@dataclass -class FilterProperties: - """过滤器基本属性""" - mesh: Mesh - rmin: float - filter_type: FilterType - H: Optional[TensorLike] = None # 过滤矩阵 - Hs: Optional[TensorLike] = None # 缩放向量 \ No newline at end of file diff --git a/app/soptx/soptx/material/__init__.py b/app/soptx/soptx/material/__init__.py index 144fa3ae5..d520103d2 100644 --- a/app/soptx/soptx/material/__init__.py +++ b/app/soptx/soptx/material/__init__.py @@ -1,14 +1,15 @@ from .base import MaterialInterpolation -from .elastic import ElasticMaterialProperties, ElasticMaterialConfig -from .thermal import ThermalMaterialProperties +from .elastic import (ElasticMaterialProperties, + ElasticMaterialInstance, + ElasticMaterialConfig) from .interpolation_scheme import SIMPInterpolation, RAMPInterpolation __all__ = [ 'MaterialInterpolation', + 'ElasticMaterialInstance', 'ElasticMaterialProperties', 'ElasticMaterialConfig', - 'ThermalMaterialProperties', 'SIMPInterpolation', 'RAMPInterpolation' ] diff --git a/app/soptx/soptx/material/elastic.py b/app/soptx/soptx/material/elastic.py index ea5884064..fc49dcf63 100644 --- a/app/soptx/soptx/material/elastic.py +++ b/app/soptx/soptx/material/elastic.py @@ -15,106 +15,62 @@ class ElasticMaterialConfig: poisson_ratio: float = 0.3 plane_assumption: Literal["plane_stress", "plane_strain", "3d"] = "plane_stress" -class ElasticMaterialProperties(LinearElasticMaterial): - """Class for elastic material properties with interpolation capabilities.""" +class ElasticMaterialInstance(LinearElasticMaterial): + """具有特定杨氏模量的弹性材料实例""" + def __init__(self, E: TensorLike, config: ElasticMaterialConfig): + super().__init__( + name="ElasticMaterial", + elastic_modulus=1.0, # 基础值,实际值由 _E 控制 + poisson_ratio=config.poisson_ratio, + hypo=config.plane_assumption + ) + self._E = E + self.config = config - def __init__(self, - config: ElasticMaterialConfig, - rho: TensorLike, - interpolation_model: MaterialInterpolation - ): - """ - Initialize elastic material properties. + @property + def elastic_modulus(self) -> TensorLike: + """获取当前的杨氏模量场""" + return self._E - Args: - config : Material configuration - rho : Density field - interpolation_model : Material interpolation model - """ + def elastic_matrix(self, bcs: Optional[TensorLike] = None) -> TensorLike: + """计算弹性矩阵""" + base_D = super().elastic_matrix() + D = self._E[:, None, None, None] * base_D + return D + +class ElasticMaterialProperties: + """材料属性计算类,负责材料的插值计算""" + def __init__(self, config: ElasticMaterialConfig, interpolation_model: MaterialInterpolation): if not isinstance(config, ElasticMaterialConfig): raise TypeError("'config' must be an instance of ElasticMaterialConfig") - if rho is None or not isinstance(rho, TensorLike): - raise TypeError("'rho' must be of type TensorLike and cannot be None") - if interpolation_model is None or not isinstance(interpolation_model, MaterialInterpolation): raise TypeError("'interpolation_model' must be an instance of MaterialInterpolation") - - super().__init__( - name="ElasticMaterialProperties", - elastic_modulus=config.elastic_modulus, - poisson_ratio=config.poisson_ratio, - hypo=config.plane_assumption - ) - - # 作为普通实例变量 + self.config = config self.interpolation_model = interpolation_model - - # 密度场需要动态更新,保持为属性 - self._rho = rho - - # 初始化基础材料 - self._base_material = LinearElasticMaterial( - name="BaseMaterial", - elastic_modulus=1.0, - poisson_ratio=config.poisson_ratio, - hypo=config.plane_assumption - ) - - @property - def rho(self) -> TensorLike: - """Get the density field.""" - return self._rho - - @rho.setter - def rho(self, value: TensorLike): - """Set the density field.""" - if value is None or not isinstance(value, TensorLike): - raise TypeError("'rho' must be of type TensorLike and cannot be None") - self._rho = value - - - @property - def base_elastic_material(self) -> LinearElasticMaterial: - """获取 E=1 时的基础材料属性""" - return self._base_material - def material_model(self) -> TensorLike: - """Calculate interpolated Young's modulus.""" + def calculate_elastic_modulus(self, density: TensorLike) -> TensorLike: + """根据密度计算杨氏模量""" E = self.interpolation_model.calculate_property( - self.rho, - self.config.elastic_modulus, - self.config.minimal_modulus, - self.interpolation_model.penalty_factor - ) + density, + self.config.elastic_modulus, + self.config.minimal_modulus, + self.interpolation_model.penalty_factor + ) return E - def material_model_derivative(self) -> TensorLike: - """Calculate derivative of interpolated Young's modulus.""" + def calculate_elastic_modulus_derivative(self, density: TensorLike) -> TensorLike: + """计算杨氏模量对密度的导数""" dE = self.interpolation_model.calculate_property_derivative( - self.rho, - self.config.elastic_modulus, - self.config.minimal_modulus, - self.interpolation_model.penalty_factor - ) + density, + self.config.elastic_modulus, + self.config.minimal_modulus, + self.interpolation_model.penalty_factor + ) return dE - def elastic_matrix(self, bcs: Optional[TensorLike] = None) -> TensorLike: - """ - Calculate the elastic matrix D for each element. - - Args: - bcs (Optional[TensorLike]): Boundary conditions - - Returns: - TensorLike: Elastic matrix D for each element - """ - if self.rho is None: - raise ValueError("Density rho must be set for MaterialProperties.") - - E = self.material_model() - base_D = super().elastic_matrix() - D = E[:, None, None, None] * base_D - - return D \ No newline at end of file + def get_base_material(self) -> ElasticMaterialInstance: + """获取基础材料实例(E=1)""" + E = bm.ones(1, dtype=bm.float64) + return ElasticMaterialInstance(E, self.config) \ No newline at end of file diff --git a/app/soptx/soptx/material/interpolation_scheme.py b/app/soptx/soptx/material/interpolation_scheme.py index e6d914b6e..3cf2faf1a 100644 --- a/app/soptx/soptx/material/interpolation_scheme.py +++ b/app/soptx/soptx/material/interpolation_scheme.py @@ -48,9 +48,11 @@ def calculate_property_derivative(self, self._validate_inputs(rho, P0, Pmin) if Pmin is None: - return penalty_factor * rho[:] ** (penalty_factor - 1) * P0 + dP = penalty_factor * rho[:] ** (penalty_factor - 1) * P0 + return dP else: - return penalty_factor * rho[:] ** (penalty_factor - 1) * (P0 - Pmin) + dP = penalty_factor * rho[:] ** (penalty_factor - 1) * (P0 - Pmin) + return dP class RAMPInterpolation(MaterialInterpolation): """Rational Approximation of Material Properties (RAMP) interpolation model.""" diff --git a/app/soptx/soptx/opt/__init__.py b/app/soptx/soptx/opt/__init__.py index ceb64e7f8..9d5a26f6e 100644 --- a/app/soptx/soptx/opt/__init__.py +++ b/app/soptx/soptx/opt/__init__.py @@ -1,9 +1,13 @@ -from .base import ObjectiveBase +from .base import ObjectiveBase, ConstraintBase, OptimizerBase from .compliance import ComplianceObjective from .volume import VolumeConstraint +from .oc import OCOptimizer __all__ = [ 'ObjectiveBase', + 'ConstraintBase', + 'OptimizerBase', 'ComplianceObjective', 'VolumeConstraint' + 'OCOptimizer' ] \ No newline at end of file diff --git a/app/soptx/soptx/opt/base.py b/app/soptx/soptx/opt/base.py index 1a8be63f7..3e8a2c7e5 100644 --- a/app/soptx/soptx/opt/base.py +++ b/app/soptx/soptx/opt/base.py @@ -1,20 +1,149 @@ from abc import ABC, abstractmethod +from typing import Optional, Dict, Any from fealpy.typing import TensorLike class ObjectiveBase(ABC): - """优化目标函数基类""" + """优化目标函数基类 + + 定义目标函数的基本接口,包括: + 1. 函数值计算 + 2. 梯度计算 + 3. Hessian矩阵计算 + """ + + @abstractmethod + def fun(self, + rho: TensorLike, + u: Optional[TensorLike] = None) -> float: + """计算目标函数值 + + Parameters + ---------- + rho : 密度场 + u : 可选的位移场,如果为None则由实现类决定如何获取 + + Returns + ------- + value : 目标函数值 + """ + pass + + @abstractmethod + def jac(self, + rho: TensorLike, + u: Optional[TensorLike] = None, + filter_params: Optional[Dict[str, Any]] = None) -> TensorLike: + """计算目标函数梯度 + + Parameters + ---------- + rho : 密度场 + u : 可选的位移场 + filter_params : 滤波器参数 + + Returns + ------- + gradient : 目标函数关于密度的梯度 + """ + pass @abstractmethod - def fun(self, x: TensorLike) -> float: - """计算目标函数值""" + def hess(self, + rho: TensorLike, + lambda_: Dict[str, Any]) -> TensorLike: + """计算目标函数Hessian矩阵 + + Parameters + ---------- + rho : 密度场 + lambda_ : Lagrange乘子相关参数 + + Returns + ------- + hessian : 目标函数的Hessian矩阵 + """ pass + +class ConstraintBase(ABC): + """约束基类 + + 定义优化问题约束的基本接口,包括: + 1. 约束函数值计算 + 2. 约束梯度计算 + 3. 约束Hessian矩阵计算 + """ @abstractmethod - def jac(self, x: TensorLike) -> TensorLike: - """计算目标函数梯度""" + def fun(self, + rho: TensorLike, + u: Optional[TensorLike] = None) -> float: + """计算约束函数值 + + Parameters + ---------- + rho : 密度场 + u : 可选的位移场 + + Returns + ------- + value : 约束函数值 + """ + pass + + @abstractmethod + def jac(self, + rho: TensorLike, + u: Optional[TensorLike] = None, + filter_params: Optional[Dict[str, Any]] = None) -> TensorLike: + """计算约束函数梯度 + + Parameters + ---------- + rho : 密度场 + u : 可选的位移场 + filter_params : 滤波器参数 + + Returns + ------- + gradient : 约束函数关于密度的梯度 + """ pass + + def hess(self, + rho: TensorLike, + lambda_: Dict[str, Any]) -> TensorLike: + """计算约束函数Hessian矩阵 + + Parameters + ---------- + rho : 密度场 + lambda_ : Lagrange乘子相关参数 + + Returns + ------- + hessian : 约束函数的Hessian矩阵 + """ + pass + +class OptimizerBase(ABC): + """优化器基类 + + 定义优化算法的基本接口 + """ @abstractmethod - def hess(self, x: TensorLike, lambda_: dict) -> TensorLike: - """计算目标函数Hessian矩阵""" + def optimize(self, + rho: TensorLike, + **kwargs) -> TensorLike: + """执行优化过程 + + Parameters + ---------- + rho : 初始密度场 + **kwargs : 其他参数,由具体优化器定义 + + Returns + ------- + rho : 优化后的密度场 + """ pass \ No newline at end of file diff --git a/app/soptx/soptx/opt/compliance.py b/app/soptx/soptx/opt/compliance.py index 9aa4652d0..064501f3a 100644 --- a/app/soptx/soptx/opt/compliance.py +++ b/app/soptx/soptx/opt/compliance.py @@ -1,76 +1,167 @@ from fealpy.backend import backend_manager as bm from fealpy.typing import TensorLike +from typing import Dict, Optional, Any -from soptx.opt.base import ObjectiveBase +from soptx.material import ElasticMaterialProperties +from soptx.solver import ElasticFEMSolver +from soptx.opt import ObjectiveBase +from soptx.filter import Filter class ComplianceObjective(ObjectiveBase): - """结构柔度最小化目标函数""" + """结构柔度最小化目标函数 - def __init__(self, - tensor_space, - material_properties, - ke0, - filter_properties=None): - """初始化柔度目标函数""" - self.space = tensor_space + 该类负责: + 1. 计算目标函数值(柔顺度) + 2. 计算目标函数对密度的梯度 + 3. 管理状态变量(位移场)的更新和缓存 + 4. 管理密度与柔顺度的对应关系 + + 变量说明: + - rho: density, 密度场 + - u: displacement, 位移场 + - ce: element compliance, 单元柔顺度 + """ + + def __init__(self, + material_properties: ElasticMaterialProperties, + solver: ElasticFEMSolver, + filter: Optional[Filter] = None): + """ + Parameters + ---------- + material_properties : 材料属性计算器 + solver : 有限元求解器 + filter : 可选的滤波器 + """ self.material_properties = material_properties - self.ke0 = ke0 - self.filter_properties = filter_properties + self.solver = solver + self.filter = filter + + # 缓存状态 + self._current_rho = None # 当前密度场 + self._current_u = None # 当前位移场 + self._element_compliance = None # 单元柔顺度 - def compute_element_compliance(self, rho: TensorLike, uh: TensorLike) -> TensorLike: - """计算单元柔度""" - self.material_properties.rho = rho + #--------------------------------------------------------------------------- + # 内部方法 + #--------------------------------------------------------------------------- + def _update_u(self, rho: TensorLike) -> TensorLike: + """更新位移场 - cell2ldof = self.space.cell_to_dof() - uhe = uh[cell2ldof] + 如果密度发生变化,重新求解状态方程;否则使用缓存的状态 - ce = bm.einsum('ci, cik, ck -> c', uhe, self.ke0, uhe) + Parameters + ---------- + rho : 密度场 - return ce + Returns + ------- + u : 位移场 + """ + # 检查是否需要更新 + if (self._current_rho is None or + self._current_u is None or + not bm.all(rho == self._current_rho)): + + # 更新求解器中的密度并求解 + self.solver.update_density(rho) + self._current_u = self.solver.solve_cg().displacement + # self._current_rho = bm.copy(rho) # 这里的 copy 导致内部状态与外部不同步 + self._current_rho = rho # 直接引用,内部状态会随外部更新 + + return self._current_u + + def _compute_element_compliance(self, u: TensorLike) -> TensorLike: + """计算单元柔顺度 - def fun(self, rho: TensorLike, uh: TensorLike) -> float: - """计算总柔度""" - ce = self.compute_element_compliance(rho, uh) - self.ce = ce # 缓存单元柔度用于梯度计算 + Parameters + ---------- + u : 位移场 - E = self.material_properties.material_model() + Returns + ------- + ce : 单元柔顺度向量 + """ + ke0 = self.solver.get_base_local_stiffness_matrix() + cell2dof = self.solver.tensor_space.cell_to_dof() + ue = u[cell2dof] + # 更新缓存 + self._element_compliance = bm.einsum('ci, cik, ck -> c', ue, ke0, ue) + return self._element_compliance + + def get_element_compliance(self) -> TensorLike: + """获取单元柔顺度""" + if self._element_compliance is None: + raise ValueError("必须先调用 fun() 计算柔顺度") + return self._element_compliance + + #--------------------------------------------------------------------------- + # 优化相关方法 + #--------------------------------------------------------------------------- + def fun(self, + rho: TensorLike, + u: Optional[TensorLike] = None) -> float: + """计算总柔度值 + + Parameters + ---------- + rho : 密度场 + u : 可选的位移场,如果为 None 则自动计算或使用缓存的位移场 + + Returns + ------- + c : 总柔顺度值 + """ + # 获取位移场 + if u is None: + u = self._update_u(rho) + + # 计算单元柔度 + ce = self._compute_element_compliance(u) + + # 计算总柔度 + E = self.material_properties.calculate_elastic_modulus(rho) c = bm.einsum('c, c -> ', E, ce) return c - def jac(self, rho: TensorLike, beta: float = None, rho_tilde: TensorLike = None) -> TensorLike: - """计算柔度关于密度的梯度""" - # 获取缓存的单元柔度 - ce = self.ce - if ce is None: - raise ValueError("必须先调用fun()计算柔度值") - - dE = self.material_properties.material_model_derivative() - dce = -bm.einsum('c, c -> c', dE, ce) + def jac(self, + rho: TensorLike, + u: Optional[TensorLike] = None, + filter_params: Optional[Dict[str, Any]] = None) -> TensorLike: + """计算目标函数梯度 - # 如果没有过滤器,直接返回梯度 - if self.filter_properties is None: - return dce - - # 应用过滤器 - ft = self.filter_properties.ft - H = self.filter_properties.H - Hs = self.filter_properties.Hs - cell_measure = self.space.mesh.entity_measure('cell') - - if ft == 0: # 灵敏度过滤 - rho_dce = bm.einsum('c, c -> c', rho[:], dce) - filtered_dce = H.matmul(rho_dce) - dce[:] = filtered_dce / Hs / bm.maximum(bm.array(0.001), rho[:]) - - elif ft == 1: # 密度过滤 - dce[:] = H.matmul(dce * cell_measure / H.matmul(cell_measure)) - - elif ft == 2: # Heaviside投影 - if beta is None or rho_tilde is None: - raise ValueError("Heaviside projection filter requires both 'beta' and 'rho_tilde'.") - dxe = beta * bm.exp(-beta * rho_tilde) + bm.exp(-beta) - dce[:] = H.matmul(dce * dxe * cell_measure / H.matmul(cell_measure)) + Parameters + ---------- + rho : 密度场 + u : 可选的位移场,如果为 None 则自动计算或使用缓存的位移场 + filter_params : 滤波器参数 + + Returns + ------- + dc : 目标函数对密度的梯度 + """ + # 获取位移场 + if u is None: + u = self._update_u(rho) - return dce \ No newline at end of file + # 获取单元柔度 + ce = (self.get_element_compliance() + if self._element_compliance is not None + else self._compute_element_compliance(u)) + + # 计算梯度 + dE = self.material_properties.calculate_elastic_modulus_derivative(rho) + dc = -bm.einsum('c, c -> c', dE, ce) + + # 应用滤波 + if self.filter is None: + return dc + + # 明确指定这是目标函数的梯度 + return self.filter.filter_sensitivity(dc, rho, 'objective', filter_params) + + def hess(self, rho: TensorLike, lambda_: dict) -> TensorLike: + """计算目标函数 Hessian 矩阵(未实现)""" + pass \ No newline at end of file diff --git a/app/soptx/soptx/opt/oc.py b/app/soptx/soptx/opt/oc.py new file mode 100644 index 000000000..1542688ab --- /dev/null +++ b/app/soptx/soptx/opt/oc.py @@ -0,0 +1,168 @@ +from typing import Dict, Any, Optional +from time import time +from dataclasses import dataclass + +from fealpy.backend import backend_manager as bm +from fealpy.typing import TensorLike + +from soptx.opt import ObjectiveBase, ConstraintBase, OptimizerBase +from soptx.filter import Filter + +@dataclass +class OCOptions: + """OC 算法的配置选项""" + max_iterations: int = 100 # 最大迭代次数 + move_limit: float = 0.2 # 每次迭代的最大移动限制 + tolerance: float = 0.01 # 收敛容差 + initial_lambda: float = 1e9 # 初始 lambda 值 + bisection_tol: float = 1e-3 # 二分法收敛容差 + +class OCOptimizer(OptimizerBase): + """Optimality Criteria (OC) 优化器 + + 用于求解拓扑优化问题的 OC 方法实现 + """ + + def __init__(self, + objective: ObjectiveBase, + constraint: ConstraintBase, + filter: Optional[Filter] = None, + options: Optional[Dict[str, Any]] = None): + """ + Parameters + ---------- + objective : 目标函数对象 + constraint : 约束条件对象 + filter : 滤波器对象 + options : 算法参数配置 + """ + self.objective = objective + self.constraint = constraint + self.filter = filter + + # 设置默认参数 + self.options = OCOptions() + if options is not None: + for key, value in options.items(): + if hasattr(self.options, key): + setattr(self.options, key, value) + + def _update_density(self, + rho: TensorLike, + dc: TensorLike, + dg: TensorLike, + lmid: float) -> TensorLike: + """使用 OC 准则更新密度 + + Parameters + ---------- + rho : 当前密度 + dc : 目标函数梯度 + dg : 约束函数梯度 + lmid : 拉格朗日乘子 + + Returns + ------- + rho_new : 更新后的密度 + """ + move = self.options.move_limit + + # OC update scheme + rho_new = bm.maximum(bm.tensor(0.0, dtype=rho.dtype), + bm.maximum(rho - move, + bm.minimum(bm.tensor(1.0, dtype=rho.dtype), + bm.minimum(rho + move, rho * bm.sqrt(-dc / dg / lmid)))) + ) + # OC update scheme + # rho_new = bm.maximum( + # bm.tensor(0.0, dtype=rho.dtype), + # bm.maximum( + # rho - move, + # bm.minimum( + # bm.tensor(1.0, dtype=rho.dtype), + # bm.minimum( + # rho + move, + # rho * bm.sqrt(-dc / dg / lmid) + # ) + # ) + # ) + # ) + + return rho_new + + def optimize(self, rho: TensorLike, **kwargs) -> TensorLike: + """运行 OC 优化算法 + + Parameters + ---------- + rho : 初始密度场 + **kwargs : 其他参数,例如: + - beta: Heaviside投影参数 + + Returns + ------- + rho : 最优密度场 + """ + # 获取参数 + max_iters = self.options.max_iterations + tol = self.options.tolerance + bisection_tol = self.options.bisection_tol + + # 准备 Heaviside 投影的参数(如果需要) + filter_params = {'beta': kwargs.get('beta')} if 'beta' in kwargs else None + + # 获取物理密度(对于非 Heaviside 投影,就是设计密度本身) + rho_phys = (self.filter.get_physical_density(rho, filter_params) + if self.filter is not None else rho) + + # 优化主循环 + for iter_idx in range(max_iters): + start_time = time() + + # 使用物理密度计算目标函数值和梯度 + obj_val = self.objective.fun(rho_phys) + obj_grad = self.objective.jac(rho_phys) + + # 使用物理密度计算约束值和梯度 + con_val = self.constraint.fun(rho_phys) + con_grad = self.constraint.jac(rho_phys) + + # 二分法求解拉格朗日乘子 + l1, l2 = 0.0, self.options.initial_lambda + + print(f"rho: {bm.mean(rho[:]):.12f}") + while (l2 - l1) / (l2 + l1) > bisection_tol: + lmid = 0.5 * (l2 + l1) + rho_new = self._update_density(rho, obj_grad, con_grad, lmid) + + # 计算新的物理密度 + if self.filter is not None: + rho_phys = self.filter.filter_density(rho_new, filter_params) + else: + rho_phys = rho_new + + # 检查约束 + if self.constraint.fun(rho_phys) > 0: + l1 = lmid + else: + l2 = lmid + + # 计算收敛性 + change = bm.max(bm.abs(rho_new - rho)) + # 更新设计变量,确保目标函数内部状态同步 + rho = rho_new + + # 输出迭代信息 + iteration_time = time() - start_time + print(f"Iteration: {iter_idx + 1}, " + f"Objective: {obj_val:.3f}, " + f"Volume: {bm.mean(rho_new[:]):.12f}, " + f"Change: {change:.3f}, " + f"Time: {iteration_time:.3f} sec") + + # 收敛检查 + if change <= tol: + print(f"Converged after {iter_idx + 1} iterations") + break + + return rho \ No newline at end of file diff --git a/app/soptx/soptx/opt/volume.py b/app/soptx/soptx/opt/volume.py index e69de29bb..66cfffd21 100644 --- a/app/soptx/soptx/opt/volume.py +++ b/app/soptx/soptx/opt/volume.py @@ -0,0 +1,98 @@ +from typing import Optional, Dict, Any +from fealpy.backend import backend_manager as bm +from fealpy.typing import TensorLike +from fealpy.mesh import Mesh + +from soptx.opt import ConstraintBase +from soptx.filter import Filter + +class VolumeConstraint(ConstraintBase): + """体积约束 + + 负责: + 1. 计算体积约束函数值 + 2. 计算体积约束的梯度 + 3. 对约束进行滤波(如果需要) + """ + + def __init__(self, + mesh: Mesh, + volume_fraction: float, + filter: Optional[Filter] = None): + """ + Parameters + ---------- + mesh : 有限元网格 + volume_fraction : 目标体积分数 + filter : 可选的滤波器对象 + """ + self._mesh = mesh + self.volume_fraction = volume_fraction + self.filter = filter + + def fun(self, rho: TensorLike) -> float: + """计算体积约束函数值 + + Parameters + ---------- + rho : 密度场 + + Returns + ------- + gneq : 约束函数值:(当前体积分数 - 目标体积分数) * 单元数量 + """ + cell_measure = self._mesh.entity_measure('cell') + NC = self._mesh.number_of_cells() + + # 计算实际体积分数 + volfrac_true = (bm.einsum('c, c -> ', cell_measure, rho) / + bm.sum(cell_measure)) + + # gneq = volfrac_true - self.volume_fraction + # gneq = (volfrac_true - self.volume_fraction) * NC + gneq = bm.sum(rho[:]) - self.volume_fraction * NC + + return gneq + + def jac(self, + rho: TensorLike, + u: Optional[TensorLike] = None, + filter_params: Optional[Dict[str, Any]] = None) -> TensorLike: + """计算体积约束梯度 + + Parameters + ---------- + rho : 密度场 + u : 可选的位移场(体积约束不需要,但为了接口一致) + filter_params : 滤波器参数 + + Returns + ------- + gradient : 约束函数对密度的梯度 + """ + cell_measure = self._mesh.entity_measure('cell') + gradient = bm.copy(cell_measure) + + if self.filter is None: + return gradient + + # 明确指定这是约束函数的梯度 + return self.filter.filter_sensitivity(gradient, rho, 'constraint', filter_params) + # 对梯度应用滤波 + # gradient = self.filter.filter_sensitivity(gradient, rho, filter_params) + + return gradient + + def hess(self, rho: TensorLike, lambda_: Dict[str, Any]) -> TensorLike: + """计算体积约束 Hessian 矩阵(未实现) + + Parameters + ---------- + rho : 密度场 + lambda_ : Lagrange乘子相关参数 + + Returns + ------- + hessian : 约束函数的 Hessian 矩阵 + """ + pass \ No newline at end of file diff --git a/app/soptx/soptx/pde/mbb_beam_2d.py b/app/soptx/soptx/pde/mbb_beam_2d.py index 921b563dc..d3b77b70f 100644 --- a/app/soptx/soptx/pde/mbb_beam_2d.py +++ b/app/soptx/soptx/pde/mbb_beam_2d.py @@ -6,7 +6,9 @@ from builtins import list class MBBBeam2dData1: - def __init__(self): + def __init__(self, + xmin: float=0, xmax: float=60, + ymin: float=0, ymax: float=20): """ flip_direction = True 0 ------- 3 ------- 6 @@ -15,13 +17,13 @@ def __init__(self): | 1 | 3 | 2 ------- 5 ------- 8 """ + self.xmin, self.xmax = xmin, xmax + self.ymin, self.ymax = ymin, ymax self.eps = 1e-12 - def domain(self, - xmin: float=0, xmax: float=60, - ymin: float=0, ymax: float=20) -> list: + def domain(self) -> list: - box = [xmin, xmax, ymin, ymax] + box = [self.xmin, self.xmax, self.ymin, self.ymax] return box diff --git a/app/soptx/soptx/solver/elastic_fem_solver.py b/app/soptx/soptx/solver/elastic_fem_solver.py index 83d76438c..34d272117 100644 --- a/app/soptx/soptx/solver/elastic_fem_solver.py +++ b/app/soptx/soptx/solver/elastic_fem_solver.py @@ -2,26 +2,18 @@ from typing import Optional from fealpy.backend import backend_manager as bm - from fealpy.typing import TensorLike from fealpy.functionspace import TensorFunctionSpace - -from fealpy.fem import LinearElasticIntegrator -from fealpy.fem import BilinearForm -from fealpy.fem import DirichletBC - +from fealpy.fem import LinearElasticIntegrator, BilinearForm, DirichletBC from fealpy.sparse import CSRTensor from fealpy.solver import cg, spsolve -from soptx.material import ElasticMaterialProperties +from soptx.material import ElasticMaterialInstance, ElasticMaterialProperties @dataclass class IterativeSolverResult: """迭代求解器的结果""" displacement: TensorLike - iterations: int - converged: bool - residual: float @dataclass class DirectSolverResult: @@ -29,19 +21,24 @@ class DirectSolverResult: displacement: TensorLike class ElasticFEMSolver: - """专门用于求解线弹性问题的有限元求解器""" + """专门用于求解线弹性问题的有限元求解器 + + 该求解器负责: + 1. 密度状态的管理 + 2. 对应密度下的材料实例管理 + 3. 有限元离散化和求解 + 4. 边界条件的处理 + """ def __init__(self, material_properties: ElasticMaterialProperties, tensor_space: TensorFunctionSpace, pde): """ - 初始化求解器 - Parameters ---------- material_properties : ElasticMaterialProperties - 材料属性 + 材料属性计算器 tensor_space : TensorFunctionSpace 张量函数空间 pde : object @@ -50,123 +47,208 @@ def __init__(self, self.material_properties = material_properties self.tensor_space = tensor_space self.pde = pde + + # 状态管理 + self._current_density = None + self._current_material = None + + # 缓存 + self._base_local_stiffness_matrix = None + self._global_stiffness_matrix = None + self._global_force_vector = None + + #--------------------------------------------------------------------------- + # 公共属性 + #--------------------------------------------------------------------------- + @property + def current_density(self) -> Optional[TensorLike]: + """获取当前密度""" + return self._current_density + + #--------------------------------------------------------------------------- + # 状态管理相关方法 + #--------------------------------------------------------------------------- + def update_density(self, density: TensorLike) -> None: + """更新密度并更新相关状态 - # 初始化积分器 - self.integrator = LinearElasticIntegrator( - material=material_properties, - q=tensor_space.p + 3 - ) + Parameters + ---------- + density : 新的密度场 + """ + if density is None: + raise ValueError("'density' cannot be None") - # 缓存基础刚度矩阵(E=1) - self._base_stiffness = None - - # 存储最近一次计算的系统矩阵和载荷向量 - self._current_stiffness = None - self._current_force = None - - @property - def base_stiffness_matrix(self) -> TensorLike: - """获取或计算基础刚度矩阵(E=1)""" - if self._base_stiffness is None: - base_material = self.material_properties.base_elastic_material - base_integrator = LinearElasticIntegrator( - material=base_material, - q=self.tensor_space.p + 3 - ) - self._base_stiffness = base_integrator.assembly(space=self.tensor_space) + self._current_density = density + # 根据新密度计算材料属性 + E = self.material_properties.calculate_elastic_modulus(density) + self._current_material = ElasticMaterialInstance(E, self.material_properties.config) + + # 清除依赖于密度的缓存 + self._global_stiffness_matrix = None + self._global_force_vector = None - return self._base_stiffness + def get_current_material(self) -> Optional[ElasticMaterialInstance]: + """获取当前材料实例""" + if self._current_material is None: + raise ValueError("Material not initialized. Call update_density first.") + return self._current_material + + #--------------------------------------------------------------------------- + # 矩阵计算和缓存相关方法 + #--------------------------------------------------------------------------- + def get_global_stiffness_matrix(self) -> Optional[CSRTensor]: + """获取最近一次求解时的全局刚度矩阵""" + return self._global_stiffness_matrix + + def get_global_force_vector(self) -> Optional[TensorLike]: + """获取最近一次求解时的全局载荷向量""" + return self._global_force_vector - def assemble_system(self) -> tuple[CSRTensor, TensorLike]: - """组装整体刚度矩阵和载荷向量""" - # 组装刚度矩阵 + def get_base_local_stiffness_matrix(self) -> TensorLike: + """获取基础材料的局部刚度矩阵(会被缓存)""" + if self._base_local_stiffness_matrix is None: + base_material = self.material_properties.get_base_material() + integrator = LinearElasticIntegrator( + material=base_material, + q=self.tensor_space.p + 3 + ) + self._base_local_stiffness_matrix = integrator.assembly(space=self.tensor_space) + return self._base_local_stiffness_matrix + + def compute_local_stiffness_matrix(self) -> TensorLike: + """计算当前材料的局部刚度矩阵(每次重新计算)""" + if self._current_material is None: + raise ValueError("Material not initialized. Call update_density first.") + + integrator = LinearElasticIntegrator( + material=self._current_material, + q=self.tensor_space.p + 3 + ) + + KE = integrator.assembly(space=self.tensor_space) + + return KE + + #--------------------------------------------------------------------------- + # 内部方法:组装和边界条件 + #--------------------------------------------------------------------------- + def _assemble_global_stiffness_matrix(self) -> CSRTensor: + """组装全局刚度矩阵""" + if self._current_material is None: + raise ValueError("Material not initialized. Call update_density first.") + + integrator = LinearElasticIntegrator( + material=self._current_material, + q=self.tensor_space.p + 3 + ) bform = BilinearForm(self.tensor_space) - bform.add_integrator(self.integrator) + bform.add_integrator(integrator) K = bform.assembly(format='csr') - - # 组装载荷向量 + self._global_stiffness_matrix = K + + return K + + def _assemble_global_force_vector(self) -> TensorLike: + """组装全局载荷向量""" force = self.pde.force F = self.tensor_space.interpolate(force) + self._global_force_vector = F + + return F + + def _apply_boundary_conditions(self, K: CSRTensor, F: TensorLike) -> tuple[CSRTensor, TensorLike]: + """应用边界条件 - # 存储当前系统 - self._current_stiffness = K - self._current_force = F + Parameters + ---------- + K : 全局刚度矩阵 + F : 全局载荷向量 - return K, F - - def apply_boundary_conditions(self, - K: CSRTensor, - F: TensorLike) -> tuple[CSRTensor, TensorLike]: - """应用边界条件""" + Returns + ------- + K : 处理边界条件后的刚度矩阵 + F : 处理边界条件后的载荷向量 + """ dirichlet = self.pde.dirichlet threshold = self.pde.threshold() uh_bd = bm.zeros(self.tensor_space.number_of_global_dofs(), dtype=bm.float64, device=bm.get_device(self.tensor_space)) - isBdDof = self.tensor_space.is_boundary_dof( - threshold=threshold, - method='interp' - ) + isBdDof = self.tensor_space.is_boundary_dof(threshold=threshold, method='interp') F = F - K.matmul(uh_bd) F[isBdDof] = uh_bd[isBdDof] - dbc = DirichletBC( - space=self.tensor_space, - gd=dirichlet, - threshold=threshold, - method='interp' - ) + dbc = DirichletBC(space=self.tensor_space, gd=dirichlet, + threshold=threshold, method='interp') K = dbc.apply_matrix(matrix=K, check=True) return K, F - + + #--------------------------------------------------------------------------- + # 求解方法 + #--------------------------------------------------------------------------- def solve_cg(self, maxiter: int = 5000, - atol: float = 1e-14, - rtol: float = 1e-14, + atol: float = 1e-12, + rtol: float = 1e-12, x0: Optional[TensorLike] = None) -> IterativeSolverResult: - """使用共轭梯度法求解""" - # 组装并应用边界条件 - K, F = self.assemble_system() - K, F = self.apply_boundary_conditions(K, F) + """使用共轭梯度法求解 + + Parameters + ---------- + maxiter : 最大迭代次数 + atol : 绝对收敛容差 + rtol : 相对收敛容差 + x0 : 初始猜测值 + + Returns + ------- + IterativeSolverResult + 求解结果,包含位移场 + """ + if self._current_density is None: + raise ValueError("Density not set. Call update_density first.") + + K0 = self._assemble_global_stiffness_matrix() + F0 = self._assemble_global_force_vector() + K, F = self._apply_boundary_conditions(K0, F0) - # 求解 uh = self.tensor_space.function() try: - uh[:], info = cg(K, F[:], x0=x0, maxiter=maxiter, - atol=atol, rtol=rtol, return_info=True) + # logger.setLevel('INFO') + # uh[:], info = cg(K, F[:], x0=x0, maxiter=maxiter, atol=atol, rtol=rtol) + # TODO 目前 FEALPy 中的 cg 只能通过 logger 获取迭代步数,无法直接返回 + uh[:] = cg(K, F[:], x0=x0, maxiter=maxiter, atol=atol, rtol=rtol) except Exception as e: raise RuntimeError(f"CG solver failed: {str(e)}") - - return IterativeSolverResult( - displacement=uh, - iterations=info['iterations'], - converged=info['success'], - residual=info['residual'] - ) + + return IterativeSolverResult(displacement=uh) def solve_direct(self, solver_type: str = 'mumps') -> DirectSolverResult: - """使用直接法求解""" - # 组装并应用边界条件 - K, F = self.assemble_system() - K, F = self.apply_boundary_conditions(K, F) + """使用直接法求解 + + Parameters + ---------- + solver_type : 求解器类型,默认为'mumps' + + Returns + ------- + DirectSolverResult + 求解结果,包含位移场 + """ + if self._current_density is None: + raise ValueError("Density not set. Call update_density first.") + + K0 = self._assemble_global_stiffness_matrix() + F0 = self._assemble_global_force_vector() + K, F = self._apply_boundary_conditions(K0, F0) - # 求解 uh = self.tensor_space.function() try: uh[:] = spsolve(K, F[:], solver=solver_type) except Exception as e: raise RuntimeError(f"Direct solver failed: {str(e)}") - return DirectSolverResult( - displacement=uh - ) - - @property - def current_system(self) -> Optional[tuple[CSRTensor, TensorLike]]: - """获取最近一次组装的系统矩阵和载荷向量""" - if self._current_stiffness is None or self._current_force is None: - return None - return self._current_stiffness, self._current_force \ No newline at end of file + return DirectSolverResult(displacement=uh) \ No newline at end of file diff --git a/app/soptx/soptx/solver/fem_solver.py b/app/soptx/soptx/solver/fem_solver.py deleted file mode 100644 index e68a83223..000000000 --- a/app/soptx/soptx/solver/fem_solver.py +++ /dev/null @@ -1,123 +0,0 @@ -from fealpy.backend import backend_manager as bm - -from fealpy.typing import TensorLike - -from fealpy.functionspace import TensorFunctionSpace -from fealpy.fem.linear_elastic_integrator import LinearElasticIntegrator -from fealpy.fem.bilinear_form import BilinearForm -from fealpy.fem import DirichletBC - -from fealpy.sparse import CSRTensor - -from soptx.material import ElasticMaterialProperties - -from fealpy.solver import cg, spsolve - -from app.soptx.soptx.utils.timer import timer - - -class FEMSolver: - def __init__(self, - material_properties: ElasticMaterialProperties, - tensor_space: TensorFunctionSpace, - pde): - """ - Initialize the FEMSolver with the provided parameters. - """ - self.material_properties = material_properties - self.tensor_space = tensor_space - self.pde = pde - - - def compute_base_stiffness_matrix(self) -> TensorLike: - """ - 计算 E=1 时的单元刚度矩阵,用于柔度计算 - - Returns: - TensorLike: 基础单元刚度矩阵(E=1) - """ - base_material = self.material_properties.base_elastic_material - integrator = LinearElasticIntegrator( - material=base_material, - q=self.tensor_space.p + 3 - ) - ke0 = integrator.assembly(space=self.tensor_space) - return ke0 - - def assemble_stiffness_matrix(self) -> CSRTensor: - """Assemble the global stiffness matrix using the material properties and integrator.""" - integrator = LinearElasticIntegrator( - material=self.material_properties, - q=self.tensor_space.p + 3 - ) - bform = BilinearForm(self.tensor_space) - bform.add_integrator(integrator) - K = bform.assembly(format='csr') - - return K - - def assemble_force_vector(self) -> TensorLike: - """Assemble the global force vector using the force function.""" - force = self.pde.force - F = self.tensor_space.interpolate(force) - - return F - - def apply_boundary_conditions(self, K: CSRTensor, F: TensorLike) -> TensorLike: - """Apply boundary conditions to the stiffness matrix and force vector.""" - dirichlet = self.pde.dirichlet - threshold = self.pde.threshold() - - uh_bd = bm.zeros(self.tensor_space.number_of_global_dofs(), - dtype=bm.float64, device=bm.get_device(self.tensor_space)) - isBdDof = self.tensor_space.is_boundary_dof(threshold=threshold, method='interp') - - F = F - K.matmul(uh_bd) - F[isBdDof] = uh_bd[isBdDof] - - dbc = DirichletBC( - space=self.tensor_space, - gd=dirichlet, - threshold=threshold, - method='interp' - ) - K = dbc.apply_matrix(matrix=K, check=True) - - return K, F - - def solve(self, solver_method) -> TensorLike: - """ - Solve the displacement field. - """ - - # tmr = timer("FEM Solver") - # next(tmr) - tmr = None - - K0 = self.assemble_stiffness_matrix() - if tmr: - tmr.send('Assemble Stiffness Matrix') - - F0 = self.assemble_force_vector() - - K, F = self.apply_boundary_conditions(K=K0, F=F0) - if tmr: - tmr.send('Apply Boundary Conditions') - - uh = self.tensor_space.function() - - if solver_method == 'cg': - uh[:] = cg(K, F[:], maxiter=5000, atol=1e-14, rtol=1e-14) - if tmr: - tmr.send('Solve System with CG') - elif solver_method == 'spsolve': - uh[:] = spsolve(K, F[:], solver='mumps') - if tmr: - tmr.send('Solve System with spsolve') - else: - raise ValueError(f"Unsupported solver method: {solver_method}") - - if tmr: - tmr.send(None) - - return uh diff --git a/app/soptx/soptx/tests/test_compliance.py b/app/soptx/soptx/tests/test_compliance.py new file mode 100644 index 000000000..aa27fb38e --- /dev/null +++ b/app/soptx/soptx/tests/test_compliance.py @@ -0,0 +1,208 @@ +""" +测试 compliance.py 中柔度目标函数的功能 +""" +from fealpy.backend import backend_manager as bm +from fealpy.mesh import UniformMesh2d +from fealpy.functionspace import LagrangeFESpace, TensorFunctionSpace + +from soptx.material import ( + ElasticMaterialConfig, + ElasticMaterialProperties, + SIMPInterpolation +) + +from soptx.pde import MBBBeam2dData1 +from soptx.solver import ElasticFEMSolver +from soptx.opt import ComplianceObjective +from soptx.filter import Filter, FilterConfig + +def test_compliance_objective(): + print("\n=== 柔度目标函数测试 ===") + + # 1. 创建网格 + nx, ny = 60, 20 + extent = [0, nx, 0, ny] + h = [1.0, 1.0] + origin = [0.0, 0.0] + mesh = UniformMesh2d( + extent=extent, h=h, origin=origin, + ipoints_ordering='yx', flip_direction='y', + device='cpu' + ) + + # 2. 创建函数空间 + p = 1 + space_C = LagrangeFESpace(mesh=mesh, p=p, ctype='C') + tensor_space_C = TensorFunctionSpace(space_C, (-1, 2)) + space_D = LagrangeFESpace(mesh=mesh, p=p-1, ctype='D') + + # 3. 创建材料属性 + # 材料配置 + material_config = ElasticMaterialConfig( + elastic_modulus=1.0, + minimal_modulus=1e-9, + poisson_ratio=0.3, + plane_assumption="plane_stress" + ) + + # 插值模型 + interpolation_model = SIMPInterpolation(penalty_factor=3.0) + + # 密度场(初始为均匀分布) + volfrac = 0.5 + array = volfrac * bm.ones(mesh.number_of_cells(), dtype=bm.float64) + rho_elem = space_D.function(array) + + # 创建材料属性对象 + material_properties = ElasticMaterialProperties( + config=material_config, + interpolation_model=interpolation_model + ) + E = material_properties.material_model(rho=rho_elem[:]) + print(f"\n单元杨氏模量信息:") + print(f"- 形状 - {E.shape}:\n {E}") + print(f"杨氏模量均值: {bm.mean(E)}") + print(f"杨氏模量最大值: {bm.max(E)}") + print(f"杨氏模量最小值: {bm.min(E)}") + + # 4. 创建PDE问题 + pde = MBBBeam2dData1(xmin=0, xmax=nx*h[0], ymin=0, ymax=ny*h[1]) + + # 5. 创建求解器 + solver = ElasticFEMSolver( + material_properties=material_properties, + tensor_space=tensor_space_C, + pde=pde + ) + + # 6. 创建目标函数对象 + objective = ComplianceObjective( + material_properties=material_properties, + solver=solver + ) + + print("\n=== 测试目标函数计算 ===") + try: + # 求解位移场 + solver_result = solver.solve_cg() + displacement = solver_result.displacement + + # 计算目标函数值 + obj_value = objective.fun(design_vars=rho_elem[:], + state_vars=displacement) + print(f"目标函数值: {obj_value:.6e}") + + # 获取单元柔顺度 + ce = objective.get_element_compliance() + print(f"\n单元柔顺度信息:") + print(f"- 形状 - {ce.shape}:\n {ce}") + print(f"- 最小值: {bm.min(ce):.6e}") + print(f"- 最大值: {bm.max(ce):.6e}") + print(f"- 平均值: {bm.mean(ce):.6e}") + + # 测试不同密度下的目标函数值 + print("\n=== 测试不同密度下的目标函数值 ===") + # 测试较小密度 + rho_small = 0.1 * bm.ones_like(rho_elem[:]) + obj_small = objective.fun(design_vars=rho_small[:], + state_vars=displacement) + print(f"密度=0.1时的目标函数值: {obj_small:.6e}") + + # 测试较大密度 + rho_large = 0.9 * bm.ones_like(rho_elem[:]) + obj_large = objective.fun(design_vars=rho_large[:], + state_vars=displacement) + print(f"密度=0.9时的目标函数值: {obj_large:.6e}") + + except Exception as e: + print(f"目标函数计算失败: {str(e)}") + + print("\n=== 测试目标函数梯度计算 ===") + try: + + # 计算单元灵敏度(不使用滤波器) + dce = objective.jac(design_vars=rho_elem[:], state_vars=displacement) + print(f"\n原始单元灵敏度信息:") + print(f"- 形状 - {dce.shape}:\n, {dce}") + print(f"- 最小值: {bm.min(dce):.6e}") + print(f"- 最大值: {bm.max(dce):.6e}") + print(f"- 平均值: {bm.mean(dce):.6e}") + + # 测试不同类型的滤波器 + print("\n=== 测试不同类型的滤波器 ===") + + # 灵敏度滤波 + filter_sens = Filter(FilterConfig(filter_type=0, filter_radius=2.4)) + filter_sens.initialize(mesh) + + objective_sens = ComplianceObjective( + material_properties=material_properties, + solver=solver, + filter=filter_sens + ) + + grad_filter_sens = objective_sens.jac(design_vars=rho_elem[:], state_vars=displacement) + print(f"\n灵敏度滤波后的梯度信息:") + print(f"- 形状 - {grad_filter_sens.shape}:\n, {grad_filter_sens}") + print(f"- 最小值: {bm.min(grad_filter_sens):.6e}") + print(f"- 最大值: {bm.max(grad_filter_sens):.6e}") + print(f"- 平均值: {bm.mean(grad_filter_sens):.6e}") + + # 密度滤波 + filter_dens = Filter(FilterConfig(filter_type=1, filter_radius=2.4)) + filter_dens.initialize(mesh) + + objective_dens = ComplianceObjective( + material_properties=material_properties, + solver=solver, + filter=filter_dens + ) + + grad_filter_dens = objective_dens.jac(design_vars=rho_elem[:], state_vars=displacement) + print(f"\n密度滤波后的梯度信息:") + print(f"- 形状 - {grad_filter_dens.shape}:\n, {grad_filter_dens}") + print(f"- 最小值: {bm.min(grad_filter_dens):.6e}") + print(f"- 最大值: {bm.max(grad_filter_dens):.6e}") + print(f"- 平均值: {bm.mean(grad_filter_dens):.6e}") + + # Heaviside投影滤波 + filter_heav = Filter(FilterConfig(filter_type=2, filter_radius=2.0)) + filter_heav.initialize(mesh) + + # objective_heav = ComplianceObjective( + # material_properties=material_properties, + # solver=solver, + # filter=filter_heav + # ) + + # # 创建Heaviside滤波参数 + # beta = 1.0 + # rho_tilde = rho_elem[:] # 这里简单使用原始密度作为示例 + + # gradient_heav = objective_heav.jac( + # design_vars=rho_elem[:], + # filter_params={'beta': beta, 'rho_tilde': rho_tilde} + # ) + # print(f"\nHeaviside投影滤波后的梯度信息:") + # print(f"- 形状: {gradient_heav.shape}") + # print(f"- 最小值: {bm.min(gradient_heav):.6e}") + # print(f"- 最大值: {bm.max(gradient_heav):.6e}") + # print(f"- 平均值: {bm.mean(gradient_heav):.6e}") + + # # 测试滤波矩阵属性 + # print("\n=== 测试滤波矩阵属性 ===") + # H = filter_sens.H + # Hs = filter_sens.Hs + # print(f"滤波矩阵 H 的形状: {H.shape}") + # print(f"滤波矩阵行和向量 Hs 的形状: {Hs.shape}") + # print(f"Hs 的最小值: {bm.min(Hs):.6e}") + # print(f"Hs 的最大值: {bm.max(Hs):.6e}") + + except Exception as e: + print(f"梯度计算失败: {str(e)}") + raise e + + + +if __name__ == "__main__": + test_compliance_objective() \ No newline at end of file diff --git a/app/soptx/soptx/tests/test_oc.py b/app/soptx/soptx/tests/test_oc.py new file mode 100644 index 000000000..c8bfe871d --- /dev/null +++ b/app/soptx/soptx/tests/test_oc.py @@ -0,0 +1,158 @@ +"""测试 OC 优化算法的功能""" + +from fealpy.backend import backend_manager as bm +from fealpy.mesh import UniformMesh2d +from fealpy.functionspace import LagrangeFESpace, TensorFunctionSpace + +from soptx.material import ( + ElasticMaterialConfig, + ElasticMaterialProperties, + SIMPInterpolation +) +from soptx.pde import MBBBeam2dData1 +from soptx.solver import ElasticFEMSolver +from soptx.opt import ComplianceObjective, VolumeConstraint +from soptx.filter import Filter, FilterConfig +from soptx.opt import OCOptimizer + +def test_oc_optimizer(): + """测试 OC 优化器的主要功能""" + + print("\n=== OC 优化器测试 ===") + + #--------------------------------------------------------------------------- + # 1. 创建计算网格和函数空间 + #--------------------------------------------------------------------------- + # 创建网格 + nx, ny = 60, 20 + extent = [0, nx, 0, ny] + h = [1.0, 1.0] + origin = [0.0, 0.0] + mesh = UniformMesh2d( + extent=extent, h=h, origin=origin, + ipoints_ordering='yx', flip_direction='y', + device='cpu' + ) + + # 创建函数空间 + p = 1 + space_C = LagrangeFESpace(mesh=mesh, p=p, ctype='C') + tensor_space_C = TensorFunctionSpace(space_C, (-1, 2)) + space_D = LagrangeFESpace(mesh=mesh, p=p-1, ctype='D') + + #--------------------------------------------------------------------------- + # 2. 创建材料属性和PDE问题 + #--------------------------------------------------------------------------- + # 创建材料配置和插值模型 + material_config = ElasticMaterialConfig( + elastic_modulus=1.0, + minimal_modulus=1e-9, + poisson_ratio=0.3, + plane_assumption="plane_stress" + ) + interpolation_model = SIMPInterpolation(penalty_factor=3.0) + + # 创建材料属性计算器 + material_properties = ElasticMaterialProperties( + config=material_config, + interpolation_model=interpolation_model + ) + + # 创建 PDE 问题 + pde = MBBBeam2dData1(xmin=0, xmax=nx*h[0], ymin=0, ymax=ny*h[1]) + + #--------------------------------------------------------------------------- + # 3. 创建求解器 + #--------------------------------------------------------------------------- + solver = ElasticFEMSolver( + material_properties=material_properties, + tensor_space=tensor_space_C, + pde=pde + ) + + # 初始密度场 + volfrac = 0.5 + array = volfrac * bm.ones(mesh.number_of_cells(), dtype=bm.float64) + rho = space_D.function(array) + + # 更新求解器中的密度 + solver.update_density(rho[:]) + + # 检查基础刚度矩阵 + ke0 = solver.get_base_local_stiffness_matrix() + print(f"\n基础材料局部刚度矩阵 - {ke0.shape}:") + print(f"{ke0[0]}") + + #--------------------------------------------------------------------------- + # 4. 创建滤波器和优化组件 + #--------------------------------------------------------------------------- + # 创建灵敏度滤波器 + filter_sens = Filter(FilterConfig(filter_type=1, filter_radius=2.4)) + filter_sens.initialize(mesh) + + # 创建目标函数 + objective = ComplianceObjective( + material_properties=material_properties, + solver=solver, + filter=filter_sens + ) + + # 创建体积约束 + constraint = VolumeConstraint( + mesh=mesh, + volume_fraction=volfrac, + filter=filter_sens + ) + + # 创建优化器 + oc_optimizer = OCOptimizer( + objective=objective, + constraint=constraint, + filter=filter_sens, + options={ + 'max_iterations': 200, + 'move_limit': 0.2, + 'tolerance': 0.01, + 'initial_lambda': 1e9, + 'bisection_tol': 1e-3 + } + ) + + #--------------------------------------------------------------------------- + # 5. 测试优化过程 + #--------------------------------------------------------------------------- + print("\n=== 开始优化测试 ===") + try: + # # 计算初始状态 + # u = solver.solve_cg().displacement + + # # 测试目标函数及其导数 + # dc = objective.jac(rho=rho[:], u=u) + # print(f"\n单元灵敏度值 - {dc.shape}:\n {dc}") + + # obj_val = objective.fun(rho=rho[:], u=u) + # print(f"\n目标函数值: {obj_val:.6e}") + + # # 测试约束函数及其导数 + # con_val = constraint.fun(rho=rho[:]) + # print(f"约束函数值: {con_val:.6e}") + + # con_grad = constraint.jac(rho=rho[:]) + # print(f"约束函数梯度 - {con_grad.shape}:\n {con_grad}") + + # 运行优化 + print("\n开始优化迭代...") + rho_opt = oc_optimizer.optimize(rho=rho[:]) + + # 输出优化结果统计 + print("\n优化结果统计:") + print(f"- 最终密度均值: {bm.mean(rho_opt):.3f}") + print(f"- 最终密度最大值: {bm.max(rho_opt):.3f}") + print(f"- 最终密度最小值: {bm.min(rho_opt):.3f}") + + except Exception as e: + print(f"\n优化过程失败: {str(e)}") + raise e + +if __name__ == "__main__": + test_oc_optimizer() \ No newline at end of file diff --git a/app/soptx/soptx/tests/test_solver.py b/app/soptx/soptx/tests/test_solver.py index cb2be4808..7c2ddf176 100644 --- a/app/soptx/soptx/tests/test_solver.py +++ b/app/soptx/soptx/tests/test_solver.py @@ -19,7 +19,7 @@ def test_elastic_fem_solver(): print("\n=== 弹性有限元求解测试 ===") # 1. 创建网格 - nx, ny = 10, 10 + nx, ny = 2, 2 extent = [0, nx, 0, ny] h = [1.0, 1.0] origin = [0.0, 0.0] @@ -60,7 +60,7 @@ def test_elastic_fem_solver(): ) # 4. 创建PDE问题 - pde = MBBBeam2dData1() + pde = MBBBeam2dData1(xmin=0, xmax=nx*h[0], ymin=0, ymax=ny*h[1]) # 5. 创建求解器 solver = ElasticFEMSolver( @@ -70,43 +70,55 @@ def test_elastic_fem_solver(): ) # 7. 测试基础刚度矩阵 - K0 = solver.base_stiffness_matrix - print("\n=== 基础刚度矩阵信息 ===") - print(f"基础刚度矩阵 - {K0.shape}:\n {K0[0]}") - + # 测试基础局部刚度矩阵 + base_local_K = solver.get_base_local_stiffness_matrix() + print("\n=== 基础局部刚度矩阵信息 ===") + print(f"基础局部刚度矩阵 - {base_local_K.shape}:\n {base_local_K[0]}") + + # 测试局部刚度矩阵计算 + local_K = solver.compute_local_stiffness_matrix() + print("\n=== 当前材料局部刚度矩阵信息 ===") + print(f"局部刚度矩阵 - {local_K.shape}:\n {local_K[0]}") + print("\n=== 测试共轭梯度求解器 ===") try: cg_result = solver.solve_cg() + print(f"CG求解结果:") - print(f"- 迭代次数: {cg_result.iterations}") - print(f"- 是否收敛: {cg_result.converged}") - print(f"- 残差: {cg_result.residual}") + # print(f"- 迭代次数: {cg_result.iterations}") + # print(f"- 是否收敛: {cg_result.converged}") + # print(f"- 残差: {cg_result.residual}") print(f"- 位移场形状: {cg_result.displacement.shape}") print(f"- 最大位移: {bm.max(bm.abs(cg_result.displacement))}") + + # 获取求解后的全局矩阵和向量 + K = solver.get_global_stiffness_matrix() + F = solver.get_global_force_vector() + if K is not None and F is not None: + print("\n=== 全局矩阵和载荷向量信息 ===") + print(f"全局刚度矩阵 - {K.shape}:\n {K.to_dense().round(4)}") + print(f"全局载荷向量 - {F.shape}:\n {F[:]}") + print(f"载荷向量最大值: {bm.max(bm.abs(F))}") except Exception as e: print(f"CG求解失败: {str(e)}") - + print("\n=== 测试直接求解器 ===") try: direct_result = solver.solve_direct(solver_type='mumps') print(f"直接求解结果:") print(f"- 位移场形状: {direct_result.displacement.shape}") print(f"- 最大位移: {bm.max(bm.abs(direct_result.displacement))}") + + # 获取求解后的全局矩阵和向量 + K = solver.get_global_stiffness_matrix() + F = solver.get_global_force_vector() + if K is not None and F is not None: + print("\n=== 全局矩阵和载荷向量信息 ===") + print(f"全局刚度矩阵 - {K.shape}:\n {K.to_dense().round(4)}") + print(f"全局载荷向量 - {F.shape}:\n {F[:]}") + print(f"载荷向量最大值: {bm.max(bm.abs(F))}") except Exception as e: print(f"直接求解失败: {str(e)}") - - # 6. 测试获取系统矩阵和载荷向量 - K, F = solver.current_system - if K is not None and F is not None: - print("\n=== 系统矩阵和载荷向量信息 ===") - print(f"刚度矩阵形状: {K.shape}") - print(f"载荷向量形状: {F.shape}") - print(f"载荷向量最大值: {bm.max(bm.abs(F))}") - - # 7. 测试基础刚度矩阵 - K0 = solver.base_stiffness_matrix - print("\n=== 基础刚度矩阵信息 ===") - print(f"基础刚度矩阵形状: {K0.shape}") if __name__ == "__main__": test_elastic_fem_solver() \ No newline at end of file diff --git a/app/soptx/soptx/tests/test_volume.py b/app/soptx/soptx/tests/test_volume.py new file mode 100644 index 000000000..184e0d20d --- /dev/null +++ b/app/soptx/soptx/tests/test_volume.py @@ -0,0 +1,135 @@ +""" +测试 volume.py 中体积约束的功能 +""" +from fealpy.backend import backend_manager as bm +from fealpy.mesh import UniformMesh2d +from fealpy.functionspace import LagrangeFESpace + +from soptx.opt import VolumeConstraint +from soptx.filter import Filter, FilterConfig + +def test_volume_constraint(): + print("\n=== 体积约束测试 ===") + + # 1. 创建网格 + nx, ny = 60, 20 + extent = [0, nx, 0, ny] + h = [1.0, 1.0] + origin = [0.0, 0.0] + mesh = UniformMesh2d( + extent=extent, h=h, origin=origin, + ipoints_ordering='yx', flip_direction='y', + device='cpu' + ) + + # 2. 创建函数空间(用于设计变量) + p = 1 + space_D = LagrangeFESpace(mesh=mesh, p=p-1, ctype='D') + + # 3. 创建设计变量(初始为均匀分布) + volfrac = 0.5 # 目标体积分数 + array = 0.4 * bm.ones(mesh.number_of_cells(), dtype=bm.float64) # 初始密度小于目标体积分数 + rho_elem = space_D.function(array) + + print("\n=== 测试约束函数计算(不使用滤波器)===") + try: + # 创建体积约束对象(不使用滤波器) + constraint = VolumeConstraint( + mesh=mesh, + volume_fraction=volfrac + ) + + # 计算约束值 + constraint_value = constraint.fun(design_vars=rho_elem[:]) + print(f"\n约束函数值(当前体积分数 - 目标体积分数)* 单元数:") + print(f"- 约束值: {constraint_value:.6e}") + + # 计算约束梯度 + gradient = constraint.jac(design_vars=rho_elem[:]) + print(f"\n约束梯度信息:") + print(f"- 形状 - {gradient.shape}:\n {gradient}") + print(f"- 最小值: {bm.min(gradient):.6e}") + print(f"- 最大值: {bm.max(gradient):.6e}") + print(f"- 平均值: {bm.mean(gradient):.6e}") + + # 测试不同密度下的约束值 + print("\n=== 测试不同密度下的约束值 ===") + # 测试较小密度 + rho_small = 0.1 * bm.ones_like(rho_elem[:]) + constraint_small = constraint.fun(design_vars=rho_small) + print(f"密度=0.1时的约束值: {constraint_small:.6e}") + + # 测试较大密度 + rho_large = 0.9 * bm.ones_like(rho_elem[:]) + constraint_large = constraint.fun(design_vars=rho_large) + print(f"密度=0.9时的约束值: {constraint_large:.6e}") + + except Exception as e: + print(f"约束函数计算失败: {str(e)}") + + print("\n=== 测试不同类型的滤波器 ===") + try: + # 灵敏度滤波 + filter_sens = Filter(FilterConfig(filter_type=0, filter_radius=2.4)) + filter_sens.initialize(mesh) + + constraint_sens = VolumeConstraint( + mesh=mesh, + volume_fraction=volfrac, + filter=filter_sens + ) + + grad_filter_sens = constraint_sens.jac(design_vars=rho_elem[:]) + print(f"\n灵敏度滤波后的梯度信息:") + print(f"- 形状 - {grad_filter_sens.shape}:\n {grad_filter_sens}") + print(f"- 最小值: {bm.min(grad_filter_sens):.6e}") + print(f"- 最大值: {bm.max(grad_filter_sens):.6e}") + print(f"- 平均值: {bm.mean(grad_filter_sens):.6e}") + + # 密度滤波 + filter_dens = Filter(FilterConfig(filter_type=1, filter_radius=2.4)) + filter_dens.initialize(mesh) + + constraint_dens = VolumeConstraint( + mesh=mesh, + volume_fraction=volfrac, + filter=filter_dens + ) + + grad_filter_dens = constraint_dens.jac(design_vars=rho_elem[:]) + print(f"\n密度滤波后的梯度信息:") + print(f"- 形状 - {grad_filter_dens.shape}:\n {grad_filter_dens}") + print(f"- 最小值: {bm.min(grad_filter_dens):.6e}") + print(f"- 最大值: {bm.max(grad_filter_dens):.6e}") + print(f"- 平均值: {bm.mean(grad_filter_dens):.6e}") + + # # Heaviside投影滤波 + # filter_heav = Filter(FilterConfig(filter_type=2, filter_radius=2.4)) + # filter_heav.initialize(mesh) + + # constraint_heav = VolumeConstraint( + # mesh=mesh, + # volume_fraction=volfrac, + # filter=filter_heav + # ) + + # # 创建Heaviside滤波参数 + # beta = 1.0 + # rho_tilde = rho_elem[:] # 这里简单使用原始密度作为示例 + + # grad_filter_heav = constraint_heav.jac( + # design_vars=rho_elem[:], + # filter_params={'beta': beta, 'rho_tilde': rho_tilde} + # ) + # print(f"\nHeaviside投影滤波后的梯度信息:") + # print(f"- 形状: {grad_filter_heav.shape}") + # print(f"- 最小值: {bm.min(grad_filter_heav):.6e}") + # print(f"- 最大值: {bm.max(grad_filter_heav):.6e}") + # print(f"- 平均值: {bm.mean(grad_filter_heav):.6e}") + + except Exception as e: + print(f"滤波器测试失败: {str(e)}") + raise e + +if __name__ == "__main__": + test_volume_constraint() \ No newline at end of file From f58423f4dda6a7c62bf5630fff2ae5dd08780522 Mon Sep 17 00:00:00 2001 From: Liujiawangmath <15674760037@163.com> Date: Mon, 18 Nov 2024 16:00:56 +0800 Subject: [PATCH 03/63] update --- fealpy/fdm/bilinear.py | 122 +++++++++++ fealpy/fdm/ocp_opt.py | 147 +++++++++++++ fealpy/fdm/ocp_opt_pde.py | 164 ++++++++++++++ fealpy/fdm/ocp_yuanshi.py | 142 ++++++++++++ fealpy/fdm/solver.py | 289 +++++++++++++++++++++++++ fealpy/fdm/solver_update.py | 309 +++++++++++++++++++++++++++ fealpy/fdm/tensor_mass_integrator.py | 74 +++++++ 7 files changed, 1247 insertions(+) create mode 100644 fealpy/fdm/bilinear.py create mode 100644 fealpy/fdm/ocp_opt.py create mode 100644 fealpy/fdm/ocp_opt_pde.py create mode 100644 fealpy/fdm/ocp_yuanshi.py create mode 100644 fealpy/fdm/solver.py create mode 100644 fealpy/fdm/solver_update.py create mode 100644 fealpy/fdm/tensor_mass_integrator.py diff --git a/fealpy/fdm/bilinear.py b/fealpy/fdm/bilinear.py new file mode 100644 index 000000000..78bdcac26 --- /dev/null +++ b/fealpy/fdm/bilinear.py @@ -0,0 +1,122 @@ + +from typing import Optional + +from fealpy.backend import backend_manager as bm +from fealpy.typing import TensorLike, CoefLike +from fealpy.utils import is_scalar, is_tensor, fill_axis + + +def integral(value: TensorLike, weights: TensorLike, measure: TensorLike, *, + entity_type=False) -> TensorLike: + """Numerical integration. + + Parameters: + value (TensorLike[..., C, Q]): The values on the quadrature points to be integrated. + weights (TensorLike[Q,]): The weights of the quadrature points. + measure (TensorLike[C,]): The measure of the quadrature points. + entity_type (bool): Whether to return integration in each entity. Defaults to False. + + Returns: + TensorLike: The result of the integration. The shape will be [..., C]\ + if `entity_type` is True, otherwise [...]. + """ + subs = '...c' if entity_type else '...' + return bm.einsum(f'c, q, ...cq -> {subs}', measure, weights, value) + + +def linear_integral(basis: TensorLike, weights: TensorLike, measure: TensorLike, + source: Optional[CoefLike]=None, + batched: bool=False) -> TensorLike: + """Numerical integration. + + Parameters: + basis (TensorLike[C, Q, I, ...]): The values on the quadrature points to be integrated. + weights (TensorLike[Q,]): The weights of the quadrature points. + measure (TensorLike[C,]): The measure of the mesh entity. + source (Number, TensorLike, optional): The source of the integration. Defaults to None. + Must be int, float, TensorLike, or callable returning TensorLike with shape (C,), (C, Q) or (C, Q, ...). + If `batched == True`, there should be a batch dimension as the first axis. + batched (bool, optional): Whether the source are batched. Defaults to False. + + Returns: + TensorLike: The result of the integration shaped (C, I) for source (C, Q, ...). + And (C, I, ...) for source (C, ) and (C, Q). + If `batched` is True, there will be a batch dimension as the first axis. + """ + if source is None: + return bm.einsum('c, q, cq... -> c...', measure, weights, basis) + + if is_scalar(source): + return bm.einsum('c, q, cq... -> c...', measure, weights, basis) * source + + elif is_tensor(source): + dof_shape = basis.shape[3:] + basis = basis.reshape(*basis.shape[:3], -1) # (C, Q, I, dof_numel) + + if source.ndim <= 2 + int(batched): + source = fill_axis(source, 3 if batched else 2) + r = bm.einsum(f'c, q, cqid, ...cq -> ...cid', measure, weights, basis, source) + return bm.reshape(r, r.shape[:-1] + dof_shape) + else: + source = fill_axis(source, 4 if batched else 3) + return bm.einsum(f'c, q, cqid, ...cqd -> ...ci', measure, weights, basis, source) + + else: + raise TypeError(f"source should be int, float or TensorLike, but got {type(source)}.") + + +def bilinear_integral(basis1: TensorLike, basis2: TensorLike, weights: TensorLike, + measure: TensorLike, + coef: Optional[CoefLike]=None, + batched: bool=False) -> TensorLike: + """Numerical integration. + + Parameters: + basis1 (TensorLike[C, Q, I, ...]): The values on the quadrature points to be integrated. + basis2 (TensorLike[C, Q, J, ...]): The values on the quadrature points to be integrated. + weights (TensorLike[Q,]): The weights of the quadrature points. + measure (TensorLike[C,]): The measure of the mesh entity. + coef (Number, TensorLike, optional): The coefficient of the integration. Defaults to None. + Must be int, float, TensorLike, or callable returning TensorLike with shape (C,), (C, Q) or (C, Q, ...). + If `batched == True`, there should be a batch dimension as the first axis. + batched (bool, optional): Whether the coef are batched. Defaults to False. + + Returns: + TensorLike: The result of the integration shaped (C, I, J). + If `batched` is True, the shape of the output is (B, C, I, J). + """ + basis1 = basis1.reshape(*basis1.shape[:3], -1) # (C, Q, I, dof_numel) + basis2 = basis2.reshape(*basis2.shape[:3], -1) # (C, Q, J, dof_numel) + + if coef is None: + return bm.einsum(f'q, c, cqid, cqjd -> cij', weights, measure, basis1, basis2) + + if is_scalar(coef): + return bm.einsum(f'q, c, cqid, cqjd -> cij', weights, measure, basis1, basis2) * coef + + elif is_tensor(coef): + # 检查 coef 的维度 + if coef.ndim >= 4 and coef.shape[-2] == coef.shape[-1]: # 检查 coef 最后两个维度是否为 (n, n) + # 进行矩阵形式的求和操作 (后两维为 n x n 的情况) + return bm.einsum(f'q, c, cqid, cqjd, ...cqkl -> ...cijkl', weights, measure, basis1, basis2, coef) + else: + # 保留原有 fill_axis 逻辑,适用于其他维度情况 + coef = fill_axis(coef, 4 if batched else 3) + return bm.einsum(f'q, c, cqid, cqjd, ...cqd -> ...cij', weights, measure, basis1, basis2, coef) + + else: + raise TypeError(f"coef should be int, float or TensorLike, but got {type(coef)}.") + +def get_semilinear_coef(value:TensorLike, coef: Optional[CoefLike]=None, batched: bool=False): + + if coef is None: + return coef * value + + if is_scalar(coef): + return coef * value + + if is_tensor(coef): + coef = fill_axis(coef, value.ndim + 1 if batched else value.ndim) + return coef * value + else: + raise TypeError(f"coef should be int, float or TensorLike, but got {type(coef)}.") diff --git a/fealpy/fdm/ocp_opt.py b/fealpy/fdm/ocp_opt.py new file mode 100644 index 000000000..1d5ed9a5e --- /dev/null +++ b/fealpy/fdm/ocp_opt.py @@ -0,0 +1,147 @@ +from fealpy.mesh import TriangleMesh +from fealpy.backend import backend_manager as bm +from fealpy.functionspace import LagrangeFESpace +from fealpy.old.timeintegratoralg import UniformTimeLine +from fealpy.functionspace import TensorFunctionSpace +from ocp_opt_pde import example_1 +#from solver import ocp_opt_solver +from solver_update import ocp_opt_solver +from fealpy.fem import DirichletBC + +from scipy.sparse import coo_array, bmat +from functools import partial +from fealpy import logger +from fealpy.solver import spsolve +logger.setLevel('ERROR') #积分子问题 + +bm.set_backend("numpy") +pde = example_1() +n = 20 +q = 4 +T = 1 +nt = 30 +maxit = 3 + +mesh = TriangleMesh.from_box(pde.domain(), nx=n, ny=n) +timeline = UniformTimeLine(0, T, nt) +dt = timeline.dt + +yspace= LagrangeFESpace(mesh, p=1) +space = LagrangeFESpace(mesh, p=1) +pspace = TensorFunctionSpace(space, (2,-1)) +solver = ocp_opt_solver(mesh, yspace, pspace, pde, timeline) + +ygodf = yspace.number_of_global_dofs() +pgdof = pspace.number_of_global_dofs() +yisbdof = yspace.is_boundary_dof() +pisbdof = pspace.is_boundary_dof() +isbdof = bm.concatenate([yisbdof, pisbdof], axis=0)#(362,) + + +ally = [None]*(nt+1) +allp = [None]*(nt+1) +allu = [None]*(nt+1) + + +y0 = yspace.function(yspace.interpolate(partial(pde.y_solution, time=0))) +y0t = yspace.interpolate(partial(pde.y_t_solution, time=0)) +p0 = pspace.function(pspace.interpolate(partial(pde.p_solution, time=0))) #(242,) p0x1,p0x2 +ally[0] = y0 +allp[0] = p0 + + +for k in range(maxit): + #A0 = solver.A0n() + A0 = solver.FBForm_A0().assembly() + b0 = solver.FLform_b0(allu[1]).assembly() + + #A0, b0 = solver.forward_boundary(A0, b0, isbdof, dt) + BC = DirichletBC(space=(yspace,pspace), threshold=(None, None), gd=(partial(pde.y_solution,time=0), partial(pde.p_solution,time = 0)),method='interp') + A0, b0 = BC.apply(A0, b0) #检查一下边界条件的处理 ,这里的A矩阵不一样。 + x0 = spsolve(A0, b0, solver='scipy') + + y1 = yspace.function() + p1 = pspace.function() + + y1[:] = x0[:ygodf] + p1[:] = x0[-pgdof:] #p1x1, p1x2 + ally[1] = y1 + allp[1] = p1 + timeline.advance() + + A_Bform= solver.FBform_A() + b_lform = solver.FLform_b() + AA = A_Bform.assembly() + # 正向求解 + for i in range(nt-1): + t1 = timeline.next_time_level() + tnextindex = timeline.current_time_level_index()+1 + + y2 = yspace.function() + px = pspace.function() + solver.FLformb_update(y0, y1, allu[tnextindex], t1) + b = b_lform.assembly() + BC = DirichletBC(space=(yspace,pspace), threshold=(None, None), gd=(partial(pde.y_solution,time=t1), partial(pde.p_solution,time = t1)),method='interp') + A, b = BC.apply(AA, b) + x = spsolve(A, b, solver='scipy') + y0[:] = y1 + y1[:] = x[:ygodf] + px[:] = x[-pgdof:] + ally[tnextindex] = y1 + allp[tnextindex] = px + timeline.advance() + + + zn0 = yspace.function(yspace.interpolate(partial(pde.z_solution, time=T))) + zn0t = yspace.interpolate(partial(pde.z_t_solution, time=0)) + zn1 = yspace.function() + zn2 = yspace.function() + qx = pspace.function() + un0 = yspace.function() + un1 = yspace.function() + + un0[:] = solver.solve_u(zn0) #积分子 + allu[tnextindex] = un0 + An = solver.FBForm_A0().assembly() + bn = solver.BLform_b0(ally[-1], allp[-1]).assembly() + BC = DirichletBC(space=(yspace,pspace), threshold=(None, None), gd=(partial(pde.y_solution,time=T), partial(pde.p_solution,time = T)),method='interp') + An, bn = BC.apply(An, bn) + xn = solver.mumps_solve(An.tocoo(), bn) + + zn1[:] = xn[:ygodf] + qx[:] = xn[-pgdof:] + timeline.backward() + + tnextindex = timeline.current_time_level_index() + un1[:] = solver.solve_u(zn1) + allu[tnextindex] = un1 + + A_Bform= solver.FBform_A() + b_lform = solver.BLform_b() + A = A_Bform.assembly() + + # 反向求解 + for i in range(nt-1): + t1 = timeline.prev_time_level() + tnextindex = timeline.current_time_level_index()-1 + u = yspace.function() + solver.BLformb_update(zn0, zn1, ally[tnextindex], allp[tnextindex], t1) + b = b_lform.assembly() + BC = DirichletBC(space=(yspace,pspace), threshold=(None, None), gd=(partial(pde.y_solution,time=t1), partial(pde.p_solution,time = t1)),method='interp') + A, b = BC.apply(A, b) + x = solver.mumps_solve(A.tocoo(), b) + + zn0[:] = zn1 + zn1[:] = x[:ygodf] + qx[:] = x[-pgdof:] + u[:] = solver.solve_u(zn1) + allu[tnextindex] = u + timeline.backward() + + +ysolution = yspace.function(yspace.interpolate(partial(pde.y_solution, time=T))) +usolution = yspace.function(yspace.interpolate(partial(pde.u_solution, time=0))) +errory = mesh.error(ally[-1], ysolution) +erroru = mesh.error(allu[0], usolution) +print(errory) +print(erroru) diff --git a/fealpy/fdm/ocp_opt_pde.py b/fealpy/fdm/ocp_opt_pde.py new file mode 100644 index 000000000..45ef6dbb3 --- /dev/null +++ b/fealpy/fdm/ocp_opt_pde.py @@ -0,0 +1,164 @@ +#!/usr/bin/python3 +'''! + @Author: wpx + @File Name: ocp_opt_pde.py + @Mail: wpx15673207315@gmail.com + @Created Time: Thu 05 Sep 2024 04:57:54 PM CST + @bref + @ref +''' +from fealpy.backend import backend_manager as bm +from fealpy.decorator import cartesian +import sympy as sp + +class example_1: + def __init__(self): + t, x1, x2 = sp.symbols('t, x1, x2', real=True) + self.x1 = x1 + self.x2 = x2 + self.t = t + self.c = 1 + + self.y = (1-x1)*(1-x2)*x1*x2*x1*x2*sp.exp(t) + self.z = (1-x1)*(1-x2)*x1*x2*x1*x2*(t-1)**3 + self.u = -(1-x1)*(1-x2)*x1*x2*x1*x2*(t-1)**3 + p0 = -(1+x1**2)*(1-x2)*x2*(1-2*x1)*sp.exp(t) + p1 = -(1+x2**2)*(1-x1)*x1*(1-2*x2)*sp.exp(t) + self.p = sp.Matrix([p0, p1]) + self.q0 = -sp.pi*sp.cos(sp.pi*x1)*sp.sin(sp.pi*x2)*(t-1)**2 + self.q1 = -sp.pi*sp.sin(sp.pi*x1)*sp.cos(sp.pi*x2)*(t-1)**2 + self.q = sp.Matrix([self.q0, self.q1]) + self.A00 = 1+x1**2 + self.A11 = 1+x2**2 + self.A = sp.Matrix([[self.A00, 0], [0, self.A11]]) + + def domain(self): + return [0, 1, 0, 1] + + @cartesian + def y_solution(self, space, time): + manager, = bm._backends + x1 = self.x1 + x2 = self.x2 + t = self.t + result = sp.lambdify([x1,x2,t], self.y, manager) + return result(space[...,0], space[...,1], time) + + @cartesian + def y_t_solution(self, space, time): + manager, = bm._backends + x1 = self.x1 + x2 = self.x2 + t = self.t + result = sp.lambdify([x1,x2,t], sp.diff(self.y, t), manager) + return result(space[...,0], space[...,1], time) + + @cartesian + def z_solution(self, space, time): + manager, = bm._backends + x1 = self.x1 + x2 = self.x2 + t = self.t + result = sp.lambdify([x1,x2,t], self.z, manager) + return result(space[...,0], space[...,1], time) + + @cartesian + def z_t_solution(self, space, time): + manager, = bm._backends + x1 = self.x1 + x2 = self.x2 + t = self.t + result = sp.lambdify([x1,x2,t], sp.diff(self.z, t), manager) + return result(space[...,0], space[...,1], time) + + @cartesian + def u_solution(self, space, time): + manager, = bm._backends + x1 = self.x1 + x2 = self.x2 + t = self.t + result = sp.lambdify([x1,x2,t], self.u, manager) + return result(space[...,0], space[...,1], time) + + @cartesian + def p_solution(self, space, time): + x = space[..., 0] + y = space[..., 1] + val1 = -(1+x**2)*(1-y)*y*(1-2*x)*bm.exp(time) + val2 = -(1+y**2)*(1-x)*x*(1-2*y)*bm.exp(time) + result = bm.stack([val1, val2], axis=-1) + return result + + @cartesian + def qx_solution(self, space, time): + manager, = bm._backends + x1 = self.x1 + x2 = self.x2 + t = self.t + val = sp.Matrix([self.q0, self.q1]) + result = sp.lambdify([x1,x2,t], val, manager) + output = result(space[..., 0], space[..., 1], time) + reshape_output = output.transpose(2, 0, 1).squeeze() + return reshape_output + + + @cartesian + def A_matirx(self, space): + x = space[..., 0] + y = space[..., 1] + result = bm.zeros(space.shape[:-1]+(2,2)) + result[..., 0, 0] = 1+x**2 + result[..., 1, 1] = 1+y**2 + return result + @cartesian + def A_inverse(self, space): + x = space[..., 0] + y = space[..., 1] + result = bm.zeros(space.shape[:-1]+(2,2)) + result[..., 0, 0] = 1/(1+x**2) + result[..., 1, 1] = 1/(1+y**2) + print(result.shape) + return result + + @cartesian + def f_fun(self, space, time, index=None): + manager, = bm._backends + y = self.y + p = self.p + u = self.u + t = self.t + x1 = self.x1 + x2 = self.x2 + self.f = sp.diff(y, t, 2) + sp.diff(p[0], x1) + sp.diff(p[1], x2) + y - u + result = sp.lambdify([x1,x2,t], self.f, manager) + return result(space[...,0], space[...,1], time) + @cartesian + def p_d_fun(self, space, time): + manager, = bm._backends + p = self.p + q = self.q + z = self.z + A = self.A + t = self.t + x1 = self.x1 + x2 = self.x2 + grad_z = sp.Matrix([sp.diff(z, x1), sp.diff(z, x2)]) + A_inv = A.inv() + p_d = p + A_inv*q + grad_z + self.p_d = p_d + result = sp.lambdify([x1,x2,t], self.p_d, manager) + output = result(space[..., 0], space[..., 1], time) # 原始形状为 (2, 1, 200, 10) + output = output.transpose(2, 3, 0, 1).squeeze() # 变为 (200, 10, 2) + return output + @cartesian + def y_d_fun(self, space, time): + manager, = bm._backends + q = self.q + y = self.y + z = self.z + t = self.t + x1 = self.x1 + x2 = self.x2 + self.y_d = y - sp.diff(z, t, 2) + sp.diff(q[0], x1) + sp.diff(q[1], x2) + result = sp.lambdify([x1,x2,t], self.y_d, manager) + return result(space[...,0], space[...,1], time) diff --git a/fealpy/fdm/ocp_yuanshi.py b/fealpy/fdm/ocp_yuanshi.py new file mode 100644 index 000000000..8de689e90 --- /dev/null +++ b/fealpy/fdm/ocp_yuanshi.py @@ -0,0 +1,142 @@ +#!/usr/bin/python3 +'''! + @Author: wpx + @File Name: ocp-opt.py + @Mail: wpx15673207315@gmail.com + @Created Time: Thu 05 Sep 2024 04:50:58 PM CST + @bref + @ref +''' +from fealpy.mesh import TriangleMesh +from fealpy.backend import backend_manager as bm +from fealpy.functionspace import LagrangeFESpace +from fealpy.old.timeintegratoralg import UniformTimeLine +from fealpy.functionspace import TensorFunctionSpace +from ocp_opt_pde import example_1 +from solver import ocp_opt_solver + +from scipy.sparse import coo_array, bmat +from functools import partial +from fealpy import logger +logger.setLevel('ERROR') #积分子问题 + +bm.set_backend("numpy") +pde = example_1() +n = 10 +q = 4 +T = 1 +nt = 30 +maxit = 3 + +mesh = TriangleMesh.from_box(pde.domain(), nx=n, ny=n) +timeline = UniformTimeLine(0, T, nt) +dt = timeline.dt + +yspace= LagrangeFESpace(mesh, p=1) +space = LagrangeFESpace(mesh, p=1) +pspace = TensorFunctionSpace(space, (2,-1)) +solver = ocp_opt_solver(mesh, yspace, pspace, pde, timeline) + +ygodf = yspace.number_of_global_dofs() +pgdof = pspace.number_of_global_dofs() +yisbdof = yspace.is_boundary_dof() +pisbdof = pspace.is_boundary_dof() +isbdof = bm.concatenate([yisbdof, pisbdof], axis=0)#(362,) + + +ally = [None]*(nt+1) +allp = [None]*(nt+1) +allu = [None]*(nt+1) + + +y0 = yspace.function(yspace.interpolate(partial(pde.y_solution, time=0))) +y0t = yspace.interpolate(partial(pde.y_t_solution, time=0)) +p0 = pspace.function(pspace.interpolate(partial(pde.p_solution, time=0))) #(242,) p0x1,p0x2 +ally[0] = y0 +allp[0] = p0 + + +for k in range(maxit): + A0 = solver.A0n() + b0 = solver.forward_b0(allu[1]) + A0, b0 = solver.forward_boundary(A0, b0, isbdof, dt) + x0 = solver.mumps_solve(A0, b0) + + y1 = yspace.function() + p1 = pspace.function() + + y1[:] = x0[:ygodf] + p1[:] = x0[-pgdof:] #p1x1, p1x2 + ally[1] = y1 + allp[1] = p1 + timeline.advance() + + AA = solver.A() + #正向求解 + for i in range(nt-1): + t1 = timeline.next_time_level() + tnextindex = timeline.current_time_level_index()+1 # 为什么要加1 + + y2 = yspace.function() + px = pspace.function() + b = solver.forward_b(y0, y1, allu[tnextindex], t1) + A,b = solver.forward_boundary(AA, b, isbdof, t1) + + x = solver.mumps_solve(A, b) + y0[:] = y1 + y1[:] = x[:ygodf] + px[:] = x[-pgdof:] + ally[tnextindex] = y1 + allp[tnextindex] = px + timeline.advance() + + + zn0 = yspace.function(yspace.interpolate(partial(pde.z_solution, time=T))) + zn0t = yspace.interpolate(partial(pde.z_t_solution, time=0)) + zn1 = yspace.function() + zn2 = yspace.function() + qx = pspace.function() + un0 = yspace.function() + un1 = yspace.function() + + un0[:] = solver.solve_u(zn0) #积分子 + allu[tnextindex] = un0 + + An = solver.A0n() + # TODO: 未完成 矩阵拼接可以用blockform + bn = solver.backward_b0(ally[-1], allp[-1]) + An, bn = solver.backward_boundary(An, bn, isbdof, T-dt) + xn = solver.mumps_solve(An, bn) + + zn1[:] = xn[:ygodf] + qx[:] = xn[-pgdof:] + timeline.backward() + + tnextindex = timeline.current_time_level_index() + un1[:] = solver.solve_u(zn1) + allu[tnextindex] = un1 + + # 反向求解 + for i in range(nt-1): + t1 = timeline.prev_time_level() + tnextindex = timeline.current_time_level_index()-1 + u = yspace.function() + b = solver.backward_b(zn0, zn1, ally[tnextindex], allp[tnextindex], t1) + A,b = solver.forward_boundary(AA, b, isbdof, t1) + + x = solver.mumps_solve(A, b) + + zn0[:] = zn1 + zn1[:] = x[:ygodf] + qx[:] = x[-pgdof:] + u[:] = solver.solve_u(zn1) + allu[tnextindex] = u + timeline.backward() + + +ysolution = yspace.function(yspace.interpolate(partial(pde.y_solution, time=T))) +usolution = yspace.function(yspace.interpolate(partial(pde.u_solution, time=0))) +errory = mesh.error(ally[-1], ysolution) +erroru = mesh.error(allu[0], usolution) +print(errory) +print(erroru) diff --git a/fealpy/fdm/solver.py b/fealpy/fdm/solver.py new file mode 100644 index 000000000..e8879870e --- /dev/null +++ b/fealpy/fdm/solver.py @@ -0,0 +1,289 @@ +from fealpy.fem import BilinearForm, ScalarMassIntegrator +from fealpy.fem import PressWorkIntegrator +from fealpy.fem import LinearForm, ScalarSourceIntegrator +from fealpy.decorator import barycentric, cartesian +from fealpy.backend import backend_manager as bm +from fealpy.sparse import COOTensor +from functools import partial + +from ocp_opt_pde import example_1 + +class ocp_opt_solver(): + def __init__(self, mesh, yspace, pspace, pde, timeline,q=4): + self.mesh = mesh + self.yspace = yspace + self.pspace = pspace + self.pde = pde + self.q = q + self.dt = timeline.dt + self.timeline = timeline + + @cartesian + def coef_M00(p, index = None): + return pde.A_inverse(p)[...,0,0] + bform = BilinearForm(yspace) + bform.add_integrator(ScalarMassIntegrator(coef=coef_M00, q=q)) + self.M00 = bform.assembly() + + @cartesian + def coef_M01(p, index = None): + return pde.A_inverse(p)[...,0,1] + bform = BilinearForm(yspace) + bform.add_integrator(ScalarMassIntegrator(coef=coef_M01, q=q)) + self.M01 = bform.assembly() + + @cartesian + def coef_M10(p, index = None): + return pde.A_inverse(p)[...,1,0] + bform = BilinearForm(yspace) + bform.add_integrator(ScalarMassIntegrator(coef=coef_M10, q=q)) + self.M10 = bform.assembly() + + @cartesian + def coef_M11(p, index = None): + return pde.A_inverse(p)[...,1,1] + bform = BilinearForm(yspace) + bform.add_integrator(ScalarMassIntegrator(coef=coef_M11, q=q)) + self.M11 = bform.assembly() + + + bform = BilinearForm(yspace) + bform.add_integrator(ScalarMassIntegrator(q=q)) + self.M = bform.assembly() + + bform = BilinearForm((yspace, pspace)) + bform.add_integrator(PressWorkIntegrator(-1,q=q)) + self.S1 = bform.assembly() + + bform = BilinearForm((yspace,pspace)) + bform.add_integrator(PressWorkIntegrator(-1,q=q)) + self.S2 = bform.assembly() + + bform = BilinearForm((yspace, pspace)) + bform.add_integrator(PressWorkIntegrator(-1,q=q)) + self.S = bform.assembly() + + + def A0n(self): + dt = self.dt + M = self.M.tocoo() + S = self.S.tocoo() + M00 = self.M00.tocoo() + M01 = self.M01.tocoo() + M10 = self.M10.tocoo() + M11 = self.M11.tocoo() + M0 = COOTensor.concat((M00, M01), axis=1) + M1 = COOTensor.concat((M10, M11), axis=1) + M_A = COOTensor.concat((M0, M1), axis=0) + A0 = COOTensor.concat(((2/dt**2 + self.pde.c)*M, -dt**2*S), axis=0) + A1 = COOTensor.concat((S.T, M_A), axis=0) + A = COOTensor.concat((A0,A1), axis=1) + return A + def forward_b0(self, u1): + if u1 == None: + u1 = self.yspace.function() + + @cartesian + def fun(p, index=None): + result = self.pde.y_solution(p, 0) + result += self.dt * self.pde.y_t_solution(p, 0) + result *= 2/self.dt**2 + return result + @cartesian + def coef(p, index=None): + result = self.pde.f_fun(p, time=self.dt, index=index) + return result + lform = LinearForm(self.yspace) + lform.add_integrator(ScalarSourceIntegrator(coef,q=self.q)) + lform.add_integrator(ScalarSourceIntegrator(u1, q=self.q)) + lform.add_integrator(ScalarSourceIntegrator(fun, q=self.q)) + F0 = lform.assembly() + F1 = bm.zeros(self.pspace.number_of_global_dofs()) + b = bm.concatenate([F0,F1]) + return b + + def backward_b0(self, yn1, pn): + T =self.timeline.T1 + + @barycentric + def fun(bcs, index=None): + result = yn1(bcs) + return result + + @cartesian + def coef(p, index=None): + result = self.pde.z_solution(p, T) + result -= self.pde.z_t_solution(p, T)*self.dt + result *= 2/self.dt**2 + result += -self.pde.y_d_fun(p, time=T-self.dt) + return result + + lform = LinearForm(self.yspace) + lform.add_integrator(ScalarSourceIntegrator(coef,q=self.q)) + lform.add_integrator(ScalarSourceIntegrator(fun, q=self.q)) + F0 = lform.assembly() + + @barycentric + def funp(bcs, index=None): + result = -pn(bcs) + return result + + @cartesian + def coefp(p, index=None): + result = self.pde.p_d_fun(p, time=T-self.dt) + return result + lform = LinearForm(self.pspace) + lform.add_integrator(ScalarSourceIntegrator(coefp,q=self.q)) + lform.add_integrator(ScalarSourceIntegrator(funp, q=self.q)) + F1= lform.assembly() + + b = bm.concatenate([F0,F1]) + return b + + + + def forward_boundary(self, A, b, isbdof, t): + x0 = bm.zeros(self.yspace.number_of_global_dofs()) + yh, yisbdof = self.yspace.boundary_interpolate( + partial(self.pde.y_solution, time=t), x0) + x1 = bm.zeros(self.pspace.number_of_global_dofs()) + px1, pisbdof = self.pspace.boundary_interpolate( + partial(self.pde.p_solution, time=t), x1) + # x2 = bm.zeros(self.yspace.number_of_global_dofs()) + # px2, pisbdof = self.yspace.boundary_interpolate( + # partial(self.pde.px2_solution, time=t), x2) + xx = bm.concatenate([x0,x1]) + b -= A@xx + b[isbdof] = xx[isbdof] + + kwargs = A.values_context() + indices = A.indices() + new_values = bm.copy(A.values()) + IDX = isbdof[indices[0, :]] | isbdof[indices[1, :]] + new_values[IDX] = 0 + A = COOTensor(indices, new_values, A.sparse_shape) + + index = bm.nonzero(isbdof)[0] + shape = new_values.shape[:-1] + (len(index), ) + one_values = bm.ones(shape, **kwargs) + one_indices = bm.stack([index, index], axis=0) + A1 = COOTensor(one_indices, one_values, A.sparse_shape) + A = A.add(A1).coalesce() + + return A,b + + def backward_boundary(self, A, b, isbdof, t): + x0 = bm.zeros(self.yspace.number_of_global_dofs()) + zh, zisbdof = self.yspace.boundary_interpolate( + partial(self.pde.z_solution, time=t), x0) + x1 = bm.zeros(self.pspace.number_of_global_dofs()) + qx1, qisbdof = self.pspace.boundary_interpolate( + partial(self.pde.qx_solution, time=t),x1) + xx = bm.concatenate([x0,x1]) + + b -= A@xx + b[isbdof] = xx[isbdof] + + kwargs = A.values_context() + indices = A.indices() + new_values = bm.copy(A.values()) + IDX = isbdof[indices[0, :]] | isbdof[indices[1, :]] + new_values[IDX] = 0 + A = COOTensor(indices, new_values, A.sparse_shape) + + index = bm.nonzero(isbdof)[0] + shape = new_values.shape[:-1] + (len(index), ) + one_values = bm.ones(shape, **kwargs) + one_indices = bm.stack([index, index], axis=0) + A1 = COOTensor(one_indices, one_values, A.sparse_shape) + A = A.add(A1).coalesce() + return A,b + + def A(self): + dt = self.dt + S = self.S.tocoo() + M = self.M.tocoo() + M01 = self.M01.tocoo() + M10 = self.M10.tocoo() + M11 = self.M11.tocoo() + M00 = self.M00.tocoo() + M0 = COOTensor.concat((M00, M01), axis=1) + M1 = COOTensor.concat((M10, M11), axis=1) + M_A = COOTensor.concat((M0, M1), axis=0) + A0 = COOTensor.concat(((1/dt**2 + self.pde.c)*M, -dt**2*S), axis=0) + A1 = COOTensor.concat((S.T,M_A ), axis=0) + A = COOTensor.concat((A0,A1), axis=1) + return A + + def forward_b(self, y0, y1, u, t): + if u==None: + u = self.yspace.function() + + @barycentric + def fun(bcs, index=None): + result = 2*y1(bcs) - y0(bcs) + result *= 1/self.dt**2 + return result + @cartesian + def coef(p, index=None): + result = self.pde.f_fun(p, time=t, index=index) + return result + + lform = LinearForm(self.yspace) + lform.add_integrator(ScalarSourceIntegrator(coef,q=self.q)) + lform.add_integrator(ScalarSourceIntegrator(u, q=self.q)) + lform.add_integrator(ScalarSourceIntegrator(fun, q=self.q)) + F0 = lform.assembly() + F1 = bm.zeros(self.pspace.number_of_global_dofs()) + b = bm.concatenate([F0,F1]) + return b + + def backward_b(self, zn0, zn1, yn1, pn, t): + + @barycentric + def fun(bcs, index=None): + result = 2*zn0(bcs) - zn1(bcs) + result *= 1/self.dt**2 + result += yn1(bcs) + return result + + @cartesian + def coef(p, index=None): + result = -self.pde.y_d_fun(p, time=t) + return result + + lform = LinearForm(self.yspace) + lform.add_integrator(ScalarSourceIntegrator(coef,q=self.q)) + lform.add_integrator(ScalarSourceIntegrator(fun, q=self.q)) + F0 = lform.assembly() + + @barycentric + def funpx1(bcs, index=None): + result = -pn(bcs) + return result + + @cartesian + def coefpx1(p, index=None): + result = self.pde.p_d_fun(p, time=t) + return result + lform = LinearForm(self.pspace) + lform.add_integrator(ScalarSourceIntegrator(coefpx1,q=self.q)) + lform.add_integrator(ScalarSourceIntegrator(funpx1, q=self.q)) + F1= lform.assembly() + + b = bm.concatenate([F0,F1]) + return b + + def solve_u(self, z): + result = bm.max(self.mesh.integral(z), 0) - z #积分子 + return result + + def mumps_solve(self, A, b): + import scipy.sparse as sp + values = A.values() + indices = A.indices() + A = sp.coo_matrix((values, (indices[0], indices[1])), shape=A.shape) + A = A.tocsr() + x = sp.linalg.spsolve(A,b) + return x + diff --git a/fealpy/fdm/solver_update.py b/fealpy/fdm/solver_update.py new file mode 100644 index 000000000..f19493ed8 --- /dev/null +++ b/fealpy/fdm/solver_update.py @@ -0,0 +1,309 @@ +from fealpy.fem import BilinearForm, ScalarMassIntegrator +from fealpy.fem import PressWorkIntegrator +from fealpy.fem import LinearForm, ScalarSourceIntegrator +from fealpy.fem import BilinearForm, LinearForm, BlockForm,LinearBlockForm +from fealpy.decorator import barycentric, cartesian +from fealpy.backend import backend_manager as bm +from fealpy.sparse import COOTensor +from functools import partial +from tensor_mass_integrator import TensorMassIntegrator +from ocp_opt_pde import example_1 + +class ocp_opt_solver(): + def __init__(self, mesh, yspace, pspace, pde, timeline,q=4): + self.mesh = mesh + self.yspace = yspace + self.pspace = pspace + self.pde = pde + self.q = q + self.dt = timeline.dt + self.timeline = timeline + + def FBForm_A0(self): + pspace = self.pspace + yspace = self.yspace + dt = self.dt + pde = self.pde + q = self.q + c = pde.c + """ + @cartesian + def coef_M00(p, index = None): + return pde.A_inverse(p)[...,0,0] + M00 = BilinearForm(yspace) + M00.add_integrator(ScalarMassIntegrator(coef=coef_M00, q=q)) + + @cartesian + def coef_M01(p, index = None): + return pde.A_inverse(p)[...,0,1] + M01 = BilinearForm(yspace) + M01.add_integrator(ScalarMassIntegrator(coef=coef_M01, q=q)) + + @cartesian + def coef_M10(p, index = None): + return pde.A_inverse(p)[...,1,0] + M10 = BilinearForm(yspace) + M10.add_integrator(ScalarMassIntegrator(coef=coef_M10, q=q)) + + @cartesian + def coef_M11(p, index = None): + return pde.A_inverse(p)[...,1,1] + M11 = BilinearForm(yspace) + M11.add_integrator(ScalarMassIntegrator(coef=coef_M11, q=q)) + M_A_inv = BlockForm([[M00, M01], [M10, M11]]) + """ + M_inv = BilinearForm(yspace) + M_inv.add_integrator(TensorMassIntegrator(coef=pde.A_inverse,q=q)) + + + + M = BilinearForm(yspace) + M.add_integrator(ScalarMassIntegrator(coef=2+c*dt**2,q=q)) + + S = BilinearForm((yspace, pspace)) + S.add_integrator(PressWorkIntegrator(coef=-dt**2,q=q)) + + S1 = BilinearForm((yspace, pspace)) + S1.add_integrator(PressWorkIntegrator(1,q=q)) + + A0 = BlockForm([[M, S.T], [S1, M_inv]]) + + return A0 + + def FBform_A(self): + yspace = self.yspace + pde = self.pde + q = self.q + pspace = self.pspace + dt = self.dt + c = pde.c + @cartesian + def coef_M00(p, index = None): + return pde.A_inverse(p)[...,0,0] + M00 = BilinearForm(yspace) + M00.add_integrator(ScalarMassIntegrator(coef=coef_M00, q=q)) + + @cartesian + def coef_M01(p, index = None): + return pde.A_inverse(p)[...,0,1] + M01 = BilinearForm(yspace) + + M01.add_integrator(ScalarMassIntegrator(coef=coef_M01, q=q)) + + @cartesian + def coef_M10(p, index = None): + return pde.A_inverse(p)[...,1,0] + M10 = BilinearForm(yspace) + M10.add_integrator(ScalarMassIntegrator(coef=coef_M10, q=q)) + + @cartesian + def coef_M11(p, index = None): + return pde.A_inverse(p)[...,1,1] + M11 = BilinearForm(yspace) + M11.add_integrator(ScalarMassIntegrator(coef=coef_M11, q=q)) + + M_A_inv = BlockForm([[M00, M01], [M10, M11]]) + + M = BilinearForm(yspace) + M.add_integrator(ScalarMassIntegrator(coef=2+c*dt**2,q=q)) + + S = BilinearForm((yspace, pspace)) + S.add_integrator(PressWorkIntegrator(coef=-dt**2,q=q)) + + S1 = BilinearForm((yspace, pspace)) + S1.add_integrator(PressWorkIntegrator(1,q=q)) + + A = BlockForm([[M, S.T], [S1, M_A_inv]]) + + return A + + def FLform_b0(self,u1): + yspace = self.yspace + pspace = self.pspace + pde = self.pde + q = self.q + + if u1 == None: + u1 = self.yspace.function() + + @cartesian + def fun(p, index=None): + result = self.pde.y_solution(p, 0) + result += self.dt * self.pde.y_t_solution(p, 0) + result *= 2/self.dt**2 + return result + @cartesian + def coef(p, index=None): + result = self.pde.f_fun(p, time=self.dt, index=index) + return result + + L0 = LinearForm(yspace) + f_coef =ScalarSourceIntegrator(coef,q=q) + u_coef = ScalarSourceIntegrator(u1,q=q) + fun_coef = ScalarSourceIntegrator(fun,q=q) + L0.add_integrator(f_coef) + L0.add_integrator(u_coef) + L0.add_integrator(fun_coef) + + L1 = LinearForm(pspace) + + L = LinearBlockForm([L0, L1]) + + return L + + def FLform_b(self): + yspace = self.yspace + pspace = self.pspace + pde = self.pde + q = self.q + + L0 = LinearForm(yspace) + self.f_coef =ScalarSourceIntegrator(q=q) + self.u_coef = ScalarSourceIntegrator(q=q) + self.fun_coef = ScalarSourceIntegrator(q=q) + L0.add_integrator(self.f_coef) + L0.add_integrator(self.u_coef) + L0.add_integrator(self.fun_coef) + + L1 = LinearForm(pspace) + + L = LinearBlockForm([L0, L1]) + return L + + def FLformb_update(self, y0, y1, u, t): + if u==None: + u = self.yspace.function() + self.u_coef.source = u + self.u_coef.clear() + + @barycentric + def fun(bcs, index=None): + result = 2*y1(bcs) - y0(bcs) + result *= 1/self.dt**2 + return result + self.fun_coef.source = fun + self.fun_coef.clear() + + @cartesian + def coef(p, index=None): + result = self.pde.f_fun(p, time=t, index=index) + return result + self.f_coef.source = coef + self.f_coef.clear() + + def BLform_b0(self, yn1, pn): + yspace = self.yspace + pspace = self.pspace + pde = self.pde + q = self.q + T =self.timeline.T1 + + @barycentric + def fun(bcs, index=None): + result = yn1(bcs) + return result + + @cartesian + def coef(p, index=None): + result = self.pde.z_solution(p, T) + result -= self.pde.z_t_solution(p, T)*self.dt + result *= 2/self.dt**2 + result += -self.pde.y_d_fun(p, time=T-self.dt) + return result + + @barycentric + def funp(bcs, index=None): + result = -pn(bcs) + return result + + @cartesian + def coefp(p, index=None): + result = self.pde.p_d_fun(p, time=T-self.dt) + return result + + L0 = LinearForm(yspace) + f_coef =ScalarSourceIntegrator(q=q) + f_coef.source = coef + fun_coef = ScalarSourceIntegrator(q=q) + fun_coef.source = fun + L0.add_integrator(f_coef) + L0.add_integrator(fun_coef) + + L1 = LinearForm(pspace) + coefp_coef = ScalarSourceIntegrator(q=q) + coefp_coef.source = coefp + funp_coef = ScalarSourceIntegrator(q=q) + funp_coef.source = funp + L1.add_integrator(coefp_coef) + L1.add_integrator(funp_coef) + + L = LinearBlockForm([L0, L1]) + return L + + def BLform_b(self): + yspace = self.yspace + pspace = self.pspace + pde = self.pde + q = self.q + T =self.timeline.T1 + + L0 = LinearForm(yspace) + self.yd_coef =ScalarSourceIntegrator(q=q) + self.fun_coef = ScalarSourceIntegrator(q=q) + L0.add_integrator(self.yd_coef) + L0.add_integrator(self.fun_coef) + + L1 = LinearForm(pspace) + self.funpx1_coef = ScalarSourceIntegrator(q=q) + self.coefpx1_coef = ScalarSourceIntegrator(q=q) + L1.add_integrator(self.coefpx1_coef) + L1.add_integrator(self.funpx1_coef) + + L = LinearBlockForm([L0, L1]) + return L + + def BLformb_update(self, zn0, zn1, yn1, pn, t): + @barycentric + def fun(bcs, index=None): + result = 2*zn0(bcs) - zn1(bcs) + result *= 1/self.dt**2 + result += yn1(bcs) + return result + self.fun_coef.source = fun + self.fun_coef.clear() + @cartesian + def coef(p, index=None): + result = -self.pde.y_d_fun(p, time=t) + return result + self.yd_coef.source = coef + self.yd_coef.clear() + + @barycentric + def funpx1(bcs, index=None): + result = -pn(bcs) + return result + self.funpx1_coef.source = funpx1 + self.funpx1_coef.clear() + + + @cartesian + def coefpx1(p, index=None): + result = self.pde.p_d_fun(p, time=t) + return result + self.coefpx1_coef.source = coefpx1 + self.coefpx1_coef.clear() + + + def solve_u(self, z): + result = bm.max(self.mesh.integral(z), 0) - z #积分子 + return result + + def mumps_solve(self, A, b): + import scipy.sparse as sp + values = A.values() + indices = A.indices() + A = sp.coo_matrix((values, (indices[0], indices[1])), shape=A.shape) + A = A.tocsr() + x = sp.linalg.spsolve(A,b) + return x + diff --git a/fealpy/fdm/tensor_mass_integrator.py b/fealpy/fdm/tensor_mass_integrator.py new file mode 100644 index 000000000..b9d30258a --- /dev/null +++ b/fealpy/fdm/tensor_mass_integrator.py @@ -0,0 +1,74 @@ +from typing import Optional + +from fealpy.backend import backend_manager as bm +from fealpy.typing import TensorLike, Index, _S + +from fealpy.mesh import HomogeneousMesh +from fealpy.functionspace.space import FunctionSpace as _FS +from fealpy.utils import process_coef_func +from fealpy.functional import linear_integral, get_semilinear_coef + +from fealpy.fem.integrator import ( + LinearInt, OpInt, CellInt, + enable_cache, + assemblymethod, + CoefLike +) +from bilinear import bilinear_integral + +class TensorMassIntegrator(LinearInt, OpInt, CellInt): + def __init__(self, coef: Optional[CoefLike]=None, q: Optional[int]=None, *, + index: Index=_S, + batched: bool=False, + method: Optional[str]=None) -> None: + method = 'assembly' if (method is None) else method + super().__init__(method=method) + self.coef = coef + self.q = q + self.index = index + self.batched = batched + + @enable_cache + def to_global_dof(self, space: _FS) -> TensorLike: + return space.cell_to_dof()[self.index] + + @enable_cache + def fetch(self, space: _FS): + q = self.q + index = self.index + mesh = getattr(space, 'mesh', None) + + if not isinstance(mesh, HomogeneousMesh): + raise RuntimeError("The ScalarMassIntegrator only support spaces on" + f"homogeneous meshes, but {type(mesh).__name__} is" + "not a subclass of HomoMesh.") + + cm = mesh.entity_measure('cell', index=index) + q = space.p+3 if self.q is None else self.q + qf = mesh.quadrature_formula(q, 'cell') + bcs, ws = qf.get_quadrature_points_and_weights() + phi = space.basis(bcs, index=index) + return bcs, ws, phi, cm, index + + def assembly(self, space: _FS) -> TensorLike: + coef = self.coef + mesh = getattr(space, 'mesh', None) + bcs, ws, phi, cm, index = self.fetch(space) + val = process_coef_func(coef, bcs=bcs, mesh=mesh, etype='cell', index=index) + + return bilinear_integral(phi, phi, ws, cm, val, batched=self.batched) + + @assemblymethod('semilinear') + def semilinear_assembly(self, space: _FS) -> TensorLike: + uh = self.uh + coef = self.coef + mesh = getattr(space, 'mesh', None) + bcs, ws, phi, cm, index = self.fetch(space) + val_A = coef.grad_func(uh(bcs)) #(C, Q) + val_F = -coef.func(uh(bcs)) #(C, Q) + coef = process_coef_func(coef, bcs=bcs, mesh=mesh, etype='cell', index=index) + coef_A = get_semilinear_coef(val_A, coef) + coef_F = get_semilinear_coef(val_F, coef) + + return bilinear_integral(phi, phi, ws, cm, coef_A, batched=self.batched), \ + linear_integral(phi, ws, cm, coef_F, batched=self.batched) From 27a32af131c597ef939a761a1c95c3e650ac185a Mon Sep 17 00:00:00 2001 From: BenHBLiu Date: Mon, 18 Nov 2024 17:04:48 +0800 Subject: [PATCH 04/63] update --- fealpy/opt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fealpy/opt/__init__.py b/fealpy/opt/__init__.py index c7252e4a1..eba2a36c6 100644 --- a/fealpy/opt/__init__.py +++ b/fealpy/opt/__init__.py @@ -19,4 +19,4 @@ from .cuckoo_search_opt import CuckooSearchOpt from .Butterfly_opt_alg import ButterflyOptAlg from .exponential_trigonometric_opt_alg import ExponentialTrigonometricOptAlg -from .Differential_Evolution import DifferentialEvolution \ No newline at end of file +from .differential_evolution import DifferentialEvolution \ No newline at end of file From b6ec6b3ff93416de4cc9eb469a0a7ba3330edb47 Mon Sep 17 00:00:00 2001 From: BenHBLiu Date: Mon, 18 Nov 2024 17:05:08 +0800 Subject: [PATCH 05/63] add Chaos --- fealpy/opt/opt_function.py | 96 +++++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/fealpy/opt/opt_function.py b/fealpy/opt/opt_function.py index f81c06e51..949a3a41a 100644 --- a/fealpy/opt/opt_function.py +++ b/fealpy/opt/opt_function.py @@ -2,6 +2,9 @@ from scipy.special import gamma def levy(n, m, beta): + """ + Levy flight + """ num = gamma(1 + beta) * bm.sin(bm.array(bm.pi * beta / 2)) den = gamma((1 + beta) / 2) * beta * 2 ** ((beta - 1) / 2) sigma_u = (num / den) ** (1 / beta) @@ -12,7 +15,98 @@ def levy(n, m, beta): def initialize(pop_size, dim, ub, lb, way: int = 0): + """ + Initialization + + way = 1 -- 10 : chaos + """ + + rand = bm.random.rand(pop_size, dim) + if way == 0: - pop = lb + bm.random.rand(pop_size, dim) * (ub - lb) + rand = rand + + # Tent + elif way == 1: + tent = 1.1 + for j in range(1,dim): + mask = rand[:, j - 1] < tent + rand[:, j] = bm.where(mask, rand[:, j - 1] / tent, (1 - rand[:, j - 1]) / (1 - tent)) + + # Logistic + elif way == 2: + miu = 2 + for i in range(1, dim): + rand[:, i] = miu * rand[:, i - 1] * (1 - rand[:, i - 1]) + + # Cubic + elif way == 3: + cubic = 1 + for j in range(1, dim): + rand[:, j] = cubic * rand[:, j - 1] * (1 - rand[:, j - 1] ** 2) + + # Chebyshev + elif way == 4: + chebyshev = 4 + for j in range(1, dim): + rand[:, j] = bm.cos(chebyshev * bm.arccos(rand[:, j - 1])) + + # Piecewise + elif way == 5: + p = 1 + for j in range(1, dim): + prev_col = rand[:, j - 1] + cond1 = (0 < prev_col) & (prev_col < p) + cond2 = (p <= prev_col) & (prev_col < 0.5) + cond3 = (0.5 <= prev_col) & (prev_col < 1 - p) + cond4 = (1 - p <= prev_col) & (prev_col < 1) + rand[:, j] = bm.where(cond1, prev_col / p, + bm.where(cond2, (prev_col - p) / (0.5 - p), + bm.where(cond3, (1 - p - prev_col) / (0.5 - p), + bm.where(cond4, (1 - prev_col) / p, rand[:, j])))) + + # Sinusoidal + elif way == 6: + sinusoidal = 2 + for j in range(1, dim): + rand[:, j] = sinusoidal * rand[:, j - 1] ** 2 * bm.sin(bm.pi * rand[:, j - 1]) + + # Sine + elif way == 7: + beta = 1 + alpha = 1 + for j in range(1, dim): + rand[:, j] =alpha * bm.sin(beta * rand[:, j - 1]) + + # Icmic + elif way == 8: + icmic = 2 + for j in range(1, dim): + rand[:, j] = bm.sin(icmic / rand[:, j - 1]) + + # Circle + elif way == 9: + a = 0.5 + b = 0.6 + for j in range(1, dim): + rand[:, j] = (rand[:, j -1] + a - b / (2 * bm.pi) * bm.sin(2 * bm.pi * rand[:, j - 1])) % 1 + + # Bernoulli + elif way == 10: + lammda = 0.4 + prev_col = rand[:, :-1] + condition = prev_col < (1 - lammda) + rand[:, 1:] = bm.where( + condition, + prev_col / (1 - lammda), + (prev_col - 1 + lammda) / lammda + ) + + if isinstance(ub, (float, int)): + pop = lb + rand * (ub - lb) + else: + for i in range(dim): + pop[:, i] = rand[:, i] * (ub[i] - lb[i]) + lb[i] + return pop \ No newline at end of file From 811f3009e58e8bb8bc7ec33ac0c2194a6ba59dcd Mon Sep 17 00:00:00 2001 From: wpx <1143615697@qq.com> Date: Tue, 19 Nov 2024 11:20:14 +0800 Subject: [PATCH 06/63] update --- app/tssim/ocp_opt/jovan/ocp_opt.py | 170 ++++++++++ .../tssim/ocp_opt/jovan}/ocp_opt_pde.py | 77 ++--- app/tssim/ocp_opt/jovan/solver_update.py | 258 +++++++++++++++ app/tssim/wang/pde.py | 4 +- .../wang/tangent_face_mass_integrator.py | 4 +- fealpy/fdm/bilinear.py | 122 ------- fealpy/fdm/ocp_opt.py | 147 --------- fealpy/fdm/ocp_yuanshi.py | 142 -------- fealpy/fdm/solver.py | 289 ---------------- fealpy/fdm/solver_update.py | 309 ------------------ fealpy/fdm/tensor_mass_integrator.py | 74 ----- fealpy/fem/press_work_integrator.py | 8 +- 12 files changed, 475 insertions(+), 1129 deletions(-) create mode 100644 app/tssim/ocp_opt/jovan/ocp_opt.py rename {fealpy/fdm => app/tssim/ocp_opt/jovan}/ocp_opt_pde.py (72%) create mode 100644 app/tssim/ocp_opt/jovan/solver_update.py delete mode 100644 fealpy/fdm/bilinear.py delete mode 100644 fealpy/fdm/ocp_opt.py delete mode 100644 fealpy/fdm/ocp_yuanshi.py delete mode 100644 fealpy/fdm/solver.py delete mode 100644 fealpy/fdm/solver_update.py delete mode 100644 fealpy/fdm/tensor_mass_integrator.py diff --git a/app/tssim/ocp_opt/jovan/ocp_opt.py b/app/tssim/ocp_opt/jovan/ocp_opt.py new file mode 100644 index 000000000..aa6e097ae --- /dev/null +++ b/app/tssim/ocp_opt/jovan/ocp_opt.py @@ -0,0 +1,170 @@ +from fealpy.mesh import TriangleMesh +from fealpy.backend import backend_manager as bm +from fealpy.functionspace import LagrangeFESpace +from fealpy.old.timeintegratoralg import UniformTimeLine +from fealpy.functionspace import TensorFunctionSpace +from ocp_opt_pde import example_1 +#from solver import ocp_opt_solver +from solver_update import ocp_opt_solver +from fealpy.fem import DirichletBC + +from scipy.sparse import coo_array, bmat +from functools import partial +from fealpy import logger +from fealpy.solver import spsolve +logger.setLevel('ERROR') #积分子问题 + +bm.set_backend("numpy") +pde = example_1() +n = 20 +q = 4 +T = 1 +nt = 30 +maxit = 3 + +mesh = TriangleMesh.from_box(pde.domain(), nx=n, ny=n) +timeline = UniformTimeLine(0, T, nt) +dt = timeline.dt + +yspace= LagrangeFESpace(mesh, p=1) +space = LagrangeFESpace(mesh, p=1) +pspace = TensorFunctionSpace(space, (2,-1)) +solver = ocp_opt_solver(mesh, yspace, pspace, pde, timeline) + +ygodf = yspace.number_of_global_dofs() +pgdof = pspace.number_of_global_dofs() +yisbdof = yspace.is_boundary_dof() +pisbdof = pspace.is_boundary_dof() +isbdof = bm.concatenate([yisbdof, pisbdof], axis=0)#(362,) + + +ally = [None]*(nt+1) +allp = [None]*(nt+1) +allu = [None]*(nt+1) + +y0 = yspace.function(yspace.interpolate(partial(pde.y_solution, time=0))) +y0t = yspace.interpolate(partial(pde.y_t_solution, time=0)) +p0 = pspace.function(pspace.interpolate(partial(pde.p_solution, time=0))) #(242,) p0x1,p0x2 +zn = yspace.function(yspace.interpolate(partial(pde.z_solution, time=T))) +znt = yspace.interpolate(partial(pde.z_t_solution, time=0)) + +zn0 = yspace.function() +zn0[:] = zn[:] +zn1 = yspace.function() +zn2 = yspace.function() +q2 = pspace.function() + +un = yspace.function() +un[:] = solver.z_to_u(zn) #积分子 +allu[-1] = un +ally[0] = y0 +allp[0] = p0 + +A0 = solver.Forward_BForm_A0().assembly() +b0_LForm = solver.Forward_LForm_b0() + +A = solver.Forward_BForm_A().assembly() +Forward_b_LForm = solver.Forward_LForm_b() + +An = solver.Forward_BForm_A0().assembly() +bn_LForm = solver.Backward_LForm_bn() + +Backward_A = solver.Forward_BForm_A().assembly() +Backward_b_LForm = solver.Backward_LForm_b() + +for k in range(maxit): + y1 = yspace.function() + p1 = pspace.function() + + solver.Forward_0_update(allu[1]) + b0 = b0_LForm.assembly() + + BC = DirichletBC(space=(yspace,pspace), threshold=(None, None), + gd=(partial(pde.y_solution,time=dt), + partial(pde.p_solution,time=dt)),method='interp') + A0, b0 = BC.apply(A0, b0) + x0 = spsolve(A0, b0, solver='scipy') + + y1[:] = x0[:ygodf] + p1[:] = x0[-pgdof:] + ally[1] = y1 + allp[1] = p1 + + timeline.advance() + + # 正向求解 + for i in bm.arange(2, nt+1): + t1 = timeline.current_time_level() + t1index = timeline.current_time_level_index() + + y2 = yspace.function() + p2 = pspace.function() + + solver.Forward_update(ally[t1index-1], ally[t1index], allu[t1index+1], t1+dt) + Forward_b = Forward_b_LForm.assembly() + + BC = DirichletBC(space=(yspace,pspace), threshold=(None, None), + gd=(partial(pde.y_solution,time=t1+dt), + partial(pde.p_solution,time=t1+dt)),method='interp') + Forward_A, Forward_b = BC.apply(A, Forward_b) + Forward_x = spsolve(Forward_A, Forward_b, solver='scipy') + + y2[:] = Forward_x[:ygodf] + p2[:] = Forward_x[-pgdof:] + ally[t1index+1] = y2 + allp[t1index+1] = p2 + timeline.advance() + + ## 反向求解第0步 + t1 = timeline.current_time_level() + t1index = timeline.current_time_level_index() + + solver.Backward_n_update(ally[t1index-1], allp[t1index-1]) + bn = bn_LForm.assembly() + + BC = DirichletBC(space=(yspace,pspace), threshold=(None, None), + gd=(partial(pde.y_solution,time = T-dt), + partial(pde.p_solution,time = T-dt)),method='interp') + An, bn = BC.apply(An, bn) + xn = spsolve(An, bn, solver='scipy') + zn1[:] = xn[:ygodf] + q2[:] = xn[-pgdof:] + + un1 = yspace.function() + un1[:] = solver.z_to_u(zn1) + allu[t1index-1] = un1 + timeline.backward() + + ## 反向求解 + for i in bm.arange(nt-1, 0, -1): + t1 = timeline.current_time_level() + t1index = timeline.current_time_level_index() + + + ##求第i-1步的z,q + solver.Backward_update(zn0, zn1, ally[t1index-1], allp[t1index-1], t1-dt) + Backward_b = Backward_b_LForm.assembly() + BC = DirichletBC(space=(yspace,pspace), threshold=(None, None), + gd=(partial(pde.y_solution,time = t1-dt), + partial(pde.p_solution,time = t1-dt)),method='interp') + Backward_A, Backward_b = BC.apply(Backward_A, Backward_b) + Backward_x = spsolve(Backward_A, Backward_b, solver='scipy') + + zn2[:] = Backward_x[:ygodf] + q2 = Backward_x[-pgdof:] + + ##求第i-1步的u + un2 = yspace.function() + un2[:] = solver.z_to_u(zn2) + allu[t1index-1] = un2 + + zn0[:] = zn1[:] + zn1[:] = zn2[:] + timeline.backward() + +ysolution = yspace.function(yspace.interpolate(partial(pde.y_solution, time=T))) +usolution = yspace.function(yspace.interpolate(partial(pde.u_solution, time=0))) +errory = mesh.error(ally[-1], ysolution) +erroru = mesh.error(allu[0], usolution) +print(errory) +print(erroru) diff --git a/fealpy/fdm/ocp_opt_pde.py b/app/tssim/ocp_opt/jovan/ocp_opt_pde.py similarity index 72% rename from fealpy/fdm/ocp_opt_pde.py rename to app/tssim/ocp_opt/jovan/ocp_opt_pde.py index 45ef6dbb3..83ee4f5a3 100644 --- a/fealpy/fdm/ocp_opt_pde.py +++ b/app/tssim/ocp_opt/jovan/ocp_opt_pde.py @@ -1,33 +1,30 @@ #!/usr/bin/python3 -'''! - @Author: wpx - @File Name: ocp_opt_pde.py - @Mail: wpx15673207315@gmail.com - @Created Time: Thu 05 Sep 2024 04:57:54 PM CST - @bref - @ref -''' from fealpy.backend import backend_manager as bm from fealpy.decorator import cartesian import sympy as sp class example_1: - def __init__(self): + def __init__(self, c=1): + self.c = c + self.manager, = bm._backends + t, x1, x2 = sp.symbols('t, x1, x2', real=True) self.x1 = x1 self.x2 = x2 self.t = t - self.c = 1 - self.y = (1-x1)*(1-x2)*x1*x2*x1*x2*sp.exp(t) - self.z = (1-x1)*(1-x2)*x1*x2*x1*x2*(t-1)**3 - self.u = -(1-x1)*(1-x2)*x1*x2*x1*x2*(t-1)**3 - p0 = -(1+x1**2)*(1-x2)*x2*(1-2*x1)*sp.exp(t) - p1 = -(1+x2**2)*(1-x1)*x1*(1-2*x2)*sp.exp(t) - self.p = sp.Matrix([p0, p1]) + self.y = (1-x1)*(1-x2)*x1*x2*sp.exp(t) + self.z = (1-x1)*(1-x2)*x1*x2*(t-1)**3 + self.u = -(1-x1)*(1-x2)*x1*x2*(t-1)**3 + + self.p0 = -(1+x1**2)*(1-x2)*x2*(1-2*x1)*sp.exp(t) + self.p1= -(1+x2**2)*(1-x1)*x1*(1-2*x2)*sp.exp(t) + self.p = sp.Matrix([self.p0, self.p1]) + self.q0 = -sp.pi*sp.cos(sp.pi*x1)*sp.sin(sp.pi*x2)*(t-1)**2 self.q1 = -sp.pi*sp.sin(sp.pi*x1)*sp.cos(sp.pi*x2)*(t-1)**2 self.q = sp.Matrix([self.q0, self.q1]) + self.A00 = 1+x1**2 self.A11 = 1+x2**2 self.A = sp.Matrix([[self.A00, 0], [0, self.A11]]) @@ -37,66 +34,71 @@ def domain(self): @cartesian def y_solution(self, space, time): - manager, = bm._backends x1 = self.x1 x2 = self.x2 t = self.t - result = sp.lambdify([x1,x2,t], self.y, manager) + result = sp.lambdify([x1,x2,t], self.p[0], self.manager) return result(space[...,0], space[...,1], time) @cartesian def y_t_solution(self, space, time): - manager, = bm._backends x1 = self.x1 x2 = self.x2 t = self.t - result = sp.lambdify([x1,x2,t], sp.diff(self.y, t), manager) + result = sp.lambdify([x1,x2,t], sp.diff(self.y, t), self.manager) return result(space[...,0], space[...,1], time) @cartesian def z_solution(self, space, time): - manager, = bm._backends x1 = self.x1 x2 = self.x2 t = self.t - result = sp.lambdify([x1,x2,t], self.z, manager) + result = sp.lambdify([x1,x2,t], self.z, self.manager) return result(space[...,0], space[...,1], time) @cartesian def z_t_solution(self, space, time): - manager, = bm._backends x1 = self.x1 x2 = self.x2 t = self.t - result = sp.lambdify([x1,x2,t], sp.diff(self.z, t), manager) + result = sp.lambdify([x1,x2,t], sp.diff(self.z, t), self.manager) return result(space[...,0], space[...,1], time) @cartesian def u_solution(self, space, time): - manager, = bm._backends x1 = self.x1 x2 = self.x2 t = self.t - result = sp.lambdify([x1,x2,t], self.u, manager) + result = sp.lambdify([x1,x2,t], self.u, self.manager) return result(space[...,0], space[...,1], time) @cartesian def p_solution(self, space, time): - x = space[..., 0] - y = space[..., 1] + ''' val1 = -(1+x**2)*(1-y)*y*(1-2*x)*bm.exp(time) val2 = -(1+y**2)*(1-x)*x*(1-2*y)*bm.exp(time) result = bm.stack([val1, val2], axis=-1) + ''' + x1 = self.x1 + x2 = self.x2 + t = self.t + + x = space[..., 0] + y = space[..., 1] + result = bm.zeros_like(space) + p0 = sp.lambdify([x1,x2,t], self.p0, self.manager) + p1 = sp.lambdify([x1,x2,t], self.p1, self.manager) + result[...,0] = p0(x, y, time) + result[...,1] = p1(x, y, time) return result @cartesian - def qx_solution(self, space, time): - manager, = bm._backends + def q_solution(self, space, time): x1 = self.x1 x2 = self.x2 t = self.t val = sp.Matrix([self.q0, self.q1]) - result = sp.lambdify([x1,x2,t], val, manager) + result = sp.lambdify([x1,x2,t], val, self.manager) output = result(space[..., 0], space[..., 1], time) reshape_output = output.transpose(2, 0, 1).squeeze() return reshape_output @@ -110,6 +112,7 @@ def A_matirx(self, space): result[..., 0, 0] = 1+x**2 result[..., 1, 1] = 1+y**2 return result + @cartesian def A_inverse(self, space): x = space[..., 0] @@ -117,12 +120,10 @@ def A_inverse(self, space): result = bm.zeros(space.shape[:-1]+(2,2)) result[..., 0, 0] = 1/(1+x**2) result[..., 1, 1] = 1/(1+y**2) - print(result.shape) return result @cartesian def f_fun(self, space, time, index=None): - manager, = bm._backends y = self.y p = self.p u = self.u @@ -130,11 +131,11 @@ def f_fun(self, space, time, index=None): x1 = self.x1 x2 = self.x2 self.f = sp.diff(y, t, 2) + sp.diff(p[0], x1) + sp.diff(p[1], x2) + y - u - result = sp.lambdify([x1,x2,t], self.f, manager) + result = sp.lambdify([x1,x2,t], self.f, self.manager) return result(space[...,0], space[...,1], time) + @cartesian def p_d_fun(self, space, time): - manager, = bm._backends p = self.p q = self.q z = self.z @@ -146,13 +147,13 @@ def p_d_fun(self, space, time): A_inv = A.inv() p_d = p + A_inv*q + grad_z self.p_d = p_d - result = sp.lambdify([x1,x2,t], self.p_d, manager) + result = sp.lambdify([x1,x2,t], self.p_d, self.manager) output = result(space[..., 0], space[..., 1], time) # 原始形状为 (2, 1, 200, 10) output = output.transpose(2, 3, 0, 1).squeeze() # 变为 (200, 10, 2) return output + @cartesian def y_d_fun(self, space, time): - manager, = bm._backends q = self.q y = self.y z = self.z @@ -160,5 +161,5 @@ def y_d_fun(self, space, time): x1 = self.x1 x2 = self.x2 self.y_d = y - sp.diff(z, t, 2) + sp.diff(q[0], x1) + sp.diff(q[1], x2) - result = sp.lambdify([x1,x2,t], self.y_d, manager) + result = sp.lambdify([x1,x2,t], self.y_d, self.manager) return result(space[...,0], space[...,1], time) diff --git a/app/tssim/ocp_opt/jovan/solver_update.py b/app/tssim/ocp_opt/jovan/solver_update.py new file mode 100644 index 000000000..5085a5a1e --- /dev/null +++ b/app/tssim/ocp_opt/jovan/solver_update.py @@ -0,0 +1,258 @@ +from fealpy.fem import BilinearForm, ScalarMassIntegrator +from fealpy.fem import PressWorkIntegrator +from fealpy.fem import LinearForm, ScalarSourceIntegrator +from fealpy.fem import BilinearForm, LinearForm, BlockForm,LinearBlockForm +from fealpy.decorator import barycentric, cartesian +from fealpy.backend import backend_manager as bm +from fealpy.sparse import COOTensor +from functools import partial +from tensor_mass_integrator import TensorMassIntegrator +from ocp_opt_pde import example_1 + +class ocp_opt_solver(): + def __init__(self, mesh, yspace, pspace, pde, timeline,q=4): + self.mesh = mesh + self.yspace = yspace + self.pspace = pspace + self.pde = pde + self.q = q + self.dt = timeline.dt + self.timeline = timeline + + def Forward_BForm_A0(self): + pspace = self.pspace + yspace = self.yspace + dt = self.dt + pde = self.pde + q = self.q + c = pde.c + M_y = BilinearForm(yspace) + M_y.add_integrator(ScalarMassIntegrator(coef=2+c*dt**2,q=q)) + + M_p = BilinearForm(pspace) + M_p.add_integrator(ScalarMassIntegrator(coef=pde.A_inverse,q=q)) + + S = BilinearForm((pspace, yspace)) + S.add_integrator(PressWorkIntegrator(coef=-dt**2,q=q)) + + S1 = BilinearForm((pspace, yspace)) + S1.add_integrator(PressWorkIntegrator(1,q=q)) + + A = BlockForm([[M_y, S], [S1.T, M_p]]) + return A + + Backward_BForm_An = Forward_BForm_A0 + + def Forward_BForm_A(self): + pspace = self.pspace + yspace = self.yspace + dt = self.dt + pde = self.pde + q = self.q + c = pde.c + + M_y = BilinearForm(yspace) + M_y.add_integrator(ScalarMassIntegrator(coef=1+c*dt**2,q=q)) + + M_p = BilinearForm(pspace) + M_p.add_integrator(ScalarMassIntegrator(coef=pde.A_inverse,q=q)) + + S = BilinearForm((pspace, yspace)) + S.add_integrator(PressWorkIntegrator(coef=-dt**2,q=q)) + + S1 = BilinearForm((pspace, yspace)) + S1.add_integrator(PressWorkIntegrator(1,q=q)) + + A = BlockForm([[M_y, S], [S1.T, M_p]]) + return A + + Backward_BForm_A = Forward_BForm_A + + def Forward_LForm_b0(self, u1=None): + yspace = self.yspace + pspace = self.pspace + pde = self.pde + q = self.q + dt = self.dt + + + @cartesian + def coef(p, index=None): + result = (dt**2)*self.pde.f_fun(p, time=dt) + result += 2*(self.pde.y_solution(p, time=0) + dt * self.pde.y_t_solution(p, time=0)) + return result + + + Ly = LinearForm(yspace) + Ly.add_integrator(ScalarSourceIntegrator(coef,q=q)) + self.forward_0_b_coef = ScalarSourceIntegrator(q=q) + Ly.add_integrator(self.forward_0_b_coef) + + Lp = LinearForm(pspace) + #TODO:检查是不是一直是0 + L = LinearBlockForm([Ly, Lp]) + return L + + def Forward_0_update(self, u1): + dt = self.dt + if u1 == None: + u1 = self.yspace.function() + + @barycentric + def coef_u1(bcs, index=None): + result = (dt**2)*u1(bcs) + return result + self.forward_0_b_coef.source = coef_u1 + + + def Forward_LForm_b(self): + yspace = self.yspace + pspace = self.pspace + pde = self.pde + q = self.q + + Ly = LinearForm(yspace) + self.c_coef = ScalarSourceIntegrator(q=q) + self.b_coef = ScalarSourceIntegrator(q=q) + Ly.add_integrator(self.c_coef) + Ly.add_integrator(self.b_coef) + + Lp = LinearForm(pspace) + + L = LinearBlockForm([Ly, Lp]) + return L + + def Forward_update(self, y0, y1, u2, t2): + dt = self.dt + + if u2==None: + u2 = self.yspace.function() + + @barycentric + def coef_b(bcs, index=None): + result = 2*y1(bcs, index) - y0(bcs, index) + result += (dt**2)*u2(bcs) + return result + + self.b_coef.source = coef_b + self.b_coef.clear() + + @cartesian + def coef_c(p, index=None): + result = (dt**2) * self.pde.f_fun(p, time=t2, index=index) + return result + self.c_coef.source = coef_c + self.c_coef.clear() + + + ### 反向求解 + def z_to_u(self, z1): + result = bm.max(self.mesh.integral(z1), 0) - z1 #积分子 + return result + + ## TODO:Pd Yd 的求解 + def Backward_LForm_bn(self): + yspace = self.yspace + pspace = self.pspace + pde = self.pde + q = self.q + T =self.timeline.T1 + dt = self.dt + + @cartesian + def z_coef_c(p, index=None): + result = 2*self.pde.z_solution(p, T) - 2*self.pde.z_t_solution(p, T)*self.dt + result -= dt**2 * self.pde.y_d_fun(p, time=T-dt) + return result + + Lz = LinearForm(yspace) + Lz.add_integrator(ScalarSourceIntegrator(z_coef_c,q=q)) + self.backward_n_z_coef = ScalarSourceIntegrator(q=q) + Lz.add_integrator(self.backward_n_z_coef) + + @cartesian + def q_coef_c(p, index=None): + result = self.pde.p_d_fun(p, T-dt) + return result + Lq = LinearForm(pspace) + Lq.add_integrator(ScalarSourceIntegrator(q_coef_c,q=q)) + self.backward_n_q_coef = ScalarSourceIntegrator(q=q) + Lq.add_integrator(self.backward_n_q_coef) + L = LinearBlockForm([Lz, Lq]) + return L + + def Backward_n_update(self, y2, p2): + dt = self.dt + + @barycentric + def coef_z(bcs, index=None): + result = dt**2 * y2(bcs, index) + return result + self.backward_n_z_coef.source = coef_z + self.backward_n_z_coef.clear() + + @barycentric + def coef_q(bcs, index=None): + result = - p2(bcs, index) + return result + self.backward_n_q_coef.source = coef_q + self.backward_n_q_coef.clear() + + + def Backward_LForm_b(self): + yspace = self.yspace + pspace = self.pspace + pde = self.pde + q = self.q + dt = self.dt + + Lz = LinearForm(yspace) + self.backward_z_c_coef = ScalarSourceIntegrator(q=q) + self.backward_z_b_coef = ScalarSourceIntegrator(q=q) + Lz.add_integrator(self.backward_z_b_coef) + Lz.add_integrator(self.backward_z_c_coef) + + Lq = LinearForm(pspace) + self.backward_q_c_coef = ScalarSourceIntegrator(q=q) + self.backward_q_b_coef = ScalarSourceIntegrator(q=q) + Lq.add_integrator(self.backward_q_b_coef) + Lq.add_integrator(self.backward_q_c_coef) + + L = LinearBlockForm([Lz, Lq]) + + return L + + def Backward_update(self, zn0, zn1, yn2, p2, t2): + dt = self.dt + @cartesian + def coef_c_z(p, index=None): + result = -dt**2 * self.pde.y_d_fun(p, time=t2) + return result + + @barycentric + def coef_b_z(bcs, index=None): + result = 2*zn1(bcs) - zn0(bcs) + result += dt**2 * yn2(bcs) + return result + self.backward_z_c_coef.source = coef_c_z + self.backward_z_c_coef.clear() + self.backward_z_b_coef.source = coef_b_z + self.backward_z_b_coef.clear() + + @cartesian + def coef_c_q(p, index=None): + result = self.pde.p_d_fun(p, time=t2) + return result + + @barycentric + def coef_b_q(bcs, index=None): + result = -p2(bcs) + return result + self.backward_q_c_coef.source = coef_c_q + self.backward_q_c_coef.clear() + self.backward_q_b_coef.source = coef_b_q + self.backward_q_b_coef.clear() + + + + diff --git a/app/tssim/wang/pde.py b/app/tssim/wang/pde.py index 58149d05e..f3ff25500 100644 --- a/app/tssim/wang/pde.py +++ b/app/tssim/wang/pde.py @@ -22,10 +22,8 @@ def __init__(self, eps=1e-10, h=1/256): ## init the parameter self.R = 5.0 ##dimensionless self.l_s = 0.0025 ##dimensionless slip length - #self.L_s = self.l_s / h - #self.L_s = 1/self.l_s + self.L_s = self.l_s self.epsilon = 0.004 ## the thickness of interface - self.L_s = self.l_s/self.epsilon self.L_d = 0.0005 ##phenomenological mobility cofficient self.lam = 12.0 ##dimensionless self.V_s = 200.0 ##dimensionless diff --git a/app/tssim/wang/tangent_face_mass_integrator.py b/app/tssim/wang/tangent_face_mass_integrator.py index 5c4ab4e24..6082da08f 100644 --- a/app/tssim/wang/tangent_face_mass_integrator.py +++ b/app/tssim/wang/tangent_face_mass_integrator.py @@ -66,7 +66,7 @@ def assembly(self, space: _FS): mesh = space.mesh val = process_coef_func(coef, bcs=bcs, mesh=mesh, etype='face', index=index) phit = bm.einsum('eqid, ed -> eqi', phi, t[index,...]) - phii = bm.einsum('eqid -> eqi', phi) - #result = bm.einsum('eqi, eqj, q, e ->eij', phii, phit, ws, fm)*coef + #phii = bm.einsum('eqid -> eqi', phi) + phii = phi[...,0] result = bilinear_integral(phii, phit, ws, fm, val, batched=self.batched) return result diff --git a/fealpy/fdm/bilinear.py b/fealpy/fdm/bilinear.py deleted file mode 100644 index 78bdcac26..000000000 --- a/fealpy/fdm/bilinear.py +++ /dev/null @@ -1,122 +0,0 @@ - -from typing import Optional - -from fealpy.backend import backend_manager as bm -from fealpy.typing import TensorLike, CoefLike -from fealpy.utils import is_scalar, is_tensor, fill_axis - - -def integral(value: TensorLike, weights: TensorLike, measure: TensorLike, *, - entity_type=False) -> TensorLike: - """Numerical integration. - - Parameters: - value (TensorLike[..., C, Q]): The values on the quadrature points to be integrated. - weights (TensorLike[Q,]): The weights of the quadrature points. - measure (TensorLike[C,]): The measure of the quadrature points. - entity_type (bool): Whether to return integration in each entity. Defaults to False. - - Returns: - TensorLike: The result of the integration. The shape will be [..., C]\ - if `entity_type` is True, otherwise [...]. - """ - subs = '...c' if entity_type else '...' - return bm.einsum(f'c, q, ...cq -> {subs}', measure, weights, value) - - -def linear_integral(basis: TensorLike, weights: TensorLike, measure: TensorLike, - source: Optional[CoefLike]=None, - batched: bool=False) -> TensorLike: - """Numerical integration. - - Parameters: - basis (TensorLike[C, Q, I, ...]): The values on the quadrature points to be integrated. - weights (TensorLike[Q,]): The weights of the quadrature points. - measure (TensorLike[C,]): The measure of the mesh entity. - source (Number, TensorLike, optional): The source of the integration. Defaults to None. - Must be int, float, TensorLike, or callable returning TensorLike with shape (C,), (C, Q) or (C, Q, ...). - If `batched == True`, there should be a batch dimension as the first axis. - batched (bool, optional): Whether the source are batched. Defaults to False. - - Returns: - TensorLike: The result of the integration shaped (C, I) for source (C, Q, ...). - And (C, I, ...) for source (C, ) and (C, Q). - If `batched` is True, there will be a batch dimension as the first axis. - """ - if source is None: - return bm.einsum('c, q, cq... -> c...', measure, weights, basis) - - if is_scalar(source): - return bm.einsum('c, q, cq... -> c...', measure, weights, basis) * source - - elif is_tensor(source): - dof_shape = basis.shape[3:] - basis = basis.reshape(*basis.shape[:3], -1) # (C, Q, I, dof_numel) - - if source.ndim <= 2 + int(batched): - source = fill_axis(source, 3 if batched else 2) - r = bm.einsum(f'c, q, cqid, ...cq -> ...cid', measure, weights, basis, source) - return bm.reshape(r, r.shape[:-1] + dof_shape) - else: - source = fill_axis(source, 4 if batched else 3) - return bm.einsum(f'c, q, cqid, ...cqd -> ...ci', measure, weights, basis, source) - - else: - raise TypeError(f"source should be int, float or TensorLike, but got {type(source)}.") - - -def bilinear_integral(basis1: TensorLike, basis2: TensorLike, weights: TensorLike, - measure: TensorLike, - coef: Optional[CoefLike]=None, - batched: bool=False) -> TensorLike: - """Numerical integration. - - Parameters: - basis1 (TensorLike[C, Q, I, ...]): The values on the quadrature points to be integrated. - basis2 (TensorLike[C, Q, J, ...]): The values on the quadrature points to be integrated. - weights (TensorLike[Q,]): The weights of the quadrature points. - measure (TensorLike[C,]): The measure of the mesh entity. - coef (Number, TensorLike, optional): The coefficient of the integration. Defaults to None. - Must be int, float, TensorLike, or callable returning TensorLike with shape (C,), (C, Q) or (C, Q, ...). - If `batched == True`, there should be a batch dimension as the first axis. - batched (bool, optional): Whether the coef are batched. Defaults to False. - - Returns: - TensorLike: The result of the integration shaped (C, I, J). - If `batched` is True, the shape of the output is (B, C, I, J). - """ - basis1 = basis1.reshape(*basis1.shape[:3], -1) # (C, Q, I, dof_numel) - basis2 = basis2.reshape(*basis2.shape[:3], -1) # (C, Q, J, dof_numel) - - if coef is None: - return bm.einsum(f'q, c, cqid, cqjd -> cij', weights, measure, basis1, basis2) - - if is_scalar(coef): - return bm.einsum(f'q, c, cqid, cqjd -> cij', weights, measure, basis1, basis2) * coef - - elif is_tensor(coef): - # 检查 coef 的维度 - if coef.ndim >= 4 and coef.shape[-2] == coef.shape[-1]: # 检查 coef 最后两个维度是否为 (n, n) - # 进行矩阵形式的求和操作 (后两维为 n x n 的情况) - return bm.einsum(f'q, c, cqid, cqjd, ...cqkl -> ...cijkl', weights, measure, basis1, basis2, coef) - else: - # 保留原有 fill_axis 逻辑,适用于其他维度情况 - coef = fill_axis(coef, 4 if batched else 3) - return bm.einsum(f'q, c, cqid, cqjd, ...cqd -> ...cij', weights, measure, basis1, basis2, coef) - - else: - raise TypeError(f"coef should be int, float or TensorLike, but got {type(coef)}.") - -def get_semilinear_coef(value:TensorLike, coef: Optional[CoefLike]=None, batched: bool=False): - - if coef is None: - return coef * value - - if is_scalar(coef): - return coef * value - - if is_tensor(coef): - coef = fill_axis(coef, value.ndim + 1 if batched else value.ndim) - return coef * value - else: - raise TypeError(f"coef should be int, float or TensorLike, but got {type(coef)}.") diff --git a/fealpy/fdm/ocp_opt.py b/fealpy/fdm/ocp_opt.py deleted file mode 100644 index 1d5ed9a5e..000000000 --- a/fealpy/fdm/ocp_opt.py +++ /dev/null @@ -1,147 +0,0 @@ -from fealpy.mesh import TriangleMesh -from fealpy.backend import backend_manager as bm -from fealpy.functionspace import LagrangeFESpace -from fealpy.old.timeintegratoralg import UniformTimeLine -from fealpy.functionspace import TensorFunctionSpace -from ocp_opt_pde import example_1 -#from solver import ocp_opt_solver -from solver_update import ocp_opt_solver -from fealpy.fem import DirichletBC - -from scipy.sparse import coo_array, bmat -from functools import partial -from fealpy import logger -from fealpy.solver import spsolve -logger.setLevel('ERROR') #积分子问题 - -bm.set_backend("numpy") -pde = example_1() -n = 20 -q = 4 -T = 1 -nt = 30 -maxit = 3 - -mesh = TriangleMesh.from_box(pde.domain(), nx=n, ny=n) -timeline = UniformTimeLine(0, T, nt) -dt = timeline.dt - -yspace= LagrangeFESpace(mesh, p=1) -space = LagrangeFESpace(mesh, p=1) -pspace = TensorFunctionSpace(space, (2,-1)) -solver = ocp_opt_solver(mesh, yspace, pspace, pde, timeline) - -ygodf = yspace.number_of_global_dofs() -pgdof = pspace.number_of_global_dofs() -yisbdof = yspace.is_boundary_dof() -pisbdof = pspace.is_boundary_dof() -isbdof = bm.concatenate([yisbdof, pisbdof], axis=0)#(362,) - - -ally = [None]*(nt+1) -allp = [None]*(nt+1) -allu = [None]*(nt+1) - - -y0 = yspace.function(yspace.interpolate(partial(pde.y_solution, time=0))) -y0t = yspace.interpolate(partial(pde.y_t_solution, time=0)) -p0 = pspace.function(pspace.interpolate(partial(pde.p_solution, time=0))) #(242,) p0x1,p0x2 -ally[0] = y0 -allp[0] = p0 - - -for k in range(maxit): - #A0 = solver.A0n() - A0 = solver.FBForm_A0().assembly() - b0 = solver.FLform_b0(allu[1]).assembly() - - #A0, b0 = solver.forward_boundary(A0, b0, isbdof, dt) - BC = DirichletBC(space=(yspace,pspace), threshold=(None, None), gd=(partial(pde.y_solution,time=0), partial(pde.p_solution,time = 0)),method='interp') - A0, b0 = BC.apply(A0, b0) #检查一下边界条件的处理 ,这里的A矩阵不一样。 - x0 = spsolve(A0, b0, solver='scipy') - - y1 = yspace.function() - p1 = pspace.function() - - y1[:] = x0[:ygodf] - p1[:] = x0[-pgdof:] #p1x1, p1x2 - ally[1] = y1 - allp[1] = p1 - timeline.advance() - - A_Bform= solver.FBform_A() - b_lform = solver.FLform_b() - AA = A_Bform.assembly() - # 正向求解 - for i in range(nt-1): - t1 = timeline.next_time_level() - tnextindex = timeline.current_time_level_index()+1 - - y2 = yspace.function() - px = pspace.function() - solver.FLformb_update(y0, y1, allu[tnextindex], t1) - b = b_lform.assembly() - BC = DirichletBC(space=(yspace,pspace), threshold=(None, None), gd=(partial(pde.y_solution,time=t1), partial(pde.p_solution,time = t1)),method='interp') - A, b = BC.apply(AA, b) - x = spsolve(A, b, solver='scipy') - y0[:] = y1 - y1[:] = x[:ygodf] - px[:] = x[-pgdof:] - ally[tnextindex] = y1 - allp[tnextindex] = px - timeline.advance() - - - zn0 = yspace.function(yspace.interpolate(partial(pde.z_solution, time=T))) - zn0t = yspace.interpolate(partial(pde.z_t_solution, time=0)) - zn1 = yspace.function() - zn2 = yspace.function() - qx = pspace.function() - un0 = yspace.function() - un1 = yspace.function() - - un0[:] = solver.solve_u(zn0) #积分子 - allu[tnextindex] = un0 - An = solver.FBForm_A0().assembly() - bn = solver.BLform_b0(ally[-1], allp[-1]).assembly() - BC = DirichletBC(space=(yspace,pspace), threshold=(None, None), gd=(partial(pde.y_solution,time=T), partial(pde.p_solution,time = T)),method='interp') - An, bn = BC.apply(An, bn) - xn = solver.mumps_solve(An.tocoo(), bn) - - zn1[:] = xn[:ygodf] - qx[:] = xn[-pgdof:] - timeline.backward() - - tnextindex = timeline.current_time_level_index() - un1[:] = solver.solve_u(zn1) - allu[tnextindex] = un1 - - A_Bform= solver.FBform_A() - b_lform = solver.BLform_b() - A = A_Bform.assembly() - - # 反向求解 - for i in range(nt-1): - t1 = timeline.prev_time_level() - tnextindex = timeline.current_time_level_index()-1 - u = yspace.function() - solver.BLformb_update(zn0, zn1, ally[tnextindex], allp[tnextindex], t1) - b = b_lform.assembly() - BC = DirichletBC(space=(yspace,pspace), threshold=(None, None), gd=(partial(pde.y_solution,time=t1), partial(pde.p_solution,time = t1)),method='interp') - A, b = BC.apply(A, b) - x = solver.mumps_solve(A.tocoo(), b) - - zn0[:] = zn1 - zn1[:] = x[:ygodf] - qx[:] = x[-pgdof:] - u[:] = solver.solve_u(zn1) - allu[tnextindex] = u - timeline.backward() - - -ysolution = yspace.function(yspace.interpolate(partial(pde.y_solution, time=T))) -usolution = yspace.function(yspace.interpolate(partial(pde.u_solution, time=0))) -errory = mesh.error(ally[-1], ysolution) -erroru = mesh.error(allu[0], usolution) -print(errory) -print(erroru) diff --git a/fealpy/fdm/ocp_yuanshi.py b/fealpy/fdm/ocp_yuanshi.py deleted file mode 100644 index 8de689e90..000000000 --- a/fealpy/fdm/ocp_yuanshi.py +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/python3 -'''! - @Author: wpx - @File Name: ocp-opt.py - @Mail: wpx15673207315@gmail.com - @Created Time: Thu 05 Sep 2024 04:50:58 PM CST - @bref - @ref -''' -from fealpy.mesh import TriangleMesh -from fealpy.backend import backend_manager as bm -from fealpy.functionspace import LagrangeFESpace -from fealpy.old.timeintegratoralg import UniformTimeLine -from fealpy.functionspace import TensorFunctionSpace -from ocp_opt_pde import example_1 -from solver import ocp_opt_solver - -from scipy.sparse import coo_array, bmat -from functools import partial -from fealpy import logger -logger.setLevel('ERROR') #积分子问题 - -bm.set_backend("numpy") -pde = example_1() -n = 10 -q = 4 -T = 1 -nt = 30 -maxit = 3 - -mesh = TriangleMesh.from_box(pde.domain(), nx=n, ny=n) -timeline = UniformTimeLine(0, T, nt) -dt = timeline.dt - -yspace= LagrangeFESpace(mesh, p=1) -space = LagrangeFESpace(mesh, p=1) -pspace = TensorFunctionSpace(space, (2,-1)) -solver = ocp_opt_solver(mesh, yspace, pspace, pde, timeline) - -ygodf = yspace.number_of_global_dofs() -pgdof = pspace.number_of_global_dofs() -yisbdof = yspace.is_boundary_dof() -pisbdof = pspace.is_boundary_dof() -isbdof = bm.concatenate([yisbdof, pisbdof], axis=0)#(362,) - - -ally = [None]*(nt+1) -allp = [None]*(nt+1) -allu = [None]*(nt+1) - - -y0 = yspace.function(yspace.interpolate(partial(pde.y_solution, time=0))) -y0t = yspace.interpolate(partial(pde.y_t_solution, time=0)) -p0 = pspace.function(pspace.interpolate(partial(pde.p_solution, time=0))) #(242,) p0x1,p0x2 -ally[0] = y0 -allp[0] = p0 - - -for k in range(maxit): - A0 = solver.A0n() - b0 = solver.forward_b0(allu[1]) - A0, b0 = solver.forward_boundary(A0, b0, isbdof, dt) - x0 = solver.mumps_solve(A0, b0) - - y1 = yspace.function() - p1 = pspace.function() - - y1[:] = x0[:ygodf] - p1[:] = x0[-pgdof:] #p1x1, p1x2 - ally[1] = y1 - allp[1] = p1 - timeline.advance() - - AA = solver.A() - #正向求解 - for i in range(nt-1): - t1 = timeline.next_time_level() - tnextindex = timeline.current_time_level_index()+1 # 为什么要加1 - - y2 = yspace.function() - px = pspace.function() - b = solver.forward_b(y0, y1, allu[tnextindex], t1) - A,b = solver.forward_boundary(AA, b, isbdof, t1) - - x = solver.mumps_solve(A, b) - y0[:] = y1 - y1[:] = x[:ygodf] - px[:] = x[-pgdof:] - ally[tnextindex] = y1 - allp[tnextindex] = px - timeline.advance() - - - zn0 = yspace.function(yspace.interpolate(partial(pde.z_solution, time=T))) - zn0t = yspace.interpolate(partial(pde.z_t_solution, time=0)) - zn1 = yspace.function() - zn2 = yspace.function() - qx = pspace.function() - un0 = yspace.function() - un1 = yspace.function() - - un0[:] = solver.solve_u(zn0) #积分子 - allu[tnextindex] = un0 - - An = solver.A0n() - # TODO: 未完成 矩阵拼接可以用blockform - bn = solver.backward_b0(ally[-1], allp[-1]) - An, bn = solver.backward_boundary(An, bn, isbdof, T-dt) - xn = solver.mumps_solve(An, bn) - - zn1[:] = xn[:ygodf] - qx[:] = xn[-pgdof:] - timeline.backward() - - tnextindex = timeline.current_time_level_index() - un1[:] = solver.solve_u(zn1) - allu[tnextindex] = un1 - - # 反向求解 - for i in range(nt-1): - t1 = timeline.prev_time_level() - tnextindex = timeline.current_time_level_index()-1 - u = yspace.function() - b = solver.backward_b(zn0, zn1, ally[tnextindex], allp[tnextindex], t1) - A,b = solver.forward_boundary(AA, b, isbdof, t1) - - x = solver.mumps_solve(A, b) - - zn0[:] = zn1 - zn1[:] = x[:ygodf] - qx[:] = x[-pgdof:] - u[:] = solver.solve_u(zn1) - allu[tnextindex] = u - timeline.backward() - - -ysolution = yspace.function(yspace.interpolate(partial(pde.y_solution, time=T))) -usolution = yspace.function(yspace.interpolate(partial(pde.u_solution, time=0))) -errory = mesh.error(ally[-1], ysolution) -erroru = mesh.error(allu[0], usolution) -print(errory) -print(erroru) diff --git a/fealpy/fdm/solver.py b/fealpy/fdm/solver.py deleted file mode 100644 index e8879870e..000000000 --- a/fealpy/fdm/solver.py +++ /dev/null @@ -1,289 +0,0 @@ -from fealpy.fem import BilinearForm, ScalarMassIntegrator -from fealpy.fem import PressWorkIntegrator -from fealpy.fem import LinearForm, ScalarSourceIntegrator -from fealpy.decorator import barycentric, cartesian -from fealpy.backend import backend_manager as bm -from fealpy.sparse import COOTensor -from functools import partial - -from ocp_opt_pde import example_1 - -class ocp_opt_solver(): - def __init__(self, mesh, yspace, pspace, pde, timeline,q=4): - self.mesh = mesh - self.yspace = yspace - self.pspace = pspace - self.pde = pde - self.q = q - self.dt = timeline.dt - self.timeline = timeline - - @cartesian - def coef_M00(p, index = None): - return pde.A_inverse(p)[...,0,0] - bform = BilinearForm(yspace) - bform.add_integrator(ScalarMassIntegrator(coef=coef_M00, q=q)) - self.M00 = bform.assembly() - - @cartesian - def coef_M01(p, index = None): - return pde.A_inverse(p)[...,0,1] - bform = BilinearForm(yspace) - bform.add_integrator(ScalarMassIntegrator(coef=coef_M01, q=q)) - self.M01 = bform.assembly() - - @cartesian - def coef_M10(p, index = None): - return pde.A_inverse(p)[...,1,0] - bform = BilinearForm(yspace) - bform.add_integrator(ScalarMassIntegrator(coef=coef_M10, q=q)) - self.M10 = bform.assembly() - - @cartesian - def coef_M11(p, index = None): - return pde.A_inverse(p)[...,1,1] - bform = BilinearForm(yspace) - bform.add_integrator(ScalarMassIntegrator(coef=coef_M11, q=q)) - self.M11 = bform.assembly() - - - bform = BilinearForm(yspace) - bform.add_integrator(ScalarMassIntegrator(q=q)) - self.M = bform.assembly() - - bform = BilinearForm((yspace, pspace)) - bform.add_integrator(PressWorkIntegrator(-1,q=q)) - self.S1 = bform.assembly() - - bform = BilinearForm((yspace,pspace)) - bform.add_integrator(PressWorkIntegrator(-1,q=q)) - self.S2 = bform.assembly() - - bform = BilinearForm((yspace, pspace)) - bform.add_integrator(PressWorkIntegrator(-1,q=q)) - self.S = bform.assembly() - - - def A0n(self): - dt = self.dt - M = self.M.tocoo() - S = self.S.tocoo() - M00 = self.M00.tocoo() - M01 = self.M01.tocoo() - M10 = self.M10.tocoo() - M11 = self.M11.tocoo() - M0 = COOTensor.concat((M00, M01), axis=1) - M1 = COOTensor.concat((M10, M11), axis=1) - M_A = COOTensor.concat((M0, M1), axis=0) - A0 = COOTensor.concat(((2/dt**2 + self.pde.c)*M, -dt**2*S), axis=0) - A1 = COOTensor.concat((S.T, M_A), axis=0) - A = COOTensor.concat((A0,A1), axis=1) - return A - def forward_b0(self, u1): - if u1 == None: - u1 = self.yspace.function() - - @cartesian - def fun(p, index=None): - result = self.pde.y_solution(p, 0) - result += self.dt * self.pde.y_t_solution(p, 0) - result *= 2/self.dt**2 - return result - @cartesian - def coef(p, index=None): - result = self.pde.f_fun(p, time=self.dt, index=index) - return result - lform = LinearForm(self.yspace) - lform.add_integrator(ScalarSourceIntegrator(coef,q=self.q)) - lform.add_integrator(ScalarSourceIntegrator(u1, q=self.q)) - lform.add_integrator(ScalarSourceIntegrator(fun, q=self.q)) - F0 = lform.assembly() - F1 = bm.zeros(self.pspace.number_of_global_dofs()) - b = bm.concatenate([F0,F1]) - return b - - def backward_b0(self, yn1, pn): - T =self.timeline.T1 - - @barycentric - def fun(bcs, index=None): - result = yn1(bcs) - return result - - @cartesian - def coef(p, index=None): - result = self.pde.z_solution(p, T) - result -= self.pde.z_t_solution(p, T)*self.dt - result *= 2/self.dt**2 - result += -self.pde.y_d_fun(p, time=T-self.dt) - return result - - lform = LinearForm(self.yspace) - lform.add_integrator(ScalarSourceIntegrator(coef,q=self.q)) - lform.add_integrator(ScalarSourceIntegrator(fun, q=self.q)) - F0 = lform.assembly() - - @barycentric - def funp(bcs, index=None): - result = -pn(bcs) - return result - - @cartesian - def coefp(p, index=None): - result = self.pde.p_d_fun(p, time=T-self.dt) - return result - lform = LinearForm(self.pspace) - lform.add_integrator(ScalarSourceIntegrator(coefp,q=self.q)) - lform.add_integrator(ScalarSourceIntegrator(funp, q=self.q)) - F1= lform.assembly() - - b = bm.concatenate([F0,F1]) - return b - - - - def forward_boundary(self, A, b, isbdof, t): - x0 = bm.zeros(self.yspace.number_of_global_dofs()) - yh, yisbdof = self.yspace.boundary_interpolate( - partial(self.pde.y_solution, time=t), x0) - x1 = bm.zeros(self.pspace.number_of_global_dofs()) - px1, pisbdof = self.pspace.boundary_interpolate( - partial(self.pde.p_solution, time=t), x1) - # x2 = bm.zeros(self.yspace.number_of_global_dofs()) - # px2, pisbdof = self.yspace.boundary_interpolate( - # partial(self.pde.px2_solution, time=t), x2) - xx = bm.concatenate([x0,x1]) - b -= A@xx - b[isbdof] = xx[isbdof] - - kwargs = A.values_context() - indices = A.indices() - new_values = bm.copy(A.values()) - IDX = isbdof[indices[0, :]] | isbdof[indices[1, :]] - new_values[IDX] = 0 - A = COOTensor(indices, new_values, A.sparse_shape) - - index = bm.nonzero(isbdof)[0] - shape = new_values.shape[:-1] + (len(index), ) - one_values = bm.ones(shape, **kwargs) - one_indices = bm.stack([index, index], axis=0) - A1 = COOTensor(one_indices, one_values, A.sparse_shape) - A = A.add(A1).coalesce() - - return A,b - - def backward_boundary(self, A, b, isbdof, t): - x0 = bm.zeros(self.yspace.number_of_global_dofs()) - zh, zisbdof = self.yspace.boundary_interpolate( - partial(self.pde.z_solution, time=t), x0) - x1 = bm.zeros(self.pspace.number_of_global_dofs()) - qx1, qisbdof = self.pspace.boundary_interpolate( - partial(self.pde.qx_solution, time=t),x1) - xx = bm.concatenate([x0,x1]) - - b -= A@xx - b[isbdof] = xx[isbdof] - - kwargs = A.values_context() - indices = A.indices() - new_values = bm.copy(A.values()) - IDX = isbdof[indices[0, :]] | isbdof[indices[1, :]] - new_values[IDX] = 0 - A = COOTensor(indices, new_values, A.sparse_shape) - - index = bm.nonzero(isbdof)[0] - shape = new_values.shape[:-1] + (len(index), ) - one_values = bm.ones(shape, **kwargs) - one_indices = bm.stack([index, index], axis=0) - A1 = COOTensor(one_indices, one_values, A.sparse_shape) - A = A.add(A1).coalesce() - return A,b - - def A(self): - dt = self.dt - S = self.S.tocoo() - M = self.M.tocoo() - M01 = self.M01.tocoo() - M10 = self.M10.tocoo() - M11 = self.M11.tocoo() - M00 = self.M00.tocoo() - M0 = COOTensor.concat((M00, M01), axis=1) - M1 = COOTensor.concat((M10, M11), axis=1) - M_A = COOTensor.concat((M0, M1), axis=0) - A0 = COOTensor.concat(((1/dt**2 + self.pde.c)*M, -dt**2*S), axis=0) - A1 = COOTensor.concat((S.T,M_A ), axis=0) - A = COOTensor.concat((A0,A1), axis=1) - return A - - def forward_b(self, y0, y1, u, t): - if u==None: - u = self.yspace.function() - - @barycentric - def fun(bcs, index=None): - result = 2*y1(bcs) - y0(bcs) - result *= 1/self.dt**2 - return result - @cartesian - def coef(p, index=None): - result = self.pde.f_fun(p, time=t, index=index) - return result - - lform = LinearForm(self.yspace) - lform.add_integrator(ScalarSourceIntegrator(coef,q=self.q)) - lform.add_integrator(ScalarSourceIntegrator(u, q=self.q)) - lform.add_integrator(ScalarSourceIntegrator(fun, q=self.q)) - F0 = lform.assembly() - F1 = bm.zeros(self.pspace.number_of_global_dofs()) - b = bm.concatenate([F0,F1]) - return b - - def backward_b(self, zn0, zn1, yn1, pn, t): - - @barycentric - def fun(bcs, index=None): - result = 2*zn0(bcs) - zn1(bcs) - result *= 1/self.dt**2 - result += yn1(bcs) - return result - - @cartesian - def coef(p, index=None): - result = -self.pde.y_d_fun(p, time=t) - return result - - lform = LinearForm(self.yspace) - lform.add_integrator(ScalarSourceIntegrator(coef,q=self.q)) - lform.add_integrator(ScalarSourceIntegrator(fun, q=self.q)) - F0 = lform.assembly() - - @barycentric - def funpx1(bcs, index=None): - result = -pn(bcs) - return result - - @cartesian - def coefpx1(p, index=None): - result = self.pde.p_d_fun(p, time=t) - return result - lform = LinearForm(self.pspace) - lform.add_integrator(ScalarSourceIntegrator(coefpx1,q=self.q)) - lform.add_integrator(ScalarSourceIntegrator(funpx1, q=self.q)) - F1= lform.assembly() - - b = bm.concatenate([F0,F1]) - return b - - def solve_u(self, z): - result = bm.max(self.mesh.integral(z), 0) - z #积分子 - return result - - def mumps_solve(self, A, b): - import scipy.sparse as sp - values = A.values() - indices = A.indices() - A = sp.coo_matrix((values, (indices[0], indices[1])), shape=A.shape) - A = A.tocsr() - x = sp.linalg.spsolve(A,b) - return x - diff --git a/fealpy/fdm/solver_update.py b/fealpy/fdm/solver_update.py deleted file mode 100644 index f19493ed8..000000000 --- a/fealpy/fdm/solver_update.py +++ /dev/null @@ -1,309 +0,0 @@ -from fealpy.fem import BilinearForm, ScalarMassIntegrator -from fealpy.fem import PressWorkIntegrator -from fealpy.fem import LinearForm, ScalarSourceIntegrator -from fealpy.fem import BilinearForm, LinearForm, BlockForm,LinearBlockForm -from fealpy.decorator import barycentric, cartesian -from fealpy.backend import backend_manager as bm -from fealpy.sparse import COOTensor -from functools import partial -from tensor_mass_integrator import TensorMassIntegrator -from ocp_opt_pde import example_1 - -class ocp_opt_solver(): - def __init__(self, mesh, yspace, pspace, pde, timeline,q=4): - self.mesh = mesh - self.yspace = yspace - self.pspace = pspace - self.pde = pde - self.q = q - self.dt = timeline.dt - self.timeline = timeline - - def FBForm_A0(self): - pspace = self.pspace - yspace = self.yspace - dt = self.dt - pde = self.pde - q = self.q - c = pde.c - """ - @cartesian - def coef_M00(p, index = None): - return pde.A_inverse(p)[...,0,0] - M00 = BilinearForm(yspace) - M00.add_integrator(ScalarMassIntegrator(coef=coef_M00, q=q)) - - @cartesian - def coef_M01(p, index = None): - return pde.A_inverse(p)[...,0,1] - M01 = BilinearForm(yspace) - M01.add_integrator(ScalarMassIntegrator(coef=coef_M01, q=q)) - - @cartesian - def coef_M10(p, index = None): - return pde.A_inverse(p)[...,1,0] - M10 = BilinearForm(yspace) - M10.add_integrator(ScalarMassIntegrator(coef=coef_M10, q=q)) - - @cartesian - def coef_M11(p, index = None): - return pde.A_inverse(p)[...,1,1] - M11 = BilinearForm(yspace) - M11.add_integrator(ScalarMassIntegrator(coef=coef_M11, q=q)) - M_A_inv = BlockForm([[M00, M01], [M10, M11]]) - """ - M_inv = BilinearForm(yspace) - M_inv.add_integrator(TensorMassIntegrator(coef=pde.A_inverse,q=q)) - - - - M = BilinearForm(yspace) - M.add_integrator(ScalarMassIntegrator(coef=2+c*dt**2,q=q)) - - S = BilinearForm((yspace, pspace)) - S.add_integrator(PressWorkIntegrator(coef=-dt**2,q=q)) - - S1 = BilinearForm((yspace, pspace)) - S1.add_integrator(PressWorkIntegrator(1,q=q)) - - A0 = BlockForm([[M, S.T], [S1, M_inv]]) - - return A0 - - def FBform_A(self): - yspace = self.yspace - pde = self.pde - q = self.q - pspace = self.pspace - dt = self.dt - c = pde.c - @cartesian - def coef_M00(p, index = None): - return pde.A_inverse(p)[...,0,0] - M00 = BilinearForm(yspace) - M00.add_integrator(ScalarMassIntegrator(coef=coef_M00, q=q)) - - @cartesian - def coef_M01(p, index = None): - return pde.A_inverse(p)[...,0,1] - M01 = BilinearForm(yspace) - - M01.add_integrator(ScalarMassIntegrator(coef=coef_M01, q=q)) - - @cartesian - def coef_M10(p, index = None): - return pde.A_inverse(p)[...,1,0] - M10 = BilinearForm(yspace) - M10.add_integrator(ScalarMassIntegrator(coef=coef_M10, q=q)) - - @cartesian - def coef_M11(p, index = None): - return pde.A_inverse(p)[...,1,1] - M11 = BilinearForm(yspace) - M11.add_integrator(ScalarMassIntegrator(coef=coef_M11, q=q)) - - M_A_inv = BlockForm([[M00, M01], [M10, M11]]) - - M = BilinearForm(yspace) - M.add_integrator(ScalarMassIntegrator(coef=2+c*dt**2,q=q)) - - S = BilinearForm((yspace, pspace)) - S.add_integrator(PressWorkIntegrator(coef=-dt**2,q=q)) - - S1 = BilinearForm((yspace, pspace)) - S1.add_integrator(PressWorkIntegrator(1,q=q)) - - A = BlockForm([[M, S.T], [S1, M_A_inv]]) - - return A - - def FLform_b0(self,u1): - yspace = self.yspace - pspace = self.pspace - pde = self.pde - q = self.q - - if u1 == None: - u1 = self.yspace.function() - - @cartesian - def fun(p, index=None): - result = self.pde.y_solution(p, 0) - result += self.dt * self.pde.y_t_solution(p, 0) - result *= 2/self.dt**2 - return result - @cartesian - def coef(p, index=None): - result = self.pde.f_fun(p, time=self.dt, index=index) - return result - - L0 = LinearForm(yspace) - f_coef =ScalarSourceIntegrator(coef,q=q) - u_coef = ScalarSourceIntegrator(u1,q=q) - fun_coef = ScalarSourceIntegrator(fun,q=q) - L0.add_integrator(f_coef) - L0.add_integrator(u_coef) - L0.add_integrator(fun_coef) - - L1 = LinearForm(pspace) - - L = LinearBlockForm([L0, L1]) - - return L - - def FLform_b(self): - yspace = self.yspace - pspace = self.pspace - pde = self.pde - q = self.q - - L0 = LinearForm(yspace) - self.f_coef =ScalarSourceIntegrator(q=q) - self.u_coef = ScalarSourceIntegrator(q=q) - self.fun_coef = ScalarSourceIntegrator(q=q) - L0.add_integrator(self.f_coef) - L0.add_integrator(self.u_coef) - L0.add_integrator(self.fun_coef) - - L1 = LinearForm(pspace) - - L = LinearBlockForm([L0, L1]) - return L - - def FLformb_update(self, y0, y1, u, t): - if u==None: - u = self.yspace.function() - self.u_coef.source = u - self.u_coef.clear() - - @barycentric - def fun(bcs, index=None): - result = 2*y1(bcs) - y0(bcs) - result *= 1/self.dt**2 - return result - self.fun_coef.source = fun - self.fun_coef.clear() - - @cartesian - def coef(p, index=None): - result = self.pde.f_fun(p, time=t, index=index) - return result - self.f_coef.source = coef - self.f_coef.clear() - - def BLform_b0(self, yn1, pn): - yspace = self.yspace - pspace = self.pspace - pde = self.pde - q = self.q - T =self.timeline.T1 - - @barycentric - def fun(bcs, index=None): - result = yn1(bcs) - return result - - @cartesian - def coef(p, index=None): - result = self.pde.z_solution(p, T) - result -= self.pde.z_t_solution(p, T)*self.dt - result *= 2/self.dt**2 - result += -self.pde.y_d_fun(p, time=T-self.dt) - return result - - @barycentric - def funp(bcs, index=None): - result = -pn(bcs) - return result - - @cartesian - def coefp(p, index=None): - result = self.pde.p_d_fun(p, time=T-self.dt) - return result - - L0 = LinearForm(yspace) - f_coef =ScalarSourceIntegrator(q=q) - f_coef.source = coef - fun_coef = ScalarSourceIntegrator(q=q) - fun_coef.source = fun - L0.add_integrator(f_coef) - L0.add_integrator(fun_coef) - - L1 = LinearForm(pspace) - coefp_coef = ScalarSourceIntegrator(q=q) - coefp_coef.source = coefp - funp_coef = ScalarSourceIntegrator(q=q) - funp_coef.source = funp - L1.add_integrator(coefp_coef) - L1.add_integrator(funp_coef) - - L = LinearBlockForm([L0, L1]) - return L - - def BLform_b(self): - yspace = self.yspace - pspace = self.pspace - pde = self.pde - q = self.q - T =self.timeline.T1 - - L0 = LinearForm(yspace) - self.yd_coef =ScalarSourceIntegrator(q=q) - self.fun_coef = ScalarSourceIntegrator(q=q) - L0.add_integrator(self.yd_coef) - L0.add_integrator(self.fun_coef) - - L1 = LinearForm(pspace) - self.funpx1_coef = ScalarSourceIntegrator(q=q) - self.coefpx1_coef = ScalarSourceIntegrator(q=q) - L1.add_integrator(self.coefpx1_coef) - L1.add_integrator(self.funpx1_coef) - - L = LinearBlockForm([L0, L1]) - return L - - def BLformb_update(self, zn0, zn1, yn1, pn, t): - @barycentric - def fun(bcs, index=None): - result = 2*zn0(bcs) - zn1(bcs) - result *= 1/self.dt**2 - result += yn1(bcs) - return result - self.fun_coef.source = fun - self.fun_coef.clear() - @cartesian - def coef(p, index=None): - result = -self.pde.y_d_fun(p, time=t) - return result - self.yd_coef.source = coef - self.yd_coef.clear() - - @barycentric - def funpx1(bcs, index=None): - result = -pn(bcs) - return result - self.funpx1_coef.source = funpx1 - self.funpx1_coef.clear() - - - @cartesian - def coefpx1(p, index=None): - result = self.pde.p_d_fun(p, time=t) - return result - self.coefpx1_coef.source = coefpx1 - self.coefpx1_coef.clear() - - - def solve_u(self, z): - result = bm.max(self.mesh.integral(z), 0) - z #积分子 - return result - - def mumps_solve(self, A, b): - import scipy.sparse as sp - values = A.values() - indices = A.indices() - A = sp.coo_matrix((values, (indices[0], indices[1])), shape=A.shape) - A = A.tocsr() - x = sp.linalg.spsolve(A,b) - return x - diff --git a/fealpy/fdm/tensor_mass_integrator.py b/fealpy/fdm/tensor_mass_integrator.py deleted file mode 100644 index b9d30258a..000000000 --- a/fealpy/fdm/tensor_mass_integrator.py +++ /dev/null @@ -1,74 +0,0 @@ -from typing import Optional - -from fealpy.backend import backend_manager as bm -from fealpy.typing import TensorLike, Index, _S - -from fealpy.mesh import HomogeneousMesh -from fealpy.functionspace.space import FunctionSpace as _FS -from fealpy.utils import process_coef_func -from fealpy.functional import linear_integral, get_semilinear_coef - -from fealpy.fem.integrator import ( - LinearInt, OpInt, CellInt, - enable_cache, - assemblymethod, - CoefLike -) -from bilinear import bilinear_integral - -class TensorMassIntegrator(LinearInt, OpInt, CellInt): - def __init__(self, coef: Optional[CoefLike]=None, q: Optional[int]=None, *, - index: Index=_S, - batched: bool=False, - method: Optional[str]=None) -> None: - method = 'assembly' if (method is None) else method - super().__init__(method=method) - self.coef = coef - self.q = q - self.index = index - self.batched = batched - - @enable_cache - def to_global_dof(self, space: _FS) -> TensorLike: - return space.cell_to_dof()[self.index] - - @enable_cache - def fetch(self, space: _FS): - q = self.q - index = self.index - mesh = getattr(space, 'mesh', None) - - if not isinstance(mesh, HomogeneousMesh): - raise RuntimeError("The ScalarMassIntegrator only support spaces on" - f"homogeneous meshes, but {type(mesh).__name__} is" - "not a subclass of HomoMesh.") - - cm = mesh.entity_measure('cell', index=index) - q = space.p+3 if self.q is None else self.q - qf = mesh.quadrature_formula(q, 'cell') - bcs, ws = qf.get_quadrature_points_and_weights() - phi = space.basis(bcs, index=index) - return bcs, ws, phi, cm, index - - def assembly(self, space: _FS) -> TensorLike: - coef = self.coef - mesh = getattr(space, 'mesh', None) - bcs, ws, phi, cm, index = self.fetch(space) - val = process_coef_func(coef, bcs=bcs, mesh=mesh, etype='cell', index=index) - - return bilinear_integral(phi, phi, ws, cm, val, batched=self.batched) - - @assemblymethod('semilinear') - def semilinear_assembly(self, space: _FS) -> TensorLike: - uh = self.uh - coef = self.coef - mesh = getattr(space, 'mesh', None) - bcs, ws, phi, cm, index = self.fetch(space) - val_A = coef.grad_func(uh(bcs)) #(C, Q) - val_F = -coef.func(uh(bcs)) #(C, Q) - coef = process_coef_func(coef, bcs=bcs, mesh=mesh, etype='cell', index=index) - coef_A = get_semilinear_coef(val_A, coef) - coef_F = get_semilinear_coef(val_F, coef) - - return bilinear_integral(phi, phi, ws, cm, coef_A, batched=self.batched), \ - linear_integral(phi, ws, cm, coef_F, batched=self.batched) diff --git a/fealpy/fem/press_work_integrator.py b/fealpy/fem/press_work_integrator.py index 98ffb0fbb..ddb07f78d 100644 --- a/fealpy/fem/press_work_integrator.py +++ b/fealpy/fem/press_work_integrator.py @@ -16,7 +16,7 @@ from .integrator import LinearInt, OpInt, CellInt, CoefLike, enable_cache from ..typing import TensorLike, Index, _S from ..backend import backend_manager as bm - +from ..functionspace import TensorFunctionSpace ''' (pI, \nabla v) @@ -39,7 +39,6 @@ def to_global_dof(self, space: _FS) -> TensorLike: def fetch(self, space: _FS): space0 = space[0] space1 = space[1] - scalar_space = space[1].scalar_space index = self.index mesh = getattr(space[0], 'mesh', None) @@ -64,7 +63,10 @@ def assembly(self, space: _FS) -> TensorLike: mesh = getattr(space[0], 'mesh', None) phi, gphi, cm, bcs, ws, index = self.fetch(space) val = process_coef_func(coef, bcs=bcs, mesh=mesh, etype='cell', index=index) - gphi = bm.einsum('...ii->...', gphi) + if isinstance(space[0], TensorFunctionSpace): + gphi = gphi + else: + gphi = bm.einsum('...ii->...', gphi) result = bilinear_integral(gphi, phi, ws, cm, val, batched=self.batched) return result From 3e10db375f67e84d7866e60c3f4fee316f7abe06 Mon Sep 17 00:00:00 2001 From: july <1985547582@qq.com> Date: Tue, 19 Nov 2024 13:11:50 +0800 Subject: [PATCH 07/63] update --- example/fem/poisson_RT_neumann.py | 130 +++++ .../RaviartThomasFiniteElementSpace3d.py | 506 ++++++++++-------- fealpy/functionspace/__init__.py | 1 + .../RaviartThomasFiniteElementSpace3d_data.py | 30 ++ .../test_RaviartThomasFiniteElementSpace3d.py | 17 +- 5 files changed, 469 insertions(+), 215 deletions(-) create mode 100644 example/fem/poisson_RT_neumann.py diff --git a/example/fem/poisson_RT_neumann.py b/example/fem/poisson_RT_neumann.py new file mode 100644 index 000000000..a9e820907 --- /dev/null +++ b/example/fem/poisson_RT_neumann.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 + +import argparse +import matplotlib.pyplot as plt + +from fealpy.mesh import TriangleMesh, TetrahedronMesh +from fealpy.functionspace import RTFiniteElementSpace2d,RTFiniteElementSpace3d +from fealpy.functionspace import LagrangeFESpace +from fealpy import logger +logger.setLevel('WARNING') + +from fealpy.backend import backend_manager as bm +from fealpy.fem import BilinearForm,ScalarMassIntegrator +from fealpy.fem import LinearForm, ScalarSourceIntegrator,BoundaryFaceSourceIntegrator +from fealpy.fem import DivIntegrator +from fealpy.fem import BlockForm,LinearBlockForm + +# from fealpy.decorator import cartesian, barycentric +from fealpy.tools.show import showmultirate, show_error_table + +# solver +from fealpy.solver import spsolve + +from fealpy.pde.poisson_2d import CosCosData as PDE2d +from fealpy.pde.poisson_3d import CosCosCosData as PDE3d +from fealpy.utils import timer + +# 参数解析 +parser = argparse.ArgumentParser(description= + """ + 任意次有限元方法求解possion方程 + """) + +parser.add_argument('--degree', + default=1, type=int, + help='Lagrange 有限元空间的次数, 默认为 1 次.') + +parser.add_argument('--backend', + default='numpy', type=str, + help='默认后端为numpy') + +parser.add_argument('--dim', + default=2, type=int, + help='默认维数为2') + +parser.add_argument('--maxit', + default=4, type=int, + help='默认最大迭代次数为4') + +args = parser.parse_args() +p = args.degree +maxit = args.maxit +dim = args.dim +backend = args.backend +bm.set_backend(backend) + +if dim == 2: + pde = PDE2d() +else: + pde = PDE3d() + + +errorType = ['$|| p - p_h||_0$ with k=2', + '$|| p - p_h||_0$ with k=3', + '$|| p - p_h||_0$ with k=4', + '$|| u - u_h||_0$ with k=2', + '$|| u - u_h||_0$ with k=3', + '$|| u - u_h||_0$ with k=4'] +errorMatrix = bm.zeros((len(errorType), maxit), dtype=bm.float64) +NDof = bm.zeros(maxit, dtype=bm.float64) + +tmr = timer() +next(tmr) + +ps = [2, 3, 4] +for j, p in enumerate(ps): + for i in range(maxit): + print("The {}-th computation:".format(i)) + + if dim == 2: + mesh = TriangleMesh.from_box([0, 1, 0, 1], nx=2**i, ny=2**i) + space1 = LagrangeFESpace(mesh,p=p,ctype="D") + space2 = RTFiniteElementSpace2d(mesh, p=p) + else: + mesh = TetrahedronMesh.from_box([0, 1, 0, 1, 0, 1], nx=2**i, ny=2**i, nz=2**i) + space1 = LagrangeFESpace(mesh,p=p,ctype="D") + space2 = RTFiniteElementSpace3d(mesh, p=p) + tmr.send(f'第{i}次网格和pde生成时间') + + pdof = space1.dof.number_of_global_dofs() + udof = space2.dof.number_of_global_dofs() + NDof[i] = 1/2**i + + uh = space2.function() + ph = space1.function() + + bform1 = BilinearForm(space2) + bform1.add_integrator(ScalarMassIntegrator(coef=1, q=p+3)) + + bform2 = BilinearForm((space1,space2)) + bform2.add_integrator(DivIntegrator(coef=-1, q=p+3)) + + M = BlockForm([[bform1,bform2], + [bform2.T,None]]) + M = M.assembly() + tmr.send(f'第{i}次矩组装时间') + # 组装右端 + + lform = LinearForm(space1) + lform.add_integrator(ScalarSourceIntegrator(pde.source)) + F = lform.assembly() + G = space2.set_neumann_bc(pde.solution) + b = bm.concatenate([-G,-F],axis=0) + tmr.send(f'第{i}次向量组装时间') + + val = spsolve(M, b,"scipy") + + uh[:] = val[:udof] + ph[:] = val[udof:] + tmr.send(f'第{i}次求解时间') + + #计算误差 + errorMatrix[j, i] = mesh.error(pde.solution, ph) + errorMatrix[j+3, i] = mesh.error(pde.flux, uh.value) + print("error = ", errorMatrix) + +next(tmr) +showmultirate(plt, 2, NDof, errorMatrix, errorType, propsize=20) +show_error_table(NDof, errorType, errorMatrix) +plt.show() \ No newline at end of file diff --git a/fealpy/functionspace/RaviartThomasFiniteElementSpace3d.py b/fealpy/functionspace/RaviartThomasFiniteElementSpace3d.py index d2147e665..25554ce4a 100644 --- a/fealpy/functionspace/RaviartThomasFiniteElementSpace3d.py +++ b/fealpy/functionspace/RaviartThomasFiniteElementSpace3d.py @@ -111,7 +111,7 @@ def is_boundary_dof(self): return flag -class RTFiniteElementSpace3d(): +class RTFiniteElementSpace3d(FunctionSpace, Generic[_MT]): def __init__(self, mesh, p): self.p = p self.mesh = mesh @@ -127,7 +127,7 @@ def __init__(self, mesh, p): self.device = mesh.device @barycentric - def basis(self, bcs): + def basis(self, bcs,index=_S): p = self.p mesh = self.mesh @@ -152,7 +152,7 @@ def basis(self, bcs): # face basis val = bm.zeros((NC,)+bcs.shape[:-1]+(ldof, 3), device=self.device, dtype=self.ftype) - phi = self.bspace.basis(bcs, p=p) #(NQ, NC, cldof) + phi = self.bspace.basis(bcs, p=p) #(NC, NQ, cldof) multiIndex = self.mesh.multi_index_matrix(p, 3) cm = self.mesh.entity_measure("cell") c2fs = self.mesh.cell_to_face_sign() @@ -160,7 +160,7 @@ def basis(self, bcs): for i in range(4): c2fsi = c2fs[:, i] flag = multiIndex[:, i]==0 - phif = phi[:, :, flag] #(NQ, NC, fldof//2) + phif = phi[:, :, flag] #(NC, NQ, fldof//2) l0 = l[lf[i, 0]] l1 = l[lf[i, 1]] @@ -171,13 +171,15 @@ def basis(self, bcs): v2 = (node[cell[:, lf[i, 2]]] - node[cell[:, i]])/cm[:, None] v = l0*v0[:, None,None,:] + l1*v1[:,None,None,:] + l2*v2[:,None, None,:] #(NQ, NC, fldof, 3) - v[~c2fsi,:, :, :] *= -1 + #v[~c2fsi,:, :, :] *= -1 + v = bm.set_at(v, (~c2fsi, slice(None), slice(None), slice(None)), -v[~c2fsi,:, :, :]) N = fldof*i - val[..., N:N+fldof, :] = v*phif[..., None] + #val[..., N:N+fldof, :] = v*phif[..., None] + val = bm.set_at(val,(..., slice(None), slice(N, N+fldof), slice(None)),v*phif[..., None]) if(p > 0): - phi = self.bspace.basis(bcs, p=p-1) #(NQ, NC, cldof) + phi = self.bspace.basis(bcs, p=p-1) #(NC, NQ, cldof) for i in range(3): l0 = l[lf[i, 0]] l1 = l[lf[i, 1]] @@ -190,218 +192,294 @@ def basis(self, bcs): v = l[i]*(l0*v0[:, None,None,:] + l1*v1[:, None,None,:] + l2*v2[:, None,None,:]) #(NQ, NC, fldof, 3) N = fldof*4+i*cldof//3 - val[..., N:N+cldof//3, :] = v*phi[..., None] - return val + #val[..., N:N+cldof//3, :] = v*phi[..., None] + val = bm.set_at(val,(..., slice(None), slice(N, N+cldof//3), slice(None)),v*phi[..., None]) + return val[index] -# @barycentric -# def div_basis(self, bc): + @barycentric + def div_basis(self, bcs,index=_S): -# p = self.p -# mesh = self.mesh -# NC = mesh.number_of_cells() -# GD = mesh.geo_dimension() -# ldof = self.dof.number_of_local_dofs() -# cldof = self.dof.number_of_local_dofs("cell") -# fldof = self.dof.number_of_local_dofs("face") -# eldof = self.dof.number_of_local_dofs("edge") -# gdof = self.dof.number_of_global_dofs() -# glambda = mesh.grad_lambda() -# ledge = mesh.ds.localEdge + p = self.p + mesh = self.mesh + NC = mesh.number_of_cells() + GD = mesh.geo_dimension() + ldof = self.dof.number_of_local_dofs() + cldof = self.dof.number_of_local_dofs("cell") + fldof = self.dof.number_of_local_dofs("face") + eldof = self.dof.number_of_local_dofs("edge") + gdof = self.dof.number_of_global_dofs() + glambda = mesh.grad_lambda() + ledge = mesh.localEdge -# node = mesh.entity('node') -# cell = mesh.entity('cell') + node = mesh.entity('node') + cell = mesh.entity('cell') -# l = np.zeros((4, )+bc[..., 0, None, None, None].shape, dtype=np.float_) -# l[0] = bc[..., 0, None, None, None] -# l[1] = bc[..., 1, None, None, None] -# l[2] = bc[..., 2, None, None, None] -# l[3] = bc[..., 3, None, None, None] #(NQ, NC, ldof, 2) + l = bm.zeros((4, )+bcs[None, :,0,None, None].shape,device=self.device, dtype=self.ftype) + l[0] = bcs[None, :, 0, None, None] + l[1] = bcs[None, :, 1, None, None] + l[2] = bcs[None, :, 2, None, None] + l[3] = bcs[None, :, 3, None, None] #(NC, NQ, ldof, 1) # l = np.tile(l, (1, NC, 1, 1)) -# phi = self.bspace.basis(bc, p=p) -# gphi = self.bspace.grad_basis(bc, p=p) -# multiIndex = self.mesh.multi_index_matrix(p, 3) -# val = np.zeros(bc.shape[:-1]+(NC, ldof), dtype=np.float_) - -# # face basis -# phi = self.bspace.basis(bc, p=p) #(NQ, NC, cldof) -# gphi = self.bspace.grad_basis(bc, p=p) -# multiIndex = self.mesh.multi_index_matrix(p, 3) -# cm = self.mesh.entity_measure("cell") -# c2fs = self.mesh.ds.cell_to_face_sign() -# lf = self.mesh.ds.localFace -# for i in range(4): -# c2fsi = c2fs[:, i] -# flag = multiIndex[:, i]==0 -# phif = phi[:, :, flag] #(NQ, NC, fldof//2) -# gphif = gphi[:, :, flag] #(NQ, NC, fldof//2) - -# l0 = l[lf[i, 0]] -# l1 = l[lf[i, 1]] -# l2 = l[lf[i, 2]] - -# g0 = glambda[:, lf[i, 0], None] #(NC, 1, 3) -# g1 = glambda[:, lf[i, 1], None] -# g2 = glambda[:, lf[i, 2], None] - -# v0 = (node[cell[:, lf[i, 0]]] - node[cell[:, i]])/cm[:, None] #(NC, 1, 3) -# v1 = (node[cell[:, lf[i, 1]]] - node[cell[:, i]])/cm[:, None] -# v2 = (node[cell[:, lf[i, 2]]] - node[cell[:, i]])/cm[:, None] - -# v = l0*v0[:, None] + l1*v1[:, None] + l2*v2[:, None] #(NQ, NC, fldof, 3) -# v[..., ~c2fsi, :, :] *= -1 -# dv = np.sum(g0*v0[:, None], axis=-1) + np.sum(g1*v1[:, None], axis=-1) + np.sum(g2*v2[:, None], axis=-1) -# dv[..., ~c2fsi, :] *= -1 - -# N = fldof*i -# val[..., N:N+fldof] = dv*phif+np.sum(gphif*v, axis=-1) - -# if(p > 0): -# phi = self.bspace.basis(bc, p=p-1) #(NQ, NC, cldof) -# gphi = self.bspace.grad_basis(bc, p=p-1) #(NQ, NC, cldof) -# for i in range(3): -# l0 = l[lf[i, 0]] -# l1 = l[lf[i, 1]] -# l2 = l[lf[i, 2]] - -# g0 = glambda[:, lf[i, 0], None] -# g1 = glambda[:, lf[i, 1], None] -# g2 = glambda[:, lf[i, 2], None] -# gi = glambda[:, i, None] - -# v0 = (node[cell[:, lf[i, 0]]] - node[cell[:, i]])/cm[:, None] -# v1 = (node[cell[:, lf[i, 1]]] - node[cell[:, i]])/cm[:, None] -# v2 = (node[cell[:, lf[i, 2]]] - node[cell[:, i]])/cm[:, None] - -# w = (l0*v0[:, None] + l1*v1[:, None] + l2*v2[:, None]) #(NQ, NC, fldof, 3) -# dw = np.sum(g0*v0[:, None], axis=-1) + np.sum(g1*v1[:, None], axis=-1) + np.sum(g2*v2[:, None], axis=-1) -# v = l[i]*w #(NQ, NC, fldof, 3) -# dv = np.sum(gi*w, axis=-1) + l[i, ..., 0]*dw - -# N = fldof*4+i*cldof//3 -# val[..., N:N+cldof//3] = dv*phi + np.sum(gphi*v, axis=-1) -# return val - -# def face_basis(self, bc, index=np.s_[:]): -# fn = self.mesh.face_normal(index=index); -# fn /= np.sqrt(np.sum(fn**2, axis=1)[:, None]) - -# fphi = self.bspace.basis(bc, p=self.p) #(NQ, NE, eldof) -# val = fphi[..., None] * fn[:, None] -# return val - -# def cell_to_dof(self): -# return self.dof.cell2dof - -# def number_of_global_dofs(self): -# return self.dof.number_of_global_dofs() - -# def number_of_local_dofs(self, doftype='all'): -# return self.dof.number_of_local_dofs(doftype) - -# @barycentric -# def value(self, uh, bc, index=np.s_[:]): -# '''@ -# @brief 计算一个有限元函数在每个单元的 bc 处的值 -# @param bc : (..., GD+1) -# @return val : (..., NC, GD) -# ''' -# phi = self.basis(bc) -# c2d = self.dof.cell_to_dof() -# # uh[c2d].shape = (NC, ldof); phi.shape = (..., NC, ldof, GD) -# val = np.einsum("cl, ...clk->...ck", uh[c2d], phi) -# return val - -# @barycentric -# def curl_value(self, uh, bc, index=np.s_[:]): -# '''@ -# @brief 计算一个有限元函数在每个单元的 bc 处的值 -# @param bc : (..., GD+1) -# @return val : (..., NC, GD) -# ''' -# cphi = self.curl_basis(bc) -# c2d = self.dof.cell_to_dof() -# # uh[c2d].shape = (NC, ldof); phi.shape = (..., NC, ldof, GD) -# val = np.einsum("cl, ...cli->...ci", uh[c2d], cphi) -# return val + phi = self.bspace.basis(bcs, p=p) + gphi = self.bspace.grad_basis(bcs, p=p) + multiIndex = self.mesh.multi_index_matrix(p, 3) + val = bm.zeros((NC,)+bcs.shape[:-1]+(ldof,), device=self.device, dtype=self.ftype) -# @barycentric -# def div_value(self, uh, bc, index=np.s_[:]): -# pass + # face basis + phi = self.bspace.basis(bcs, p=p) #(NQ, NC, cldof) + gphi = self.bspace.grad_basis(bcs, p=p) + multiIndex = self.mesh.multi_index_matrix(p, 3) + cm = self.mesh.entity_measure("cell") + c2fs = self.mesh.cell_to_face_sign() + lf = self.mesh.localFace + for i in range(4): + c2fsi = c2fs[:, i] + flag = multiIndex[:, i]==0 + phif = phi[:, :, flag] #(NQ, NC, fldof//2) + gphif = gphi[:, :, flag] #(NQ, NC, fldof//2) -# @barycentric -# def grad_value(self, uh, bc, index=np.s_[:]): -# pass + l0 = l[lf[i, 0]] + l1 = l[lf[i, 1]] + l2 = l[lf[i, 2]] -# @barycentric -# def edge_value(self, uh, bc, index=np.s_[:]): -# pass + g0 = glambda[:, lf[i, 0], None] #(NC, 1, 3) + g1 = glambda[:, lf[i, 1], None] + g2 = glambda[:, lf[i, 2], None] -# @barycentric -# def face_value(self, uh, bc, index=np.s_[:]): -# pass + v0 = (node[cell[:, lf[i, 0]]] - node[cell[:, i]])/cm[:, None] #(NC, 1, 3) + v1 = (node[cell[:, lf[i, 1]]] - node[cell[:, i]])/cm[:, None] + v2 = (node[cell[:, lf[i, 2]]] - node[cell[:, i]])/cm[:, None] -# def mass_matrix(self): -# mesh = self.mesh -# NC = mesh.number_of_cells() -# ldof = self.dof.number_of_local_dofs() -# gdof = self.dof.number_of_global_dofs() -# cm = self.cellmeasure -# c2d = self.dof.cell_to_dof() #(NC, ldof) + v = l0*v0[:, None,None,:] + l1*v1[:, None,None,:] + l2*v2[:, None,None,:] #(NQ, NC, fldof, 3) + #v[~c2fsi,:, :, :] *= -1 + v = bm.set_at(v, (~c2fsi, slice(None), slice(None), slice(None)), -v[~c2fsi,:, :, :]) + dv = bm.sum(g0*v0[:, None], axis=-1) + bm.sum(g1*v1[:, None], axis=-1) + bm.sum(g2*v2[:, None], axis=-1) + dv[~c2fsi] *= -1 -# bcs, ws = self.integrator.get_quadrature_points_and_weights() -# phi = self.basis(bcs) #(NQ, NC, ldof, GD) -# mass = np.einsum("qclg, qcdg, c, q->cld", phi, phi, cm, ws) + N = fldof*i + #val[..., N:N+fldof] = dv[:,None,:]*phif+bm.sum(gphif*v, axis=-1) + val = bm.set_at(val,(...,slice(N, N+fldof)),dv[:,None,:]*phif+bm.sum(gphif*v, axis=-1)) -# I = np.broadcast_to(c2d[:, :, None], shape=mass.shape) -# J = np.broadcast_to(c2d[:, None, :], shape=mass.shape) -# M = csr_matrix((mass.flat, (I.flat, J.flat)), shape=(gdof, gdof)) -# return M + if(p > 0): + phi = self.bspace.basis(bcs, p=p-1) #(NQ, NC, cldof) + gphi = self.bspace.grad_basis(bcs, p=p-1) #(NQ, NC, cldof) + for i in range(3): + l0 = l[lf[i, 0]] + l1 = l[lf[i, 1]] + l2 = l[lf[i, 2]] -# def div_matrix(self, space): -# mesh = self.mesh -# NC = mesh.number_of_cells() -# ldof = self.dof.number_of_local_dofs() -# gdof0 = self.dof.number_of_global_dofs() -# gdof1 = space.dof.number_of_global_dofs() -# cm = self.cellmeasure + g0 = glambda[:, lf[i, 0], None] + g1 = glambda[:, lf[i, 1], None] + g2 = glambda[:, lf[i, 2], None] + gi = glambda[:, i, None] -# c2d = self.dof.cell_to_dof() #(NC, ldof) -# c2d_space = space.dof.cell_to_dof() + v0 = (node[cell[:, lf[i, 0]]] - node[cell[:, i]])/cm[:, None] + v1 = (node[cell[:, lf[i, 1]]] - node[cell[:, i]])/cm[:, None] + v2 = (node[cell[:, lf[i, 2]]] - node[cell[:, i]])/cm[:, None] -# bcs, ws = self.integrator.get_quadrature_points_and_weights() + w = (l0*v0[:, None,None,:] + l1*v1[:, None,None,:] + l2*v2[:, None,None,:]) #(NQ, NC, fldof, 3) + dw = bm.sum(g0*v0[:, None], axis=-1) + bm.sum(g1*v1[:, None], axis=-1) + bm.sum(g2*v2[:, None], axis=-1) + v = l[i]*w #(NQ, NC, fldof, 3) + dv = bm.sum(gi[:,None]*w, axis=-1) + l[i, ..., 0]*dw[:,None] -# if space.basis.coordtype == 'barycentric': -# fval = space.basis(bcs) #(NQ, NC, ldof1) -# else: -# points = self.mesh.bc_to_point(bcs) -# fval = space.basis(points) + N = fldof*4+i*cldof//3 + #val[..., N:N+cldof//3] = dv*phi + bm.sum(gphi*v, axis=-1) + val = bm.set_at(val,(...,slice(N, N+cldof//3)),dv*phi + bm.sum(gphi*v, axis=-1)) + return val[index] + + @barycentric + def face_basis(self, bcs, index=_S): + # fn = self.mesh.face_normal(index=index) + # fn /= bm.sqrt(bm.sum(fn**2, axis=1)[:, None]) -# phi = self.div_basis(bcs) #(NQ, NC, ldof) -# A = np.einsum("qcl, qcd, c, q->cld", phi, fval, cm, ws) + # fphi = self.bspace.basis(bcs, p=self.p) #(NQ, NE, eldof) + # val = fphi[..., None] * fn[:, None,None,:] + fn = self.mesh.face_unit_normal(index=index) + fm = self.mesh.entity_measure('face')[index] + fm = 1/fm + fm = 3*fm + fphi = self.bspace.basis(bcs, p=self.p) #(NC, NQ, eldof) + val0 = fphi * fm[:, None,None] -# I = np.broadcast_to(c2d[:, :, None], shape=A.shape) -# J = np.broadcast_to(c2d_space[:, None, :], shape=A.shape) -# B = csr_matrix((A.flat, (I.flat, J.flat)), shape=(gdof0, gdof1)) -# return B + val = val0[:,:,:,None] * fn[:,None,None,:] -# def source_vector(self, f): -# mesh = self.mesh -# cm = self.cellmeasure -# ldof = self.dof.number_of_local_dofs() -# gdof = self.dof.number_of_global_dofs() -# bcs, ws = self.integrator.get_quadrature_points_and_weights() -# c2d = self.dof.cell_to_dof() #(NC, ldof) + return val -# p = mesh.bc_to_point(bcs) #(NQ, NC, GD) -# fval = f(p) #(NQ, NC, GD) + def cell_to_dof(self): + return self.dof.cell2dof + + def number_of_global_dofs(self): + return self.dof.number_of_global_dofs() + + def number_of_local_dofs(self, doftype='all'): + return self.dof.number_of_local_dofs(doftype) + + + def is_boundary_dof(self, threshold=None,method=None): + return self.dof.is_boundary_dof() + + @barycentric + def value(self, uh, bcs, index=_S): + '''@ + @brief 计算一个有限元函数在每个单元的 bc 处的值 + @param bc : (..., GD+1) + @return val : (..., NC, GD) + ''' + phi = self.basis(bcs) + c2d = self.dof.cell_to_dof() + # uh[c2d].shape = (NC, ldof); phi.shape = (..., NC, ldof, GD) + val = bm.einsum("cl, cqlk->cqk", uh[c2d], phi) + return val + + @barycentric + def div_value(self, uh, bcs, index=_S): + '''@ + @brief 计算一个有限元函数在每个单元的 bc 处的值 + @param bc : (..., GD+1) + @return val : (..., NC, GD) + ''' + cphi = self.div_basis(bcs) + c2d = self.dof.cell_to_dof() + # uh[c2d].shape = (NC, ldof); phi.shape = (..., NC, ldof, GD) + val = bm.einsum("cl, cql->cq", uh[c2d], cphi) + return val + + @barycentric + def curl_value(self, uh, bc, index=_S): + pass + + @barycentric + def grad_value(self, uh, bc, index=_S): + pass + + @barycentric + def edge_value(self, uh, bc, index=_S): + pass + + @barycentric + def face_value(self, uh, bc, index=_S): + pass + + def mass_matrix(self): + mesh = self.mesh + NC = mesh.number_of_cells() + ldof = self.dof.number_of_local_dofs() + gdof = self.dof.number_of_global_dofs() + cm = self.cellmeasure + c2d = self.dof.cell_to_dof() #(NC, ldof) + + bcs, ws = self.qf.get_quadrature_points_and_weights() + phi = self.basis(bcs) #(NC, NQ, ldof, GD) + mass = bm.einsum("cqlg, cqdg, c, q->cld", phi, phi, cm, ws) + + I = bm.broadcast_to(c2d[:, :, None], shape=mass.shape) + J = bm.broadcast_to(c2d[:, None, :], shape=mass.shape) + M = csr_matrix((mass.flat, (I.flat, J.flat)), shape=(gdof, gdof)) + return M + + def div_matrix(self, space): + mesh = self.mesh + NC = mesh.number_of_cells() + ldof = self.dof.number_of_local_dofs() + gdof0 = self.dof.number_of_global_dofs() + gdof1 = space.dof.number_of_global_dofs() + cm = self.cellmeasure + + c2d = self.dof.cell_to_dof() #(NC, ldof) + c2d_space = space.dof.cell_to_dof() + + bcs, ws = self.qf.get_quadrature_points_and_weights() + # if space.basis.coordtype == 'barycentric': + fval = space.basis(bcs) #(NQ, NC, ldof1) + # else: + # points = self.mesh.bc_to_point(bcs) + # fval = space.basis(points) + + phi = self.div_basis(bcs) #(NQ, NC, ldof) + A = bm.einsum("cql, cqd, c, q->cld", phi, fval, cm, ws) + + I = bm.broadcast_to(c2d[:, :, None], shape=A.shape) + J = bm.broadcast_to(c2d_space[:, None, :], shape=A.shape) + B = csr_matrix((A.flat, (I.flat, J.flat)), shape=(gdof0, gdof1)) + return B + + def source_vector(self, f): + mesh = self.mesh + cm = self.cellmeasure + ldof = self.dof.number_of_local_dofs() + gdof = self.dof.number_of_global_dofs() + bcs, ws = self.qf.get_quadrature_points_and_weights() + c2d = self.dof.cell_to_dof() #(NC, ldof) + + p = mesh.bc_to_point(bcs) #(NC, NQ, GD) + fval = f(p) #(NC, NQ, GD) + + phi = self.basis(bcs) #(NC, NQ, ldof, GD) + val = bm.einsum("cqg, cqlg, q, c->cl", fval, phi, ws, cm)# (NC, ldof) + vec = bm.zeros(gdof, device=self.device, dtype=self.ftype) + bm.add.at(vec, c2d, val) + #bm.scatter_add(vec, c2d.reshape(-1), val.reshape(-1)) + return vec + + def set_neumann_bc(self, g): + p = self.p + mesh = self.mesh + + qf = self.mesh.quadrature_formula(p+3, 'face') + bcs, ws = qf.get_quadrature_points_and_weights() + + fidx = self.mesh.boundary_face_index() + gdof = self.dof.number_of_global_dofs() + face2dof = self.dof.face_to_dof()[fidx] + + phi = self.face_basis(bcs)[fidx] #(NF, NQ, fdof, GD) + f2n = self.mesh.face_unit_normal(index=fidx) + phi = bm.einsum("fqlg, fg->fql", phi, f2n) #(NF, NQ, fdof) + + points = mesh.bc_to_point(bcs)[fidx] + gval = g(points) + + fm = self.mesh.entity_measure("face")[fidx] + vec = bm.zeros(gdof, device=self.device, dtype=self.ftype) + k= bm.einsum("fql, fq, f, q->fl", phi, gval, fm, ws) + bm.add.at(vec,face2dof,k) + return vec + + def set_dirichlet_bc(self, gd, uh, threshold=None, q=None, method=None): + """ + @brief 设置狄利克雷边界条件,使用边界上的 L2 投影 + """ + p = self.p + mesh = self.mesh + bspace = self.bspace + isbdFace = mesh.boundary_face_flag() + face2dof = self.dof.face_to_dof()[isbdFace] + fm = mesh.entity_measure('face')[isbdFace] + gdof = self.number_of_global_dofs() + n = mesh.edge_unit_normal()[isbdFace] + # Bernstein 空间的单位质量矩阵 + qf = self.mesh.quadrature_formula(p+3, 'face') + bcs, ws = qf.get_quadrature_points_and_weights() + bphi = 3*bspace.basis(bcs,p=p) + M = bm.einsum("cql, cqm, q->lm", bphi, bphi, ws) + Minv = bm.linalg.inv(M) + Minv = Minv*fm[:,None,None] + + points = mesh.bc_to_point(bcs)[isbdFace] + gDval = -gd(points, n) + g = -bm.einsum('cql, cq,q->cl', bphi, gDval,ws) + + #uh[edge2dof] = bm.einsum('cl, clm->cm', g, Minv) # (NC, ldof) + uh = bm.set_at(uh,(face2dof),bm.einsum('cl, clm->cm', g, Minv)) + # 边界自由度 + isDDof = bm.zeros(gdof, device=self.device, dtype=bm.bool) + isDDof[face2dof] = True + return uh,isDDof + + boundary_interpolate = set_dirichlet_bc -# phi = self.basis(bcs) #(NQ, NC, ldof, GD) -# val = np.einsum("qcg, qclg, q, c->cl", fval, phi, ws, cm)# (NC, ldof) -# vec = np.zeros(gdof, dtype=np.float_) -# np.add.at(vec, c2d, val) -# return vec # def projection(self, f, method="L2"): # M = self.mass_matrix() @@ -459,29 +537,29 @@ def basis(self, bcs): # val = np.einsum("qci, q, c->", errval, ws, cm) # return np.sqrt(val) -# def set_neumann_bc(self, g): -# bcs, ws = self.integralalg.faceintegrator.get_quadrature_points_and_weights() + # def set_neumann_bc(self, g): + # bcs, ws = self.integralalg.faceintegrator.get_quadrature_points_and_weights() -# fdof = self.dof.number_of_local_dofs('face') -# fidx = self.mesh.ds.boundary_face_index() -# phi = self.face_basis(bcs, index=fidx) #(NQ, NE0, edof, GD) -# f2n = self.mesh.face_unit_normal(index=fidx) -# phi = np.einsum("qelg, eg->qel", phi, f2n) #(NQ, NE0, edof) + # fdof = self.dof.number_of_local_dofs('face') + # fidx = self.mesh.ds.boundary_face_index() + # phi = self.face_basis(bcs, index=fidx) #(NQ, NE0, edof, GD) + # f2n = self.mesh.face_unit_normal(index=fidx) + # phi = np.einsum("qelg, eg->qel", phi, f2n) #(NQ, NE0, edof) -# point = self.mesh.bc_to_point(bcs, index=fidx) -# gval = g(point) #(NQ, NE0) + # point = self.mesh.bc_to_point(bcs, index=fidx) + # gval = g(point) #(NQ, NE0) -# fm = self.mesh.entity_measure("face")[fidx] -# integ = np.einsum("qel, qe, e, q->el", phi, gval, fm, ws) + # fm = self.mesh.entity_measure("face")[fidx] + # integ = np.einsum("qel, qe, e, q->el", phi, gval, fm, ws) -# f2d = np.ones((len(fidx), fdof), dtype=np.int_) -# f2d[:, 0] = fdof*fidx -# f2d = np.cumsum(f2d, axis=-1) + # f2d = np.ones((len(fidx), fdof), dtype=np.int_) + # f2d[:, 0] = fdof*fidx + # f2d = np.cumsum(f2d, axis=-1) -# gdof = self.dof.number_of_global_dofs() -# val = np.zeros(gdof, dtype=np.float_) -# np.add.at(val, f2d, integ) -# return val + # gdof = self.dof.number_of_global_dofs() + # val = np.zeros(gdof, dtype=np.float_) + # np.add.at(val, f2d, integ) + # return val # def set_dirichlet_bc(self, gD, uh, threshold=None, q=None): # p = self.p diff --git a/fealpy/functionspace/__init__.py b/fealpy/functionspace/__init__.py index c6437b713..6f644c911 100644 --- a/fealpy/functionspace/__init__.py +++ b/fealpy/functionspace/__init__.py @@ -17,6 +17,7 @@ from .second_nedelec_fe_space_3d import SecondNedelecFiniteElementSpace3d from .RaviartThomasFiniteElementSpace2d import RTFiniteElementSpace2d +from .RaviartThomasFiniteElementSpace3d import RTFiniteElementSpace3d from .parametric_lagrange_fe_space import ParametricLagrangeFESpace diff --git a/test/functionspace/RaviartThomasFiniteElementSpace3d_data.py b/test/functionspace/RaviartThomasFiniteElementSpace3d_data.py index f8106bb66..426780c89 100644 --- a/test/functionspace/RaviartThomasFiniteElementSpace3d_data.py +++ b/test/functionspace/RaviartThomasFiniteElementSpace3d_data.py @@ -225,5 +225,35 @@ [-0.48, -0.3 , 0.06], [ 0.36, -0.9 , 0.18]]]]), + "div_basis":np.array([[[ 4.8, 7.2, 9.6, 2.4, 7.2, 9.6, 2.4, 4.8, 9.6, + 2.4, 4.8, 7.2, -3.6, -1.2, 1.2], + [ 2.4, 7.2, 2.4, 12. , 7.2, 2.4, 12. , 2.4, 2.4, + 12. , 2.4, 7.2, 6. , -3.6, 1.2]], + + [[ 4.8, 7.2, 9.6, -2.4, -7.2, -9.6, 2.4, 4.8, 9.6, + 2.4, 4.8, 7.2, -3.6, -1.2, 1.2], + [ 2.4, 7.2, 2.4, -12. , -7.2, -2.4, 12. , 2.4, 2.4, + 12. , 2.4, 7.2, 6. , -3.6, 1.2]], + + [[ 4.8, 7.2, 9.6, -2.4, -7.2, -9.6, 2.4, 4.8, 9.6, + 2.4, 4.8, 7.2, -3.6, -1.2, 1.2], + [ 2.4, 7.2, 2.4, -12. , -7.2, -2.4, 12. , 2.4, 2.4, + 12. , 2.4, 7.2, 6. , -3.6, 1.2]], + + [[ 4.8, 7.2, 9.6, -2.4, -7.2, -9.6, 2.4, 4.8, 9.6, + 2.4, 4.8, 7.2, -3.6, -1.2, 1.2], + [ 2.4, 7.2, 2.4, -12. , -7.2, -2.4, 12. , 2.4, 2.4, + 12. , 2.4, 7.2, 6. , -3.6, 1.2]], + + [[ 4.8, 7.2, 9.6, -2.4, -7.2, -9.6, 2.4, 4.8, 9.6, + 2.4, 4.8, 7.2, -3.6, -1.2, 1.2], + [ 2.4, 7.2, 2.4, -12. , -7.2, -2.4, 12. , 2.4, 2.4, + 12. , 2.4, 7.2, 6. , -3.6, 1.2]], + + [[ 4.8, 7.2, 9.6, -2.4, -7.2, -9.6, -2.4, -4.8, -9.6, + 2.4, 4.8, 7.2, -3.6, -1.2, 1.2], + [ 2.4, 7.2, 2.4, -12. , -7.2, -2.4, -12. , -2.4, -2.4, + 12. , 2.4, 7.2, 6. , -3.6, 1.2]]]), + } ] \ No newline at end of file diff --git a/test/functionspace/test_RaviartThomasFiniteElementSpace3d.py b/test/functionspace/test_RaviartThomasFiniteElementSpace3d.py index 0016f01a0..43e03f0ea 100644 --- a/test/functionspace/test_RaviartThomasFiniteElementSpace3d.py +++ b/test/functionspace/test_RaviartThomasFiniteElementSpace3d.py @@ -39,9 +39,24 @@ def test_basis(self,meshdata,backend): np.testing.assert_allclose(bm.to_numpy(basis), meshdata["basis"],1e-15) + @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) + @pytest.mark.parametrize("meshdata", init_data) + def test_div_basis(self,meshdata,backend): + bm.set_backend(backend) + + mesh = TetrahedronMesh.from_box(nx = 1,ny =1,nz=1) + p = 1 + a = RTFiniteElementSpace3d(mesh,p) + bcs = bm.array([[0.1,0.2,0.3,0.4], + [0.5,0.1,0.3,0.1]]) + div_basis = a.div_basis(bcs) + + np.testing.assert_allclose(bm.to_numpy(div_basis), meshdata["div_basis"],1e-14) + if __name__ == "__main__": # test = TestFirstNedelecDof3d() # test.test_cell_to_dof(init_data[0],'numpy') test = TestRTFiniteElementSpace3d() - test.test_basis(init_data[0],'pytorch') \ No newline at end of file + #test.test_basis(init_data[0],'numpy') + test.test_div_basis(init_data[0],'numpy') \ No newline at end of file From d65060dc6d925163dbd2ecebee093a964fd24045 Mon Sep 17 00:00:00 2001 From: chenchunyu Date: Tue, 19 Nov 2024 15:46:38 +0800 Subject: [PATCH 08/63] update fracturex --- .../cases/phase_field/model0_example.py | 2 +- .../square_domian_with_fracture.py | 8 +++++- .../fracturex/phasefield/main_solver.py | 25 ++++++++++++------- fealpy/solver/gmres_solver.py | 16 ++++++------ 4 files changed, 33 insertions(+), 18 deletions(-) diff --git a/app/fracturex/fracturex/cases/phase_field/model0_example.py b/app/fracturex/fracturex/cases/phase_field/model0_example.py index 1928c8660..09315834b 100644 --- a/app/fracturex/fracturex/cases/phase_field/model0_example.py +++ b/app/fracturex/fracturex/cases/phase_field/model0_example.py @@ -146,7 +146,7 @@ def is_dirchlet_boundary(self, p): if bm.backend_name == 'pytorch': ms.auto_assembly_matrix() - +#ms.set_scipy_solver() ms.output_timer() ms.save_vtkfile(fname=vtkname) diff --git a/app/fracturex/fracturex/cases/phase_field/square_domian_with_fracture.py b/app/fracturex/fracturex/cases/phase_field/square_domian_with_fracture.py index edcfbd50c..b1d45307c 100644 --- a/app/fracturex/fracturex/cases/phase_field/square_domian_with_fracture.py +++ b/app/fracturex/fracturex/cases/phase_field/square_domian_with_fracture.py @@ -120,6 +120,10 @@ def adaptive_mesh(self, mesh, d0=0.49, d1=1.01, h=0.005): default=False, type=bool, help='是否使用 GPU, 默认为 False.') +parser.add_argument('--cupy', + default=False, type=bool, + help='是否使用cupy求解.') + args = parser.parse_args() p= args.degree maxit = args.maxit @@ -133,6 +137,7 @@ def adaptive_mesh(self, mesh, d0=0.49, d1=1.01, h=0.005): vtkname = args.vtkname +'_' + args.mesh_type + '_' force_type = args.force_type gpu = args.gpu +cupy = args.cupy tmr = timer() next(tmr) @@ -163,7 +168,6 @@ def adaptive_mesh(self, mesh, d0=0.49, d1=1.01, h=0.005): fname = args.mesh_type + '_square_with_a_notch_init.vtu' mesh.to_vtk(fname=fname) - ms = MainSolve(mesh=mesh, material_params=model.params, p=p, model_type=model_type) tmr.send('init') @@ -189,6 +193,8 @@ def adaptive_mesh(self, mesh, d0=0.49, d1=1.01, h=0.005): if bm.backend_name == 'pytorch': ms.auto_assembly_matrix() +if cupy: + ms.set_cupy_solver() ms.output_timer() ms.save_vtkfile(fname=vtkname) diff --git a/app/fracturex/fracturex/phasefield/main_solver.py b/app/fracturex/fracturex/phasefield/main_solver.py index c035916e9..9b4298dca 100644 --- a/app/fracturex/fracturex/phasefield/main_solver.py +++ b/app/fracturex/fracturex/phasefield/main_solver.py @@ -19,9 +19,6 @@ from fealpy.solver import cg, spsolve, gmres from scipy.sparse.linalg import lgmres -#from scipy.sparse.linalg import gmres -#from scipy.sparse.linalg import minres -#from scipy.linalg import issymmetric from app.fracturex.fracturex.phasefield.energy_degradation_function import EnergyDegradationFunction as EDFunc from app.fracturex.fracturex.phasefield.phase_fracture_material import PhaseFractureMaterialFactory @@ -83,10 +80,14 @@ def __init__(self, mesh, material_params: Dict, self._atype = None self._timer = False - if self.uh.device == 'cpu': + if bm.device_type(self.uh) == 'cpu': + print('Using scipy solver.') self._solver = 'scipy' - else: + elif bm.device_type(self.uh) == 'cuda': + print('Using cupy solver.') self._solver = 'cupy' + else: + raise ValueError(f"Unknown device type: {bm.device_type(self.uh)}") # Initialize the timer self.tmr = timer() @@ -225,7 +226,6 @@ def solve_displacement(self) -> float: tmr.send('apply_bc') du = self.solver(A, R, atol=1e-20) - uh += du[:] self.uh = uh @@ -268,9 +268,8 @@ def coef(bc, index): A, R = self._apply_boundary_conditions(A, R, field='phase') tmr.send('phase_apply_bc') - + dd = self.solver(A, R, atol=1e-20) - d += dd[:] @@ -618,12 +617,20 @@ def set_energy_degradation(self, EDfunc=None): else: self.EDFunc = EDFunc - def gpu_solver(self): + def set_cupy_solver(self): """ Use GPU to solve the problem. """ + print('Using cupy solver.') self._solver = 'cupy' + def set_scipy_solver(self): + """ + Use GPU to solve the problem. + """ + print('Using scipy solver.') + self._solver = 'scipy' + def solver(self, A, R, atol=1e-20): """ Choose the solver. diff --git a/fealpy/solver/gmres_solver.py b/fealpy/solver/gmres_solver.py index 4ffd4937f..6761c0941 100644 --- a/fealpy/solver/gmres_solver.py +++ b/fealpy/solver/gmres_solver.py @@ -15,7 +15,7 @@ def _to_cupy_data(A, b, x0): if isinstance(A.indices(), np.ndarray): # numpy backend A = A.to_scipy() A = cp.sparse.csr_matrix(A.astype(cp.float64)) - elif A.indices().device.type == "cpu": # torch backend + elif bm.device_type(A.indices()) == "cpu": # torch backend A = A.device_put("cuda") indices = cp.from_dlpack(A.indices()) data = cp.from_dlpack(A.values()) @@ -25,14 +25,16 @@ def _to_cupy_data(A, b, x0): data = cp.from_dlpack(A.values()) A = cp.sparse.csr_matrix((data, (indices[0], indices[1])), shape=A.shape) - if isinstance(b, np.ndarray) or b.device.type == "cpu": + if isinstance(b, np.ndarray) or bm.device_type(b) == "cpu": b = bm.to_numpy(b) b = cp.array(b) - x0 = bm.to_numpy(x0) - x0 = cp.array(x0) + if x0 is not None: + x0 = bm.to_numpy(x0) + x0 = cp.array(x0) else: b = cp.from_dlpack(b) - x0 = cp.from_dlpack(x0) + if x0 is not None: + x0 = cp.from_dlpack(x0) return A, b, x0 @@ -50,14 +52,14 @@ def _cupy_solve(A, b, tol, x0, maxiter ,atol): import cupy as cp import cupyx.scipy.sparse.linalg as cpx - iscpu = isinstance(b, np.ndarray) or b.device.type == "cpu" + iscpu = isinstance(b, np.ndarray) or bm.device_type(b) == "cpu" A, b, x0 = _to_cupy_data(A, b, x0) x, info = cpx.gmres(A, b, tol=tol, x0=x0, maxiter=maxiter, atol=atol) if iscpu: x = cp.asnumpy(x) return x -def _scipy_solve(A, b, tol, x0, maxiter ,atol): +def _scipy_solve(A, b, tol, x0, maxiter, atol): from scipy.sparse.linalg import gmres from scipy.sparse import csr_matrix From 8a07a73d65a941c9f3a8fd6e7fb5702ce21afaa0 Mon Sep 17 00:00:00 2001 From: tiantian0347 Date: Tue, 19 Nov 2024 15:51:46 +0800 Subject: [PATCH 09/63] update --- .../crack_surface_density_function.py | 1 + .../phasefield/energy_degradation_function.py | 71 +++++++++++-------- .../fracturex/phasefield/main_solver.py | 12 +++- 3 files changed, 52 insertions(+), 32 deletions(-) create mode 100644 app/fracturex/fracturex/phasefield/crack_surface_density_function.py diff --git a/app/fracturex/fracturex/phasefield/crack_surface_density_function.py b/app/fracturex/fracturex/phasefield/crack_surface_density_function.py new file mode 100644 index 000000000..764106aab --- /dev/null +++ b/app/fracturex/fracturex/phasefield/crack_surface_density_function.py @@ -0,0 +1 @@ +from fealpy.backend import backend_manager as bm \ No newline at end of file diff --git a/app/fracturex/fracturex/phasefield/energy_degradation_function.py b/app/fracturex/fracturex/phasefield/energy_degradation_function.py index 9887b4ab7..9ebcb0bb8 100644 --- a/app/fracturex/fracturex/phasefield/energy_degradation_function.py +++ b/app/fracturex/fracturex/phasefield/energy_degradation_function.py @@ -1,25 +1,25 @@ class EnergyDegradationFunction: def __init__(self, degradation_type='quadratic', **kwargs): """ - 初始化能量退化函数模块。 + Initialize the energy degradation function module. - 参数: - degradation_type (str): 能量退化函数的类型,支持 'quadratic', - 'thrice', 'user_defined' 等。 - kwargs (dict): 额外的参数用于不同类型的退化函数,例如指数函数中的指数因子。 + Parameters: + Degradation-type (str): The type of energy degradation function that supports' quadratic ', + 'thrice ',' user_define ', etc. + Kwargs (dict): Additional parameters are used for different types of degenerate functions, such as exponential factors in exponential functions. """ self.degradation_type = degradation_type self.params = kwargs def degradation_function(self, d): """ - 根据相场值 d 计算能量退化因子 g(d)。 + Calculate the energy degradation factor g (d) based on the phase field value d. - 参数: - d (float or numpy array): 相场值,取值范围为 [0, 1]。 + Parameters: + d (float or numpy array): Phase field value, with a value range of [0,1]. - 返回: - g (float or numpy array): 退化因子 g(d)。 + return: + g (float or numpy array): Degradation factor g (d). """ if self.degradation_type == 'quadratic': return self._quadratic_degradation(d) @@ -37,17 +37,25 @@ def grad_degradation_function(self, d): return self._user_defined_grad_degradation(d) else: raise ValueError(f"Unknown degradation type: {self.degradation_type}") + + def grad_grad_degradation_function(self, d): + if self.degradation_type == 'quadratic': + return self._quadratice_grad_grad_degradation(d) + elif self.degradation_type == 'user_defined': + return self._user_defined_grad_grad_degradation(d) + else: + raise ValueError(f"Unknown degradation type: {self.degradation_type}") def _quadratic_degradation(self, d): """ - 二次能量退化函数 g(d) = (1 - d)^2。 + The quadratic energy degradation function g (d)=(1-d) ^ 2. - 参数: - d (float or numpy array): 相场值。 + Parameters: + d (float or numpy array): phase field value. - 返回: - g (float or numpy array): 退化因子 g(d)。 + return: + g (float or numpy array): Degradation factor g (d). """ eps = 1e-10 gd = (1 - d)**2 + eps @@ -55,22 +63,27 @@ def _quadratic_degradation(self, d): def _quadratic_grad_degradation(self, d): """ - 二次能量退化函数的导数 g'(d) = -2(1 - d)。 + The derivative of the quadratic energy degradation function g'(d) = -2(1 - d)。 """ eps = 1e-10 - gd = -2*(1 - d) + eps - return gd + g_gd = -2*(1 - d) + return g_gd + + def _quadratice_grad_grad_degradation(self, d): + """ + The second derivative of the quadratic energy degradation function g''(d) = 2。 + """ + return 2 def _thrice_degradation(self, d): """ - 指数型能量退化函数 g(d) = 3(1-d)^2-2(1-d)^3。 + The thrice degradation function g(d) = 3(1-d)^2-2(1-d)^3。 - 参数: - d (float or numpy array): 相场值。 - alpha (float): 指数函数中的指数因子,通常为一个正数。 + Parameters: + d (float or numpy array): phase field value. - 返回: - g (float or numpy array): 退化因子 g(d)。 + return: + g (float or numpy array): Degradation factor g (d). """ eps = 1e-10 gd = 3*(1 - d)**2 - 2*(1-d)**3 + eps @@ -78,13 +91,13 @@ def _thrice_degradation(self, d): def _user_defined_degradation(self, d): """ - 用户自定义能量退化函数,可以是任何用户定义的形式。 + User defined energy degradation function, which can be in any user-defined form. - 参数: - d (float or numpy array): 相场值。 + Parameters: + d (float or numpy array): phase field value. - 返回: - g (float or numpy array): 退化因子 g(d)。 + return: + g (float or numpy array): Degradation factor g (d). """ custom_function = self.params.get('custom_function') if custom_function is None: diff --git a/app/fracturex/fracturex/phasefield/main_solver.py b/app/fracturex/fracturex/phasefield/main_solver.py index c035916e9..e1d95497e 100644 --- a/app/fracturex/fracturex/phasefield/main_solver.py +++ b/app/fracturex/fracturex/phasefield/main_solver.py @@ -56,7 +56,8 @@ def __init__(self, mesh, material_params: Dict, # self.model = model # Material and energy degradation function - self.EDFunc = EDFunc() + self.set_energy_degradation(degradation_type='quadratic') + self.EDFunc = EDFunc(degradation_type='quadratic') self.model_type = model_type self.pfcm = PhaseFractureMaterialFactory.create(model_type, material_params, self.EDFunc) @@ -91,7 +92,12 @@ def __init__(self, mesh, material_params: Dict, # Initialize the timer self.tmr = timer() next(self.tmr) - + + def initialization_settings(self): + """ + Initialize the settings for the problem. + """ + pass def solve(self, maxit: int = 50): """ @@ -614,7 +620,7 @@ def set_energy_degradation(self, EDfunc=None): Energy degradation function. """ if EDfunc is not None: - pass + self.EDFunc = EDfunc else: self.EDFunc = EDFunc From c4c5c9c070c6191249482bd9966bc791d9a30f4d Mon Sep 17 00:00:00 2001 From: "brighthe98@gmail.com" Date: Tue, 19 Nov 2024 19:30:39 +0800 Subject: [PATCH 10/63] update --- .gitignore | 1 + app/soptx/simp/compliance_minimizer.py | 176 ----------- app/soptx/simp/compliance_minimizer_copy.py | 188 ----------- app/soptx/simp/simp_compliance.py | 105 ------- app/soptx/simp/simp_compliance_test.py | 183 ----------- .../simp/test_compliance_minimizer.ipynb | 256 --------------- .../simp/test_compliance_minimizer_copy.ipynb | 234 -------------- app/soptx/simp/test_lagrange_d.py | 90 ------ app/soptx/simp/top_2d_1_0.py | 295 ------------------ app/soptx/simp/top_2d_2_0.py | 253 --------------- app/soptx/simp/top_3d_1_0.py | 224 ------------- app/soptx/simp/utilfuncs.py | 33 -- app/soptx/soptx/opt/oc.py | 3 +- app/soptx/soptx/tests/test_oc.py | 5 + fealpy/mesh/uniform_mesh_2d.py | 72 +++++ 15 files changed, 79 insertions(+), 2039 deletions(-) delete mode 100644 app/soptx/simp/compliance_minimizer.py delete mode 100644 app/soptx/simp/compliance_minimizer_copy.py delete mode 100644 app/soptx/simp/simp_compliance.py delete mode 100644 app/soptx/simp/simp_compliance_test.py delete mode 100644 app/soptx/simp/test_compliance_minimizer.ipynb delete mode 100644 app/soptx/simp/test_compliance_minimizer_copy.ipynb delete mode 100644 app/soptx/simp/test_lagrange_d.py delete mode 100644 app/soptx/simp/top_2d_1_0.py delete mode 100644 app/soptx/simp/top_2d_2_0.py delete mode 100644 app/soptx/simp/top_3d_1_0.py delete mode 100644 app/soptx/simp/utilfuncs.py diff --git a/.gitignore b/.gitignore index ae09c4ccc..8e54a3177 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ dist *.png *.fig *.vtk +*.vts *.so *.swn *.swo diff --git a/app/soptx/simp/compliance_minimizer.py b/app/soptx/simp/compliance_minimizer.py deleted file mode 100644 index b4f4c0edd..000000000 --- a/app/soptx/simp/compliance_minimizer.py +++ /dev/null @@ -1,176 +0,0 @@ -import jax -import jax.numpy as jnp -from jax import jit, value_and_grad -from utilfuncs import Mesher - -class ComplianceMinimizer: - def __init__(self, - mesh = None, - bc = None, - material = None, - globalvolCons = None, - optimizationParams = None, - projection = None): - - # 默认网格参数 - if mesh is None: - nelx, nely = 60, 30 - elemSize = jnp.array([1., 1.]) - mesh = {'nelx': nelx, 'nely': nely, 'elemSize': elemSize, - 'ndof': 2 * (nelx + 1) * (nely + 1), 'numElems': nelx * nely} - else: - nelx, nely = mesh['nelx'], mesh['nely'] - - # 默认材料参数 - if material is None: - material = {'Emax': 1., 'Emin': 1e-3, 'nu': 0.3, 'penal': 3.} - - # 默认全局体积约束 - if globalvolCons is None: - globalvolCons = {'isOn': True, 'vf': 0.5} - - # 默认优化器参数 - if optimizationParams is None: - optimizationParams = {'maxIters': 200, 'minIters': 100, 'relTol': 0.05} - - # 默认投影滤波器参数 - if projection is None: - projection = {'isOn': True, 'beta': 4, 'c0': 0.5} - - # 默认边界条件和载荷 - if bc is None: - example = 1 - if example == 1: - force = jnp.zeros((mesh['ndof'], 1)) - dofs = jnp.arange(mesh['ndof']) - fixed = dofs[0:2 * (nely + 1):1] - free = jnp.setdiff1d(jnp.arange(mesh['ndof']), fixed) - # JAX 数组是不可变的,不能直接进行赋值操作。需要使用 JAX 提供的 .at[].set() 方法来进行修改 - force = force.at[2 * (nelx + 1) * (nely + 1) - 2 * nely + 1, 0].set(-1) - symXAxis = False - symYAxis = False - elif example == 2: - force = jnp.zeros((mesh['ndof'], 1)) - dofs = jnp.arange(mesh['ndof']) - fixed = dofs[0:2 * (nely + 1):1] - free = jnp.setdiff1d(jnp.arange(mesh['ndof']), fixed) - # JAX 数组是不可变的,不能直接进行赋值操作。需要使用 JAX 提供的 .at[].set() 方法来进行修改 - force = force.at[2 * (nelx + 1) * (nely + 1) - (nely + 1), 0].set(-1) - symXAxis = True - symYAxis = False - else: - force = jnp.zeros((mesh['ndof'], 1)) - fixed = jnp.array([]) - free = jnp.array([]) - symXAxis = False - symYAxis = False - - bc = {'force': force, 'fixed': fixed, 'free': free, - 'symXAxis': symXAxis, 'symYAxis': symYAxis} - - self.mesh = mesh - self.material = material - self.bc = bc - - # 初始化 Mesher 类并获取初始刚度矩阵 - M = Mesher() - self.edofMat, self.idx = M.getMeshStructure(mesh) - self.K0 = M.getK0(self.material) - - # 设置全局体积约束 - self.globalVolumeConstraint = globalvolCons - - # 自动微分计算柔顺度及其灵敏度 - self.objectiveHandle = value_and_grad(self.computeCompliance) - - # 自动微分计算约束值及其灵敏度 - self.consHandle = self.computeConstraints - self.numConstraints = 1 - - # 设置优化器参数 - self.optimizationParams = optimizationParams - - # 设置投影滤波器参数 - self.projection = projection - - def computeCompliance(self, rho): - - @jit - # 投影滤波器 - def projectionFilter(rho): - if(self.projection['isOn']): - v1 = jnp.tanh(self.projection['c0'] * self.projection['beta']) - nm = v1 + jnp.tanh(self.projection['beta'] * (rho - self.projection['c0'])) - dnm = v1 + jnp.tanh(self.projection['beta'] * (1. - self.projection['c0'])) - return nm/dnm - else: - return rho - - @jit - # SIMP 材料插值模型 - def materialModel(rho): - E = self.material['Emin'] + \ - (self.material['Emax'] - self.material['Emin']) * \ - (rho + 0.01) ** self.material['penal'] - return E - - @jit - # 组装全局刚度矩阵 - def assembleK(E): - K_asm = jnp.zeros((self.mesh['ndof'], self.mesh['ndof'])) - K_elem = (self.K0.flatten()[jnp.newaxis]).T - - K_elem = (K_elem*E).T.flatten() - K_asm = K_asm.at[(self.idx)].add(K_elem) - return K_asm - - @jit - # 直接法求解线性方程组 - def solveKuf(K): - u_free = jax.scipy.linalg.solve(K[self.bc['free'],:][:,self.bc['free']], \ - self.bc['force'][self.bc['free']], check_finite=False) - u = jnp.zeros((self.mesh['ndof'])) - u = u.at[self.bc['free']].set(u_free.reshape(-1)) - return u - - rho = projectionFilter(rho) - E = materialModel(rho) - K = assembleK(E) - u = solveKuf(K) - J = jnp.dot(self.bc['force'].T, u)[0] - - return J - - def computeConstraints(self, rho, epoch): - - @jit - # 计算体积约束 - def computeGlobalVolumeConstraint(rho): - g = jnp.mean(rho) / self.globalVolumeConstraint['vf'] - 1. - return g - - # 体积约束的值及其灵敏度 - c, gradc = value_and_grad(computeGlobalVolumeConstraint)(rho) - c, gradc = c.reshape((1, 1)), gradc.reshape((1, -1)) - - return c, gradc - - def mmaOptimize(self, optimizationParams, ft): - - rho = jnp.ones((self.mesh['nelx'] * self.mesh['nely'])) - loop = 0 - change = 1. - - while( (change > optimizationParams['relTol']) \ - and (loop < optimizationParams['maxIters']) \ - or (loop < optimizationParams['minIters']) ): - - loop = loop + 1 - - J, dJ = self.objectiveHandle(rho) - - vc, dvc = self.consHandle(rho, loop) - - - - diff --git a/app/soptx/simp/compliance_minimizer_copy.py b/app/soptx/simp/compliance_minimizer_copy.py deleted file mode 100644 index 050e0def8..000000000 --- a/app/soptx/simp/compliance_minimizer_copy.py +++ /dev/null @@ -1,188 +0,0 @@ -import jax -import jax.numpy as jnp -from jax import jit, value_and_grad -from utilfuncs import Mesher - -@jit -# 投影滤波器 -def projectionFilter(projection, rho): - if(projection['isOn']): - v1 = jnp.tanh(projection['c0'] * projection['beta']) - nm = v1 + jnp.tanh(projection['beta'] * (rho - projection['c0'])) - dnm = v1 + jnp.tanh(projection['beta'] * (1. - projection['c0'])) - return nm/dnm - else: - return rho - -@jit -# SIMP 材料插值模型 -def materialModel(material, rho): - E = material['Emin'] + \ - (material['Emax'] - material['Emin']) * \ - (rho + 0.01) ** material['penal'] - return E - -@jit -# 组装全局刚度矩阵 -def assembleK(mesh, K0, E, idx): - K_asm = jnp.zeros((mesh['ndof'], mesh['ndof'])) - K_elem = (K0.flatten()[jnp.newaxis]).T - - K_elem = (K_elem * E).T.flatten() - K_asm = K_asm.at[(idx)].add(K_elem) - return K_asm - -@jit -# 直接法求解线性方程组 -def solveKuf(bc, K, mesh): - u_free = jax.scipy.linalg.solve(K[bc['free'],:][:, bc['free']], \ - bc['force'][bc['free']], check_finite=False) - u = jnp.zeros((mesh['ndof'])) - u = u.at[bc['free']].set(u_free.reshape(-1)) - return u - -class ComplianceMinimizer: - def __init__(self, - mesh = None, - bc = None, - material = None, - globalvolCons = None, - optimizationParams = None, - projection = None): - - # 默认网格参数 - if mesh is None: - nelx, nely = 60, 30 - elemSize = jnp.array([1., 1.]) - mesh = {'nelx': nelx, 'nely': nely, 'elemSize': elemSize, - 'ndof': 2 * (nelx + 1) * (nely + 1), 'numElems': nelx * nely} - else: - nelx, nely = mesh['nelx'], mesh['nely'] - - # 默认材料参数 - if material is None: - material = {'Emax': 1., 'Emin': 1e-3, 'nu': 0.3, 'penal': 3.} - - # 默认全局体积约束 - if globalvolCons is None: - globalvolCons = {'isOn': True, 'vf': 0.5} - - # 默认优化器参数 - if optimizationParams is None: - optimizationParams = {'maxIters': 200, 'minIters': 100, 'relTol': 0.05} - - # 默认投影滤波器参数 - if projection is None: - projection = {'isOn': True, 'beta': 4, 'c0': 0.5} - - # 默认边界条件和载荷 - if bc is None: - example = 1 - if example == 1: - force = jnp.zeros((mesh['ndof'], 1)) - dofs = jnp.arange(mesh['ndof']) - fixed = dofs[0:2 * (nely + 1):1] - free = jnp.setdiff1d(jnp.arange(mesh['ndof']), fixed) - # JAX 数组是不可变的,不能直接进行赋值操作。需要使用 JAX 提供的 .at[].set() 方法来进行修改 - force = force.at[2 * (nelx + 1) * (nely + 1) - 2 * nely + 1, 0].set(-1) - symXAxis = False - symYAxis = False - elif example == 2: - force = jnp.zeros((mesh['ndof'], 1)) - dofs = jnp.arange(mesh['ndof']) - fixed = dofs[0:2 * (nely + 1):1] - free = jnp.setdiff1d(jnp.arange(mesh['ndof']), fixed) - # JAX 数组是不可变的,不能直接进行赋值操作。需要使用 JAX 提供的 .at[].set() 方法来进行修改 - force = force.at[2 * (nelx + 1) * (nely + 1) - (nely + 1), 0].set(-1) - symXAxis = True - symYAxis = False - else: - force = jnp.zeros((mesh['ndof'], 1)) - fixed = jnp.array([]) - free = jnp.array([]) - symXAxis = False - symYAxis = False - - bc = {'force': force, 'fixed': fixed, 'free': free, - 'symXAxis': symXAxis, 'symYAxis': symYAxis} - - self.mesh = mesh - self.material = material - self.bc = bc - - # 初始化 Mesher 类并获取初始刚度矩阵 - M = Mesher() - self.edofMat, self.idx = M.getMeshStructure(mesh) - self.K0 = M.getK0(self.material) - - # 设置全局体积约束 - self.globalVolumeConstraint = globalvolCons - - # 自动微分计算柔顺度及其灵敏度 - self.objectiveHandle = value_and_grad(self.computeCompliance) - - # 自动微分计算约束值及其灵敏度 - self.consHandle = self.computeConstraints - self.numConstraints = 1 - - # 设置优化器参数 - self.optimizationParams = optimizationParams - - # 设置投影滤波器参数 - self.projection = projection - - def projectionFilter(self, rho): - return projectionFilter(self.projection, rho) - - def materialModel(self, rho): - return materialModel(self.material, rho) - - def assembleK(self, E): - return assembleK(self.mesh, self.K0, E, self.idx) - - def solveKuf(self, K): - return solveKuf(self.bc, K, self.mesh) - - def computeCompliance(self, rho): - - rho = self.projectionFilter(rho) - E = self.materialModel(rho) - K = self.assembleK(E) - u = self.solveKuf(K) - J = jnp.dot(self.bc['force'].T, u)[0] - - return J - - def computeConstraints(self, rho, epoch): - - @jit - # 计算体积约束 - def computeGlobalVolumeConstraint(rho): - g = jnp.mean(rho) / self.globalVolumeConstraint['vf'] - 1. - return g - - # 体积约束的值及其灵敏度 - c, gradc = value_and_grad(computeGlobalVolumeConstraint)(rho) - c, gradc = c.reshape((1, 1)), gradc.reshape((1, -1)) - - return c, gradc - - def mmaOptimize(self, optimizationParams, ft): - - rho = jnp.ones((self.mesh['nelx'] * self.mesh['nely'])) - loop = 0 - change = 1. - - while( (change > optimizationParams['relTol']) \ - and (loop < optimizationParams['maxIters']) \ - or (loop < optimizationParams['minIters']) ): - - loop = loop + 1 - - J, dJ = self.objectiveHandle(rho) - - vc, dvc = self.consHandle(rho, loop) - - - - diff --git a/app/soptx/simp/simp_compliance.py b/app/soptx/simp/simp_compliance.py deleted file mode 100644 index 7ce69ae00..000000000 --- a/app/soptx/simp/simp_compliance.py +++ /dev/null @@ -1,105 +0,0 @@ -import numpy as np - -from fealpy.mesh import UniformMesh2d -from fealpy.functionspace import LagrangeFESpace as Space -from utilfuncs import compute_filter - -class TopSimp: - def __init__(self, mesh=None, space=None, bc=None, material=None, filter=None): - - # Default mesh parameters - if mesh is None: - nelx, nely = 3, 2 - domain = [0, 3, 0, 2] - hx = (domain[1] - domain[0]) / nelx - hy = (domain[3] - domain[2]) / nely - mesh = UniformMesh2d(extent=(0, nelx, 0, nely), h=(hx, hy), origin=(domain[0], domain[2])) - - # Default space parameters - if space is None: - p = 1 - GD = 2 - space = Space(mesh, p=p, doforder='vdims') - vspace = GD * (space, ) - - # Default boundary conditions and loads - if bc is None: - example = 1 - if example == 1: - gdof = vspace[0].number_of_global_dofs() - vgdof = gdof * GD - force = np.zeros( (vgdof, 1) ) - force[vgdof-1, 0] = -1 - #force[1, 0] = -1 - fixeddofs = np.arange(0, 2*(mesh.ny+1), 1) - # np.union1d( np.arange(0, 2*(nely+1), 2), np.array([2*(nelx+1)*(nely+1) - 1]) ) - - - bc = {'force': force, 'fixeddofs': fixeddofs} - - # Default material parameters - if material is None: - material = {'E0': 1., 'nu': 0.3, 'penal': 3.} - - # Default filter parameters - if filter is None: - filter_radius = 1.5 - H, Hs = compute_filter(mesh, filter_radius) - ft = {'type':1, 'H':H, 'Hs':Hs} - - self.mesh = mesh - self.space = space - self.bc = bc - self.material = material - self.ft = ft - - # SIMP 材料插值模型 - def material_model(self, rho): - E = self.material['E0'] * (rho) ** self.material['penal'] - return E - - def fe_analysis(self, rho): - from fealpy.fem import LinearElasticityOperatorIntegrator - from fealpy.fem import BilinearForm - from scipy.sparse import spdiags - from scipy.sparse.linalg import spsolve - GD = 2 - uh = self.space.function(dim=GD) - lambda_ = (self.material['E0'] * self.material['nu']) / ((1+self.material['nu']) * (1-2*self.material['nu'])) - mu = (self.material['E0']) / (2 * (1+self.material['nu'])) - - p = 1 - integrator = LinearElasticityOperatorIntegrator(lam=lambda_, mu=mu, q=p+1) - vspace = GD * (self.space, ) - bform = BilinearForm(vspace) - bform.add_domain_integrator(integrator) - KK = integrator.assembly_cell_matrix(space=vspace) - print("KK:", KK.shape, "\n", KK[0].round(4)) - K = bform.assembly() - #print("K:", K.shape, "\n", K.toarray().round(4)) - - dflag = self.bc['fixeddofs'] - #print("dflag:", dflag) - uh.flat[dflag] = 0 - - F = self.bc['force'] - #print("F:", F.shape, "\n", F.T.round(4)) - F -= K@uh.reshape(-1, 1) - F[dflag.flat] = uh.reshape(-1, 1)[dflag.flat] - #print("F:", F.shape, "\n", F.T.round(4)) - - bdIdx = np.zeros(K.shape[0], dtype=np.int_) - bdIdx[dflag.flat] = 1 - - D0 = spdiags(1-bdIdx, 0, K.shape[0], K.shape[0]) - D1 = spdiags(bdIdx, 0, K.shape[0], K.shape[0]) - K = D0@K@D0 + D1 - #print("K:", K.shape, "\n", K.toarray().round(4)) - - # 线性方程组求解 - uh.flat[:] = spsolve(K, F) - - return uh - - def compute_compliance(self, rho): - pass diff --git a/app/soptx/simp/simp_compliance_test.py b/app/soptx/simp/simp_compliance_test.py deleted file mode 100644 index c9f7c489f..000000000 --- a/app/soptx/simp/simp_compliance_test.py +++ /dev/null @@ -1,183 +0,0 @@ -import numpy as np - -from fealpy.mesh import UniformMesh2d -from fealpy.functionspace import LagrangeFESpace as Space -from utilfuncs import compute_filter - -class TopSimp: - def __init__(self, mesh=None, space=None, bc=None, material=None, \ - filter=None, global_volume_constraints=None): - - # Default mesh parameters - if mesh is None: - nx, ny = 3, 2 - domain = [0, 3, 0, 2] - hx = (domain[1] - domain[0]) / nx - hy = (domain[3] - domain[2]) / ny - mesh = UniformMesh2d(extent=(0, nx, 0, ny), h=(hx, hy), origin=(domain[0], domain[2])) - import matplotlib.pyplot as plt - fig = plt.figure() - axes = fig.gca() - mesh.add_plot(axes) - mesh.find_node(axes, showindex=True) - mesh.find_cell(axes, showindex=True) - plt.show() - - # Default space parameters - if space is None: - p = 1 - GD = 2 - space = Space(mesh, p=p, doforder='vdims') - vspace = GD * (space, ) - - # Default boundary conditions and loads - if bc is None: - example = 1 - if example == 1: - gdof = vspace[0].number_of_global_dofs() - vgdof = gdof * GD - force = np.zeros( (vgdof, 1) ) - force[vgdof-1, 0] = -1 - #force[1, 0] = -1 - fixeddofs = np.arange(0, 2*(mesh.ny+1), 1) - # np.union1d( np.arange(0, 2*(nely+1), 2), np.array([2*(nelx+1)*(nely+1) - 1]) ) - - bc = {'force': force, 'fixeddofs': fixeddofs} - - # Default material parameters - if material is None: - material = {'E0': 1., 'nu': 0.3, 'penal': 3.} - - # Default filter parameters - if filter is None: - filter_radius = 1.5 - H, Hs = compute_filter(mesh, filter_radius) - ft = {'type':1, 'H':H, 'Hs':Hs} - - # Default global volume constraints - if global_volume_constraints is None: - global_volume_constraints = {'isOn':True, 'volfrac':0.5} - - self.mesh = mesh - self.space = space - self.bc = bc - self.material = material - self.ft = ft - self.global_volume_constraints = global_volume_constraints - - # SIMP 材料插值模型 - def material_model(self, rho): - E = self.material['E0'] * (rho) ** self.material['penal'] - return E - - def fe_analysis(self, rho): - from linear_elasticity_operator_intergrator_test import BeamOperatorIntegrator - from fealpy.fem import BilinearForm - from scipy.sparse import spdiags - from scipy.sparse.linalg import spsolve - GD = 2 - uh = self.space.function(dim=GD) - - integrator = BeamOperatorIntegrator(rho=rho, penal=self.material['penal'], \ - nu=self.material['nu'], E0=self.material['E0']) - vspace = GD * (self.space, ) - bform = BilinearForm(vspace) - bform.add_domain_integrator(integrator) - KK = integrator.assembly_cell_matrix(space=vspace) - #print("KK:", KK.shape, "\n", KK[0].round(4)) - K = bform.assembly() - #print("K:", K.shape, "\n", K.toarray().round(4)) - - dflag = self.bc['fixeddofs'] - uh.flat[dflag] = 0 - - F = self.bc['force'] - #print("F:", F.shape, "\n", F.T.round(4)) - F -= K@uh.reshape(-1, 1) - F[dflag.flat] = uh.reshape(-1, 1)[dflag.flat] - #print("F:", F.shape, "\n", F.T.round(4)) - - bdIdx = np.zeros(K.shape[0], dtype=np.int_) - bdIdx[dflag.flat] = 1 - - D0 = spdiags(1-bdIdx, 0, K.shape[0], K.shape[0]) - D1 = spdiags(bdIdx, 0, K.shape[0], K.shape[0]) - K = D0@K@D0 + D1 - #print("K:", K.shape, "\n", K.toarray().round(4)) - - # 线性方程组求解 - uh.flat[:] = spsolve(K, F) - - ldof = self.space.number_of_local_dofs() - vldof = GD * ldof - cell2dof = vspace[0].cell_to_dof() - print("cell2dof:", cell2dof) - NC = self.mesh.number_of_cells() - ue = np.zeros((NC, vldof)) - - reshaped_uh = uh.reshape(-1) - # 每个单元的自由度(每个节点两个自由度) - updated_cell2dof = np.repeat(cell2dof*GD, GD, axis=1) + np.tile(np.array([0, 1]), (NC, ldof)) - print("updated_cell2dof:", updated_cell2dof.shape, "\n", updated_cell2dof) - idx = np.array([0, 1, 4, 5, 6, 7, 2, 3], dtype=np.int_) - # 用 Top 中的自由度替换 FEALPy 中的自由度 - updated_cell2dof = updated_cell2dof[:, idx] - ue = reshaped_uh[updated_cell2dof] - - return uh, ue - - def compute_compliance(self, rho): - - uh, ue = self.fe_analysis(rho) - c1 = np.dot(self.bc['force'].T, uh.flat) - - nu = self.material['nu'] - E0 = self.material['E0'] - k = np.array([1/2 - nu/6, 1/8 + nu/8, -1/4 - nu/12, -1/8 + 3 * nu/8, - -1/4 + nu/12, -1/8 - nu/8, nu/6, 1/8 - 3 * nu/8]) - KE = E0 / (1 - nu**2) * np.array([ - [k[0], k[1], k[2], k[3], k[4], k[5], k[6], k[7]], - [k[1], k[0], k[7], k[6], k[5], k[4], k[3], k[2]], - [k[2], k[7], k[0], k[5], k[6], k[3], k[4], k[1]], - [k[3], k[6], k[5], k[0], k[7], k[2], k[1], k[4]], - [k[4], k[5], k[6], k[7], k[0], k[1], k[2], k[3]], - [k[5], k[4], k[3], k[2], k[1], k[0], k[7], k[6]], - [k[6], k[3], k[4], k[1], k[2], k[7], k[0], k[5]], - [k[7], k[2], k[1], k[4], k[3], k[6], k[5], k[0]] - ]) - - temp1 = rho ** self.material['penal'] - temp2 = np.einsum('ij, jk, ki -> i', ue, KE, ue.T) - c2 = np.einsum('i, j -> ', temp1, temp2) - - return c1, c2 - - def compute_compliance_sensitivity(self, rho): - - uh, ue = self.fe_analysis(rho) - - nu = self.material['nu'] - E0 = self.material['E0'] - k = np.array([1/2 - nu/6, 1/8 + nu/8, -1/4 - nu/12, -1/8 + 3 * nu/8, - -1/4 + nu/12, -1/8 - nu/8, nu/6, 1/8 - 3 * nu/8]) - KE = E0 / (1 - nu**2) * np.array([ - [k[0], k[1], k[2], k[3], k[4], k[5], k[6], k[7]], - [k[1], k[0], k[7], k[6], k[5], k[4], k[3], k[2]], - [k[2], k[7], k[0], k[5], k[6], k[3], k[4], k[1]], - [k[3], k[6], k[5], k[0], k[7], k[2], k[1], k[4]], - [k[4], k[5], k[6], k[7], k[0], k[1], k[2], k[3]], - [k[5], k[4], k[3], k[2], k[1], k[0], k[7], k[6]], - [k[6], k[3], k[4], k[1], k[2], k[7], k[0], k[5]], - [k[7], k[2], k[1], k[4], k[3], k[6], k[5], k[0]] - ]) - - temp1 = -self.material['penal'] * rho ** (self.material['penal'] - 1) - temp2 = np.einsum('ij, jk, ki -> i', ue, KE, ue.T) - dc = np.einsum('i, j -> ', temp1, temp2) - - return dc - - def compute_global_volume_constraint(self, rho): - g = np.mean(rho) / self.global_volume_constraints['vf'] - 1. - - return g diff --git a/app/soptx/simp/test_compliance_minimizer.ipynb b/app/soptx/simp/test_compliance_minimizer.ipynb deleted file mode 100644 index 78f32aadf..000000000 --- a/app/soptx/simp/test_compliance_minimizer.ipynb +++ /dev/null @@ -1,256 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# 柔顺度计算" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "jupyter": { - "is_executing": true - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Compliance with default rho: 41.853535\n" - ] - } - ], - "source": [ - "from compliance_minimizer import ComplianceMinimizer\n", - "import jax.numpy as jnp \n", - "\n", - "Opt = ComplianceMinimizer()\n", - "\n", - "rho = jnp.ones((Opt.mesh['nelx'] * Opt.mesh['nely']))\n", - "compliance = Opt.computeCompliance(rho)\n", - "print(\"Compliance with default rho:\", compliance)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 投影滤波器" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Original rho values: [0. 0.125 0.25 0.375 0.5 0.625 0.75 0.875 1. ]\n", - "Projected rho values: [0. 0.031 0.105 0.26000002 0.5 0.74\n", - " 0.89500004 0.96900004 1. ]\n" - ] - } - ], - "source": [ - "def test_projectionFilter(rho, beta, c0, isOn):\n", - " if isOn:\n", - " v1 = jnp.tanh(c0 * beta)\n", - " nm = v1 + jnp.tanh(beta * (rho - c0))\n", - " dnm = v1 + jnp.tanh(beta * (1. - c0))\n", - " return nm / dnm\n", - " else:\n", - " return rho\n", - "rho_values = jnp.array([0.0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0])\n", - "# 投影参数\n", - "beta = 4\n", - "c0 = 0.5\n", - "isOn = True\n", - "# 打印原始 rho 值和经过投影滤波器后的 rho 值\n", - "print(\"Original rho values: \", rho_values)\n", - "projected_rho_values = test_projectionFilter(rho_values, beta, c0, isOn)\n", - "print(\"Projected rho values: \", projected_rho_values.round(3))" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Original rho values: (9,) \n", - " [0. 0.125 0.25 0.375 0.5 0.625 0.75 0.875 1. ]\n", - "E: (1800,) \n", - " [1.0302707 1.0302707 1.0302707 ... 1.0302707 1.0302707 1.0302707]\n" - ] - } - ], - "source": [ - "import numpy as np\n", - "def test_materialModel(rho, Emin, Emax, penal):\n", - " E = Emin + (Emax - Emin) * (rho + 0.01) ** penal\n", - " return E\n", - "rho_values = np.array([0.0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0])\n", - "print(\"Original rho values: \", rho_values.shape, \"\\n\", rho_values)\n", - "Emin = 1e-3\n", - "Emax = 1\n", - "penal = 3\n", - "E = test_materialModel(rho, Emin, Emax, penal) \n", - "print(\"E: \", E.shape, \"\\n\", E)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 测试单元刚度矩阵" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "KE:\n", - " [[ 0.4945055 0.17857143 -0.3021978 -0.01373626 -0.24725275 -0.17857143\n", - " 0.05494506 0.01373626]\n", - " [ 0.17857143 0.4945055 0.01373626 0.05494506 -0.17857143 -0.24725275\n", - " -0.01373626 -0.3021978 ]\n", - " [-0.3021978 0.01373626 0.4945055 -0.17857143 0.05494506 -0.01373626\n", - " -0.24725275 0.17857143]\n", - " [-0.01373626 0.05494506 -0.17857143 0.4945055 0.01373626 -0.3021978\n", - " 0.17857143 -0.24725275]\n", - " [-0.24725275 -0.17857143 0.05494506 0.01373626 0.4945055 0.17857143\n", - " -0.3021978 -0.01373626]\n", - " [-0.17857143 -0.24725275 -0.01373626 -0.3021978 0.17857143 0.4945055\n", - " 0.01373626 0.05494506]\n", - " [ 0.05494506 -0.01373626 -0.24725275 0.17857143 -0.3021978 0.01373626\n", - " 0.4945055 -0.17857143]\n", - " [ 0.01373626 -0.3021978 0.17857143 -0.24725275 -0.01373626 0.05494506\n", - " -0.17857143 0.4945055 ]]\n" - ] - } - ], - "source": [ - "KE = Opt.K0\n", - "print(\"KE:\\n\", KE)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# 测试体积约束\n", - "体积约束\n", - "$$g \\equiv \\frac{\\sum_e\\rho_ev_e}{V^*} - 1 \\leq 0$$\n", - "其中 $\\rho_e$ 和 $v_e$ 分别是第 $e$ 个单元的密度和体积。\n", - "\n", - "`computeGlobalVolumeConstraint` 中的实现假设了每个单元体积相同且归一化,从而使用单元密度的平均值 $\\mathrm{mean}(\\rho)$ 来近似 $\\sum_e\\rho_ev_e$\n", - "\n", - "对于输入 $\\rho=[0.1,0.3,0.5,0.7,0.9]$,单元体积 $v_e=0.2$,因此 $g(\\rho)=\\frac{0.5}{0.5}-1=0$,$\\frac{\\partial{g(\\rho)}}{\\partial\\rho}=\\frac{v_e}{V^*}=\\frac{0.2}{0.5}=0.4$" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Original rho values: [0.1 0.3 0.5 0.7 0.9]\n", - "test: 0.5\n", - "Constraint value: 0.0\n", - "Constraint gradient: [0.4 0.4 0.4 0.4 0.4]\n", - "Numerical gradient: [0.40233135 0.40233135 0.40233135 0.40233135 0.40233135]\n", - "Gradient error: 0.005213049\n" - ] - } - ], - "source": [ - "import jax.numpy as jnp\n", - "from jax import value_and_grad\n", - "\n", - "# 定义计算体积约束的函数\n", - "def computeGlobalVolumeConstraint(rho, vf):\n", - " g = jnp.mean(rho) / vf - 1.\n", - " return g\n", - "\n", - "# 测试函数\n", - "def test_computeConstraints(rho, vf):\n", - " # 计算体积约束的值及其灵敏度\n", - " constraint_value, constraint_grad = value_and_grad(computeGlobalVolumeConstraint)(rho, vf)\n", - " return constraint_value, constraint_grad\n", - "\n", - "# 数值梯度计算函数\n", - "def numerical_grad(f, x, vf, eps=1e-5):\n", - " grad = jnp.zeros_like(x)\n", - " for i in range(len(x)):\n", - " x_pos = x.at[i].add(eps)\n", - " x_neg = x.at[i].add(-eps)\n", - " grad = grad.at[i].set((f(x_pos, vf) - f(x_neg, vf)) / (2 * eps))\n", - " return grad\n", - "\n", - "# 设置测试的 rho 值和体积分数 vf\n", - "rho_values = jnp.array([0.1, 0.3, 0.5, 0.7, 0.9])\n", - "vf = 0.5\n", - "\n", - "# 打印原始 rho 值\n", - "print(\"Original rho values: \", rho_values)\n", - "\n", - "# 调用测试函数并打印结果\n", - "constraint_value, constraint_grad = test_computeConstraints(rho_values, vf)\n", - "print(\"test:\", jnp.mean(rho_values))\n", - "print(\"Constraint value: \", constraint_value)\n", - "print(\"Constraint gradient: \", constraint_grad)\n", - "\n", - "# 计算数值梯度\n", - "numerical_gradient = numerical_grad(computeGlobalVolumeConstraint, rho_values, vf)\n", - "print(\"Numerical gradient: \", numerical_gradient)\n", - "\n", - "# 比较数值梯度和自动微分计算的梯度\n", - "error = jnp.linalg.norm(constraint_grad - numerical_gradient)\n", - "print(\"Gradient error: \", error)\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "jax-fem-env", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.18" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/app/soptx/simp/test_compliance_minimizer_copy.ipynb b/app/soptx/simp/test_compliance_minimizer_copy.ipynb deleted file mode 100644 index 54a6aefec..000000000 --- a/app/soptx/simp/test_compliance_minimizer_copy.ipynb +++ /dev/null @@ -1,234 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# 柔顺度计算" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "ename": "TracerBoolConversionError", - "evalue": "Attempted boolean conversion of traced array with shape bool[]..\nThe error occurred while tracing the function projectionFilter at /home/heliang/FEALPy_Development/fealpy/app/StruTopoOpt/compliance_minimizer_copy.py:6 for jit. This concrete value was not available in Python because it depends on the value of the argument projection['isOn'].\nSee https://jax.readthedocs.io/en/latest/errors.html#jax.errors.TracerBoolConversionError", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mTracerBoolConversionError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[1], line 7\u001b[0m\n\u001b[1;32m 4\u001b[0m Opt \u001b[38;5;241m=\u001b[39m ComplianceMinimizer()\n\u001b[1;32m 6\u001b[0m rho \u001b[38;5;241m=\u001b[39m jnp\u001b[38;5;241m.\u001b[39mones((Opt\u001b[38;5;241m.\u001b[39mmesh[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnelx\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m*\u001b[39m Opt\u001b[38;5;241m.\u001b[39mmesh[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnely\u001b[39m\u001b[38;5;124m'\u001b[39m]))\n\u001b[0;32m----> 7\u001b[0m compliance \u001b[38;5;241m=\u001b[39m \u001b[43mOpt\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcomputeCompliance\u001b[49m\u001b[43m(\u001b[49m\u001b[43mrho\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 8\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mCompliance with default rho:\u001b[39m\u001b[38;5;124m\"\u001b[39m, compliance)\n", - "File \u001b[0;32m~/FEALPy_Development/fealpy/app/StruTopoOpt/compliance_minimizer_copy.py:148\u001b[0m, in \u001b[0;36mComplianceMinimizer.computeCompliance\u001b[0;34m(self, rho)\u001b[0m\n\u001b[1;32m 146\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mcomputeCompliance\u001b[39m(\u001b[38;5;28mself\u001b[39m, rho):\n\u001b[0;32m--> 148\u001b[0m rho \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mprojectionFilter\u001b[49m\u001b[43m(\u001b[49m\u001b[43mrho\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 149\u001b[0m E \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmaterialModel(rho)\n\u001b[1;32m 150\u001b[0m K \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39massembleK(E)\n", - "File \u001b[0;32m~/FEALPy_Development/fealpy/app/StruTopoOpt/compliance_minimizer_copy.py:135\u001b[0m, in \u001b[0;36mComplianceMinimizer.projectionFilter\u001b[0;34m(self, rho)\u001b[0m\n\u001b[1;32m 134\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mprojectionFilter\u001b[39m(\u001b[38;5;28mself\u001b[39m, rho):\n\u001b[0;32m--> 135\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mprojectionFilter\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mprojection\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrho\u001b[49m\u001b[43m)\u001b[49m\n", - " \u001b[0;31m[... skipping hidden 11 frame]\u001b[0m\n", - "File \u001b[0;32m~/FEALPy_Development/fealpy/app/StruTopoOpt/compliance_minimizer_copy.py:9\u001b[0m, in \u001b[0;36mprojectionFilter\u001b[0;34m(projection, rho)\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[38;5;129m@jit\u001b[39m\n\u001b[1;32m 7\u001b[0m \u001b[38;5;66;03m# 投影滤波器\u001b[39;00m\n\u001b[1;32m 8\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mprojectionFilter\u001b[39m(projection, rho):\n\u001b[0;32m----> 9\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m(projection[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124misOn\u001b[39m\u001b[38;5;124m'\u001b[39m]):\n\u001b[1;32m 10\u001b[0m v1 \u001b[38;5;241m=\u001b[39m jnp\u001b[38;5;241m.\u001b[39mtanh(projection[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mc0\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m*\u001b[39m projection[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mbeta\u001b[39m\u001b[38;5;124m'\u001b[39m])\n\u001b[1;32m 11\u001b[0m nm \u001b[38;5;241m=\u001b[39m v1 \u001b[38;5;241m+\u001b[39m jnp\u001b[38;5;241m.\u001b[39mtanh(projection[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mbeta\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m*\u001b[39m (rho \u001b[38;5;241m-\u001b[39m projection[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mc0\u001b[39m\u001b[38;5;124m'\u001b[39m]))\n", - " \u001b[0;31m[... skipping hidden 1 frame]\u001b[0m\n", - "File \u001b[0;32m~/miniconda3/envs/jax-fem-env/lib/python3.9/site-packages/jax/_src/core.py:1475\u001b[0m, in \u001b[0;36mconcretization_function_error..error\u001b[0;34m(self, arg)\u001b[0m\n\u001b[1;32m 1474\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21merror\u001b[39m(\u001b[38;5;28mself\u001b[39m, arg):\n\u001b[0;32m-> 1475\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m TracerBoolConversionError(arg)\n", - "\u001b[0;31mTracerBoolConversionError\u001b[0m: Attempted boolean conversion of traced array with shape bool[]..\nThe error occurred while tracing the function projectionFilter at /home/heliang/FEALPy_Development/fealpy/app/StruTopoOpt/compliance_minimizer_copy.py:6 for jit. This concrete value was not available in Python because it depends on the value of the argument projection['isOn'].\nSee https://jax.readthedocs.io/en/latest/errors.html#jax.errors.TracerBoolConversionError" - ] - } - ], - "source": [ - "from compliance_minimizer_copy import ComplianceMinimizer\n", - "import jax.numpy as jnp \n", - "\n", - "Opt = ComplianceMinimizer()\n", - "\n", - "rho = jnp.ones((Opt.mesh['nelx'] * Opt.mesh['nely']))\n", - "compliance = Opt.computeCompliance(rho)\n", - "print(\"Compliance with default rho:\", compliance)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "JAX 遇到 TracerBoolConversionError 的原因是因为 JAX 无法在 JIT 编译的函数内部对布尔值进行直接判断。这个错误源自于 projection['isOn'] 的布尔判断。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 投影滤波器" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Original rho values: [0. 0.125 0.25 0.375 0.5 0.625 0.75 0.875 1. ]\n", - "Projected rho values: [0. 0.031 0.105 0.26000002 0.5 0.74\n", - " 0.89500004 0.96900004 1. ]\n" - ] - } - ], - "source": [ - "def test_projectionFilter(rho, beta, c0, isOn):\n", - " if isOn:\n", - " v1 = jnp.tanh(c0 * beta)\n", - " nm = v1 + jnp.tanh(beta * (rho - c0))\n", - " dnm = v1 + jnp.tanh(beta * (1. - c0))\n", - " return nm / dnm\n", - " else:\n", - " return rho\n", - "rho_values = jnp.array([0.0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0])\n", - "# 投影参数\n", - "beta = 4\n", - "c0 = 0.5\n", - "isOn = True\n", - "# 打印原始 rho 值和经过投影滤波器后的 rho 值\n", - "print(\"Original rho values: \", rho_values)\n", - "projected_rho_values = test_projectionFilter(rho_values, beta, c0, isOn)\n", - "print(\"Projected rho values: \", projected_rho_values.round(3))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 测试单元刚度矩阵" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "KE:\n", - " [[ 0.4945055 0.17857143 -0.3021978 -0.01373626 -0.24725275 -0.17857143\n", - " 0.05494506 0.01373626]\n", - " [ 0.17857143 0.4945055 0.01373626 0.05494506 -0.17857143 -0.24725275\n", - " -0.01373626 -0.3021978 ]\n", - " [-0.3021978 0.01373626 0.4945055 -0.17857143 0.05494506 -0.01373626\n", - " -0.24725275 0.17857143]\n", - " [-0.01373626 0.05494506 -0.17857143 0.4945055 0.01373626 -0.3021978\n", - " 0.17857143 -0.24725275]\n", - " [-0.24725275 -0.17857143 0.05494506 0.01373626 0.4945055 0.17857143\n", - " -0.3021978 -0.01373626]\n", - " [-0.17857143 -0.24725275 -0.01373626 -0.3021978 0.17857143 0.4945055\n", - " 0.01373626 0.05494506]\n", - " [ 0.05494506 -0.01373626 -0.24725275 0.17857143 -0.3021978 0.01373626\n", - " 0.4945055 -0.17857143]\n", - " [ 0.01373626 -0.3021978 0.17857143 -0.24725275 -0.01373626 0.05494506\n", - " -0.17857143 0.4945055 ]]\n" - ] - } - ], - "source": [ - "KE = Opt.K0\n", - "print(\"KE:\\n\", KE)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# 测试体积约束\n", - "体积约束\n", - "$$g \\equiv \\frac{\\sum_e\\rho_ev_e}{V^*} - 1 \\leq 0$$\n", - "其中 $\\rho_e$ 和 $v_e$ 分别是第 $e$ 个单元的密度和体积。\n", - "\n", - "`computeGlobalVolumeConstraint` 中的实现假设了每个单元体积相同且归一化,从而使用单元密度的平均值 $\\mathrm{mean}(\\rho)$ 来近似 $\\sum_e\\rho_ev_e$\n", - "\n", - "对于输入 $\\rho=[0.1,0.3,0.5,0.7,0.9]$,单元体积 $v_e=0.2$,因此 $g(\\rho)=\\frac{0.5}{0.5}-1=0$,$\\frac{\\partial{g(\\rho)}}{\\partial\\rho}=\\frac{v_e}{V^*}=\\frac{0.2}{0.5}=0.4$" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Original rho values: [0.1 0.3 0.5 0.7 0.9]\n", - "test: 0.5\n", - "Constraint value: 0.0\n", - "Constraint gradient: [0.4 0.4 0.4 0.4 0.4]\n", - "Numerical gradient: [0.40233135 0.40233135 0.40233135 0.40233135 0.40233135]\n", - "Gradient error: 0.005213049\n" - ] - } - ], - "source": [ - "import jax.numpy as jnp\n", - "from jax import value_and_grad\n", - "\n", - "# 定义计算体积约束的函数\n", - "def computeGlobalVolumeConstraint(rho, vf):\n", - " g = jnp.mean(rho) / vf - 1.\n", - " return g\n", - "\n", - "# 测试函数\n", - "def test_computeConstraints(rho, vf):\n", - " # 计算体积约束的值及其灵敏度\n", - " constraint_value, constraint_grad = value_and_grad(computeGlobalVolumeConstraint)(rho, vf)\n", - " return constraint_value, constraint_grad\n", - "\n", - "# 数值梯度计算函数\n", - "def numerical_grad(f, x, vf, eps=1e-5):\n", - " grad = jnp.zeros_like(x)\n", - " for i in range(len(x)):\n", - " x_pos = x.at[i].add(eps)\n", - " x_neg = x.at[i].add(-eps)\n", - " grad = grad.at[i].set((f(x_pos, vf) - f(x_neg, vf)) / (2 * eps))\n", - " return grad\n", - "\n", - "# 设置测试的 rho 值和体积分数 vf\n", - "rho_values = jnp.array([0.1, 0.3, 0.5, 0.7, 0.9])\n", - "vf = 0.5\n", - "\n", - "# 打印原始 rho 值\n", - "print(\"Original rho values: \", rho_values)\n", - "\n", - "# 调用测试函数并打印结果\n", - "constraint_value, constraint_grad = test_computeConstraints(rho_values, vf)\n", - "print(\"test:\", jnp.mean(rho_values))\n", - "print(\"Constraint value: \", constraint_value)\n", - "print(\"Constraint gradient: \", constraint_grad)\n", - "\n", - "# 计算数值梯度\n", - "numerical_gradient = numerical_grad(computeGlobalVolumeConstraint, rho_values, vf)\n", - "print(\"Numerical gradient: \", numerical_gradient)\n", - "\n", - "# 比较数值梯度和自动微分计算的梯度\n", - "error = jnp.linalg.norm(constraint_grad - numerical_gradient)\n", - "print(\"Gradient error: \", error)\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "jax-fem-env", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.12" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/app/soptx/simp/test_lagrange_d.py b/app/soptx/simp/test_lagrange_d.py deleted file mode 100644 index 05756673f..000000000 --- a/app/soptx/simp/test_lagrange_d.py +++ /dev/null @@ -1,90 +0,0 @@ -from fealpy.backend import backend_manager as bm -# bm.set_backend('numpy') -bm.set_backend('pytorch') -# bm.set_backend('jax') - -from fealpy.mesh import UniformMesh2d -from fealpy.functionspace import LagrangeFESpace, TensorFunctionSpace - -from fealpy.typing import TensorLike - -# Half-MBB -def source(points: TensorLike) -> TensorLike: - - val = bm.zeros(points.shape, dtype=points.dtype) - val[ny, 1] = -1 - - return val - -def dirichlet(points: TensorLike) -> TensorLike: - - return bm.ones(points.shape, dtype=points.dtype) - -def is_dirichlet_boundary(points: TensorLike) -> TensorLike: - """ - Determine which boundary edges satisfy the given property. - - Args: - points (TensorLike): The coordinates of the points defining the edges. - - Returns: - TensorLike: A boolean array indicating which boundary edges satisfy the property. - The length of the array is NBE, which represents the number of boundary edges. - """ - temp1 = bm.abs(points[:, 0]) < 1e-12 - temp2 = (bm.arange(len(points)) % 2 == 0) - temp = temp1 & temp2 - - return temp - -def is_dirichlet_boundary_edge(points: TensorLike) -> TensorLike: - """ - Determine which boundary edges satisfy the given property. - - Args: - points (TensorLike): The coordinates of the points defining the edges. - - Returns: - TensorLike: A boolean array indicating which boundary edges satisfy the property. - The length of the array is NBE, which represents the number of boundary edges. - """ - temp = bm.abs(points[:, 0]) < 1e-12 - - return temp - -def is_dirichlet_direction_0() -> TensorLike: - temp = bm.tensor([True, False]) - - return temp - -def is_dirichlet_direction_1() -> TensorLike: - temp = bm.tensor([1, 0]) - - return temp - - -# Default input parameters -nx = 2 -ny = 2 - -extent = [0, nx, 0, ny] -h = [1, 1] -origin = [0, 0] -mesh = UniformMesh2d(extent, h, origin) -NN = mesh.number_of_nodes() -NC = mesh.number_of_cells() - -space = LagrangeFESpace(mesh, p=2, ctype='C') -ldof = space.number_of_local_dofs() -gdof = space.number_of_global_dofs() - -tensor_space = TensorFunctionSpace(space, shape=(-1, 2)) -tldof = tensor_space.number_of_local_dofs() -tgdof = tensor_space.number_of_global_dofs() -uh = tensor_space.function() - -space_d = LagrangeFESpace(mesh, p=2, ctype='D') -ldof_d = space_d.number_of_local_dofs() -gdof_d = space_d.number_of_global_dofs() -rho = space_d.function() -print("uh:", uh) \ No newline at end of file diff --git a/app/soptx/simp/top_2d_1_0.py b/app/soptx/simp/top_2d_1_0.py deleted file mode 100644 index 3ddc199e1..000000000 --- a/app/soptx/simp/top_2d_1_0.py +++ /dev/null @@ -1,295 +0,0 @@ -from fealpy.mesh import UniformMesh2d -from fealpy.functionspace import LagrangeFESpace, TensorFunctionSpace - -from fealpy.fem import LinearElasticityIntegrator, BilinearForm -from fealpy.fem import DirichletBC as DBC - -from fealpy.typing import TensorLike, Tuple -from builtins import float - -from fealpy.sparse import COOTensor - -from fealpy.solver import cg - -from fealpy.backend import backend_manager as bm - -# bm.set_backend('numpy') -# bm.set_backend('pytorch') -# bm.set_backend('jax') - -def material_model_SIMP(rho: TensorLike, penal: float, E0: float, - Emin: float=None) -> TensorLike: - if Emin is None: - E = rho ** penal * E0 - else: - E = Emin + rho ** penal * (E0 - Emin) - return E - -def material_model_SIMP_derivative(rho: TensorLike, penal: float, E0: float, - Emin: float=None) -> TensorLike: - if Emin is None: - dE = -penal * rho ** (penal - 1) * E0 - else: - dE = -penal * rho ** (penal - 1) * (E0 - Emin) - return dE - -# TODO 可以使用稀疏矩阵存储 -def compute_filter(rmin: int) -> Tuple[TensorLike, TensorLike]: - H = bm.zeros((NC, NC), dtype=bm.float64) - - # 确定哪些单元在滤波器半径范围内 - for i1 in range(nx): - for j1 in range(ny): - e1 = (i1) * ny + j1 - # 确定滤波器半径 rmin 的整数边界 - imin = max(i1 - (bm.ceil(rmin) - 1), 0.) - imax = min(i1 + (bm.ceil(rmin)), nx) - for i2 in range(int(imin), int(imax)): - jmin = max(j1 - (bm.ceil(rmin) - 1), 0.) - jmax = min(j1 + (bm.ceil(rmin)), ny) - for j2 in range(int(jmin), int(jmax)): - e2 = i2 * ny + j2 - H[e1, e2] = max(0., rmin - bm.sqrt((i1-i2)**2 + (j1-j2)**2)) - - Hs = bm.sum(H, axis=1) - - return H, Hs - -def apply_filter(ft, rho, dc, dv): - - if ft == 0: - dc = bm.matmul(H, bm.multiply(rho, dc) / Hs / bm.maximum(1e-3, rho)) - dv = dv - elif ft == 1: - dc = bm.matmul(H, (dc / Hs)) - dv = bm.matmul(H, (dv / Hs)) - - return dc, dv - - -# def check(nx, ny, rmin, rho, dc): - -# dcn_e = bm.zeros((nx, ny), dtype=bm.float64) - -# dc_e = bm.reshape(dc, (nx, ny)) -# rho_e = bm.reshape(rho, (nx, ny)) - -# # 计算过滤器半径 -# r = int(rmin) - -# for i in range(nx): -# for j in range(ny): -# sum_val = 0.0 -# # 确定邻域的范围 -# min_x = max(i - r, 0) -# max_x = min(i + r + 1, nx) -# min_y = max(j - r, 0) -# max_y = min(j + r + 1, ny) - -# for k in range(min_x, max_x): -# for l in range(min_y, max_y): - -# # Calculate convolution operator value for the element (k,l) with respect to (i,j) -# fac = rmin - bm.sqrt((i - k)**2 + (j - l)**2) - -# # Accumulate the convolution sum -# sum_val += max(0, fac) - -# # 基于 convolution 算子的值修改单元的灵敏度 -# dcn_e[j, i] += max(0, fac) * rho_e[l, k] * dc_e[l, k] - -# # Normalize the modified sensitivity for element (i, j) -# dcn_e[j, i] /= (rho_e[j, i] * sum_val) - -# dcn = bm.reshape(bm.flip(dcn_e, axis=0), (nx, ny)).T.reshape(-1) - -# return dcn - - -# Optimality criterion -def oc(volfrac, nx, ny, rho, dc, dv, passive=None): - # 初始化 Lagrange 乘子 - l1, l2 = 0, 1e5 - - # 定义设计变量中允许的最大变化 - move = 0.2 - - rho_new = bm.copy(rho) - - # 二分法以找到满足体积约束的 Lagrange 乘子 - while (l2 - l1) > 1e-4: - # 计算当前 Lagrange 乘子区间的中点 - lmid = 0.5 * (l2 + l1) - # Lower limit move restriction - tmp0 = rho - move - # Upper limit move restriction - tmp1 = rho + move - - # Design variable update (intermediate step) using OC update scheme - be = -dc / dv / lmid - tmp2 = rho * bm.sqrt(be) - tmp3 = bm.minimum(tmp1, tmp2) - tmp4 = bm.minimum(1, tmp3) - tmp5 = bm.maximum(tmp0, tmp4) - - rho_new = bm.maximum(0.001, tmp5) - - # 寻找 Passive 单元,并将其密度设置为最小密度 - if passive is not None: - rho_new[passive == 1] = 0.001 - - # 检查当前设计是否满足体积约束 - if bm.sum(rho_new) - volfrac * nx * ny > 0: - l1 = lmid - else: - l2 = lmid - - return rho_new - - -# Short Cantilever -def source(points: TensorLike) -> TensorLike: - - val = bm.zeros(points.shape, dtype=points.dtype) - val[nx*(ny+1), 1] = -1 - - return val - -def dirichlet(points: TensorLike) -> TensorLike: - - return bm.zeros(points.shape, dtype=points.dtype) - -def is_dirichlet_boundary_edge(points: TensorLike) -> TensorLike: - """ - Determine which boundary edges satisfy the given property. - - Args: - points (TensorLike): The coordinates of the points defining the edges. - - Returns: - TensorLike: A boolean array indicating which boundary edges satisfy the property. - The length of the array is NBE, which represents the number of boundary edges. - """ - - temp = (points[:, 0] == 0.0) - - return temp - - -# Default input parameters -nx = 3 -ny = 2 -volfrac = 0.5 -penal = 3.0 -rmin = 1.5 - -extent = [0, nx, 0, ny] -h = [1, 1] -origin = [0, 0] -mesh = UniformMesh2d(extent, h, origin) -# import matplotlib.pyplot as plt -# fig = plt.figure() -# axes = fig.add_subplot(111) -# mesh.add_plot(axes) -# mesh.find_node(axes, showindex=True) -# mesh.find_cell(axes, showindex=True) -# plt.show() -NC = mesh.number_of_cells() -p = 1 -space = LagrangeFESpace(mesh, p=p, ctype='C') -tensor_space = TensorFunctionSpace(space, shape=(-1, 2)) - -# Allocate design variables, initialize and allocate sens. -rho = volfrac * bm.ones(NC, dtype=bm.float64) - -# element stiffness matrix -integrator_bi = LinearElasticityIntegrator(E=1.0, nu=0.3, - elasticity_type='stress', coef=None, q=5) -KE = integrator_bi.assembly(space=tensor_space) - -# Filter -H, Hs = compute_filter(rmin) - -import matplotlib.pyplot as plt -plt.ion() -fig, ax = plt.subplots() -image = ax.imshow(-rho.reshape(nx, ny).T, cmap='gray', vmin=-1, vmax=0) -ax.axis('off') - -# Set loop counter and gradient vectors -loop = 0 -change = 1 -dv = bm.zeros(NC, dtype=bm.float64) -dc = bm.ones(NC, dtype=bm.float64) -c = bm.ones(NC, dtype=bm.float64) -while change > 0.01 and loop < 2000: - loop += 1 - - rho_old = bm.copy(rho) - - # Setup and solve FE problem - uh = tensor_space.function() - E = material_model_SIMP(rho=rho, penal=penal, E0=1.0, Emin=None) - integrator_bi = LinearElasticityIntegrator(E=1.0, nu=0.3, - elasticity_type='stress', coef=E, q=5) - KK = integrator_bi.assembly(space=tensor_space) - bform = BilinearForm(tensor_space) - bform.add_integrator(integrator_bi) - K = bform.assembly() - - F = tensor_space.interpolate(source) - - dbc = DBC(space=tensor_space, gd=dirichlet, left=False) - isDDof = tensor_space.is_boundary_dof(threshold=is_dirichlet_boundary_edge) - - F = dbc.check_vector(F) - uh = tensor_space.boundary_interpolate(gD=dirichlet, uh=uh, threshold=is_dirichlet_boundary_edge) - F = F - K.matmul(uh[:]) - F[isDDof] = uh[isDDof] - - K = dbc.check_matrix(K) - kwargs = K.values_context() - indices = K.indices() - new_values = bm.copy(K.values()) - IDX = isDDof[indices[0, :]] | isDDof[indices[1, :]] - new_values[IDX] = 0 - - K = COOTensor(indices, new_values, K.sparse_shape) - index, = bm.nonzero(isDDof) - one_values = bm.ones(len(index), **kwargs) - one_indices = bm.stack([index, index], axis=0) - K1 = COOTensor(one_indices, one_values, K.sparse_shape) - K = K.add(K1).coalesce() - K_dense = K.to_dense() - - uh[:] = cg(K, F, maxiter=5000, atol=1e-14, rtol=1e-14) - - # Objective and sensitivity - obj = 0 - cell2ldof = tensor_space.cell_to_dof() - uhe = uh[cell2ldof] - c[:] = bm.einsum('ci, cik, ck -> c', uhe, KE, uhe) - obj += bm.einsum('c, c -> ', E, c) - - dE = material_model_SIMP_derivative(rho=rho, penal=penal, E0=1.0, Emin=None) - dc[:] = bm.einsum('c, c -> c', dE, c) - - dv[:] = bm.ones(NC, dtype=bm.float64) - - # 灵敏度过滤 - dcn, dvn = apply_filter(ft=0, H=H, Hs=Hs, rho=rho, dc=dc, dv=dv) - - # Optimality criteria - rho = oc(volfrac=volfrac, nx=nx, ny=ny, rho=rho, dc=dc, dv=dv) - - # Compute the change by the inf. norm - change = bm.linalg.norm(rho.reshape(NC, 1) - rho_old.reshape(NC, 1), bm.inf) - - # Write iteration history to screen - print("iter.: {0} , object.: {1:.3f} Volfrac.: {2:.3f}, change.: {3:.3f}".format( - loop, obj, bm.sum(rho) / NC, change)) - - # Plot - image.set_data(-rho.reshape(nx, ny).T) - plt.draw() - plt.pause(1e-5) \ No newline at end of file diff --git a/app/soptx/simp/top_2d_2_0.py b/app/soptx/simp/top_2d_2_0.py deleted file mode 100644 index 4c75e2633..000000000 --- a/app/soptx/simp/top_2d_2_0.py +++ /dev/null @@ -1,253 +0,0 @@ -from fealpy.mesh import UniformMesh2d -from fealpy.functionspace import LagrangeFESpace, TensorFunctionSpace - -from fealpy.fem import LinearElasticityIntegrator, BilinearForm -from fealpy.fem import DirichletBC as DBC - -from fealpy.typing import TensorLike, Tuple -from builtins import float - -from fealpy.sparse import COOTensor - -from fealpy.solver import cg - -from fealpy.backend import backend_manager as bm - -from math import ceil, sqrt - - -# bm.set_backend('numpy') -bm.set_backend('pytorch') -# bm.set_backend('jax') - -def material_model_SIMP(rho: TensorLike, penal: float, E0: float, - Emin: float=None) -> TensorLike: - if Emin is None: - E = rho ** penal * E0 - else: - E = Emin + rho ** penal * (E0 - Emin) - return E - -def material_model_SIMP_derivative(rho: TensorLike, penal: float, E0: float, - Emin: float=None) -> TensorLike: - if Emin is None: - dE = -penal * rho ** (penal - 1) * E0 - else: - dE = -penal * rho ** (penal - 1) * (E0 - Emin) - return dE - -def compute_filter(rmin: int) -> Tuple[TensorLike, TensorLike]: - nfilter = int(nx * ny * ((2 * (ceil(rmin) - 1) + 1) ** 2)) - iH = bm.zeros(nfilter) - jH = bm.zeros(nfilter) - sH = bm.zeros(nfilter) - cc = 0 - for i in range(nx): - for j in range(ny): - row = i * ny + j - kk1 = int(max(i - (ceil(rmin) - 1), 0)) - kk2 = int(min(i + ceil(rmin), nx)) - ll1 = int(max(j - (ceil(rmin) - 1), 0)) - ll2 = int(min(j + ceil(rmin), ny)) - for k in range(kk1, kk2): - for l in range(ll1, ll2): - col = k * ny + l - fac = rmin - sqrt(((i-k) * (i-k) + (j-l) * (j-l))) - iH[cc] = row - jH[cc] = col - sH[cc] = max(0.0, fac) - cc = cc + 1 - # Finalize assembly and convert to csc format - H = COOTensor(indices=bm.stack((iH, jH), axis=0), values=sH, spshape=(nx*ny, nx*ny)).tocsr() - Hs = H @ bm.ones(H.shape[1]) - # TODO 目前 COOTensor 没有实现 sum 方法 - # Hs = bm.sum(H, axis=1) - - return H, Hs - -# Optimality criterion -def oc(nx, ny, rho, dce, dve, g): - l1 = 0 - l2 = 1e9 - move = 0.2 - # reshape to perform vector operations - rho_new = bm.zeros(nx*ny, dtype=bm.float64) - while (l2 - l1) / (l2 + l1)> 1e-3: - lmid = 0.5 * (l2 + l1) - rho_new[:] = bm.maximum(0.0, bm.maximum(rho-move, - bm.minimum(1.0, - bm.minimum(rho+move, rho*bm.sqrt(-dce/dve/lmid))))) - # 检查当前设计是否满足体积约束 - gt = g + bm.sum((dve * (rho_new - rho))) - if gt > 0 : - l1 = lmid - else: - l2 = lmid - - return rho_new, gt - - -# Half-MBB -def source(points: TensorLike) -> TensorLike: - - val = bm.zeros(points.shape, dtype=points.dtype) - val[ny, 1] = -1 - - return val - -def dirichlet(points: TensorLike) -> TensorLike: - - return bm.zeros(points.shape, dtype=points.dtype) - -# def is_dirichlet_boundary(points: TensorLike) -> TensorLike: -# """ -# Determine which boundary edges satisfy the given property. - -# Args: -# points (TensorLike): The coordinates of the points defining the edges. - -# Returns: -# TensorLike: A boolean array indicating which boundary edges satisfy the property. -# The length of the array is NBE, which represents the number of boundary edges. -# """ -# temp1 = (points[:, 0] == 0.0) -# temp2 = (bm.arange(len(points)) % 2 == 0) -# temp = temp1 & temp2 - -# return temp - -def is_dirichlet_boundary_edge(points: TensorLike) -> TensorLike: - """ - Determine which boundary edges satisfy the given property. - - Args: - points (TensorLike): The coordinates of the points defining the edges. - - Returns: - TensorLike: A boolean array indicating which boundary edges satisfy the property. - The length of the array is NBE, which represents the number of boundary edges. - """ - temp = (points[:, 0] == 0.0) - - return temp - - -# Default input parameters -nx = 160 -ny = 80 -volfrac = 0.5 -penal = 3.0 -rmin = 1.5 -ft = 0 # ft==0 -> sens, ft==1 -> dens - - -extent = [0, nx, 0, ny] -h = [1, 1] -origin = [0, 0] -mesh = UniformMesh2d(extent, h, origin) -NC = mesh.number_of_cells() -p = 1 -space = LagrangeFESpace(mesh, p=p, ctype='C') -tensor_space = TensorFunctionSpace(space, shape=(-1, 2)) - -# Allocate design variables, initialize and allocate sens. -rho = volfrac * bm.ones(NC, dtype=bm.float64) -rho_old = bm.copy(rho) -rho_Phys = bm.copy(rho_old) -g = 0 # must be initialized to use the NGuyen/Paulino OC approach - -# element stiffness matrix -integrator_bi = LinearElasticityIntegrator(E=1.0, nu=0.3, - elasticity_type='stress', coef=None, q=5) -KE = integrator_bi.assembly(space=tensor_space) - -# Filter -H, Hs = compute_filter(rmin) - -# Set loop counter and gradient vectors -loop = 0 -change = 1 -dve = bm.zeros(NC, dtype=bm.float64) -dce = bm.ones(NC, dtype=bm.float64) -ce = bm.ones(NC, dtype=bm.float64) -while change > 0.01 and loop < 2000: - loop += 1 - - # Setup and solve FE problem - uh = tensor_space.function() - E = material_model_SIMP(rho=rho_Phys, penal=penal, E0=1.0, Emin=1e-9) - integrator_bi = LinearElasticityIntegrator(E=1.0, nu=0.3, - elasticity_type='stress', coef=E, q=5) - KK = integrator_bi.assembly(space=tensor_space) - bform = BilinearForm(tensor_space) - bform.add_integrator(integrator_bi) - K = bform.assembly() - - F = tensor_space.interpolate(source) - - dbc = DBC(space=tensor_space, gd=dirichlet, left=False) - isDDof = tensor_space.is_boundary_dof(threshold=is_dirichlet_boundary_edge) - isDDof[1::2] = False - - F = dbc.check_vector(F) - uh = tensor_space.boundary_interpolate(gD=dirichlet, uh=uh, - threshold=is_dirichlet_boundary_edge) - uh[1::2] = 0 - F = F - K.matmul(uh[:]) - F[isDDof] = uh[isDDof] - - K = dbc.check_matrix(K) - kwargs = K.values_context() - indices = K.indices() - new_values = bm.copy(K.values()) - IDX = isDDof[indices[0, :]] | isDDof[indices[1, :]] - new_values[IDX] = 0 - - K = COOTensor(indices, new_values, K.sparse_shape) - index, = bm.nonzero(isDDof) - one_values = bm.ones(len(index), **kwargs) - one_indices = bm.stack([index, index], axis=0) - K1 = COOTensor(one_indices, one_values, K.sparse_shape) - K = K.add(K1).coalesce() - K_dense = K.to_dense() - - uh[:] = cg(K, F, maxiter=5000, atol=1e-14, rtol=1e-14) - - # Objective and sensitivity - c = 0 - cell2ldof = tensor_space.cell_to_dof() - uhe = uh[cell2ldof] - ce[:] = bm.einsum('ci, cik, ck -> c', uhe, KE, uhe) - c += bm.einsum('c, c -> ', E, ce) - - dE = material_model_SIMP_derivative(rho=rho_Phys, penal=penal, E0=1.0, Emin=1e-9) - dce[:] = bm.einsum('c, c -> c', dE, ce) - - dve[:] = bm.ones(NC, dtype=bm.float64) - - # Sensitivity filtering - # asarray 函数的作用是将输入数据转换为一个数组,它的一个重要特性是: - # 如果输入已经是一个数组, 它不会复制数据, 而是返回输入的原始数组. - # 这种行为可以节省内存和提高效率, 特别是在处理大数据时. - if ft == 0: - dce[:] = bm.asarray(H * (rho * dce)[bm.newaxis].T / Hs)[:, 0] / bm.maximum(0.001, rho) - elif ft == 1: - dce[:] = bm.asarray(H * (dce[bm.newaxis].T / Hs))[:, 0] - dve[:] = bm.asarray(H * (dve[bm.newaxis].T / Hs))[:, 0] - - # Optimality criteria - rho_old[:] = rho - rho, g = oc(rho=rho, dce=dce, dve=dve, g=g) - - # Filter design variables - if ft == 0: - rho_Phys[:] = rho - elif ft == 1: - rho_Phys[:] = bm.asarray(H * rho[bm.newaxis].T / Hs)[:, 0] - - # Compute the change by the inf. norm - change = bm.linalg.norm(rho.reshape(NC, 1) - rho_old.reshape(NC, 1), bm.inf) - - # Write iteration history to screen - print("it.: {0} , c.: {1:.3f} Vol.: {2:.3f}, ch.: {3:.3f}".format( - loop, c, (g + volfrac * NC) / NC, change)) diff --git a/app/soptx/simp/top_3d_1_0.py b/app/soptx/simp/top_3d_1_0.py deleted file mode 100644 index 2912cab8c..000000000 --- a/app/soptx/simp/top_3d_1_0.py +++ /dev/null @@ -1,224 +0,0 @@ -from fealpy.mesh import UniformMesh3d -from fealpy.functionspace import LagrangeFESpace, TensorFunctionSpace - -from fealpy.fem import LinearElasticityIntegrator, BilinearForm -from fealpy.fem import DirichletBC as DBC - -from fealpy.typing import TensorLike, Tuple -from builtins import float - -from fealpy.sparse import COOTensor - -from fealpy.solver import cg - -from fealpy.backend import backend_manager as bm - -# bm.set_backend('numpy') -# bm.set_backend('pytorch') -# bm.set_backend('jax') - -def material_model_SIMP(rho: TensorLike, penal: float, E0: float, - Emin: float=None) -> TensorLike: - if Emin is None: - E = rho ** penal * E0 - else: - E = Emin + rho ** penal * (E0 - Emin) - return E - -def material_model_SIMP_derivative(rho: TensorLike, penal: float, E0: float, - Emin: float=None) -> TensorLike: - if Emin is None: - dE = -penal * rho ** (penal - 1) * E0 - else: - dE = -penal * rho ** (penal - 1) * (E0 - Emin) - return dE - -# TODO 可以使用稀疏矩阵存储 -def compute_filter(rmin: int) -> Tuple[TensorLike, TensorLike]: - H = bm.zeros((NC, NC), dtype=bm.float64) - - # 确定哪些单元在滤波器半径范围内 - for i1 in range(nx): - for j1 in range(ny): - e1 = (i1) * ny + j1 - # 确定滤波器半径 rmin 的整数边界 - imin = max(i1 - (bm.ceil(rmin) - 1), 0.) - imax = min(i1 + (bm.ceil(rmin)), nx) - for i2 in range(int(imin), int(imax)): - jmin = max(j1 - (bm.ceil(rmin) - 1), 0.) - jmax = min(j1 + (bm.ceil(rmin)), ny) - for j2 in range(int(jmin), int(jmax)): - e2 = i2 * ny + j2 - H[e1, e2] = max(0., rmin - bm.sqrt((i1-i2)**2 + (j1-j2)**2)) - - Hs = bm.sum(H) - - return H, Hs - -# Optimality criterion -def oc(rho, dce, dve, g): - l1 = 0 - l2 = 1e9 - move = 0.2 - # reshape to perform vector operations - rho_new = bm.zeros(NC, dtype=bm.float64) - while (l2 - l1) / (l2 + l1)> 1e-3: - lmid = 0.5 * (l2 + l1) - rho_new[:] = bm.maximum(0.0, bm.maximum(rho-move, - bm.minimum(1.0, - bm.minimum(rho+move, rho*bm.sqrt(-dce/dve/lmid))))) - # 检查当前设计是否满足体积约束 - gt = g + bm.sum((dve * (rho_new - rho))) - if gt > 0 : - l1 = lmid - else: - l2 = lmid - - return rho_new, gt - - -def source(points: TensorLike) -> TensorLike: - - val = bm.zeros(points.shape, dtype=points.dtype) - val[nx*(ny+1)*(nz+1)+(nz+1):, 1] = -1 - - return val - -def dirichlet(points: TensorLike) -> TensorLike: - - return bm.zeros(points.shape, dtype=points.dtype) - -def is_dirichlet_boundary(points: TensorLike) -> TensorLike: - - return points[..., 0] == 0 - - -# Default input parameters -# nx = 60 -# ny = 20 -# nz = 4 -nx = 4 -ny = 1 -nz = 2 -volfrac = 0.3 -penal = 3.0 -rmin = 1.5 -ft = 0 # ft==0 -> sens, ft==1 -> dens - -extent = [0, nx, 0, ny, 0, nz] -h = [1, 1, 1] -origin = [0, 0, 0] -mesh = UniformMesh3d(extent, h, origin) - -import matplotlib.pyplot as plt -fig = plt.figure() -axes = fig.add_subplot(111, projection='3d') -mesh.add_plot(axes) -mesh.find_node(axes, showindex=True, color='r', fontsize=20) -# mesh.find_edge(axes, showindex=True, color='g', fontsize=25) -mesh.find_face(axes, showindex=True, color='r', fontsize=20) -mesh.find_cell(axes, showindex=True, color='b', fontsize=15) -plt.show() - -NC = mesh.number_of_cells() -p = 1 -space = LagrangeFESpace(mesh, p=p, ctype='C') -tensor_space = TensorFunctionSpace(space, shape=(-1, 3)) - -# Allocate design variables, initialize and allocate sens. -rho = volfrac * bm.ones(NC, dtype=bm.float64) -rho_old = rho.copy() -rho_Phys = rho.copy() -g = 0 # must be initialized to use the NGuyen/Paulino OC approach - -# element stiffness matrix -integrator_bi = LinearElasticityIntegrator(E=1.0, nu=0.3, coef=None, q=5) -KE = integrator_bi.assembly(space=tensor_space) - -# Filter -H, Hs = compute_filter(rmin) - -# Set loop counter and gradient vectors -loop = 0 -change = 1 -dve = bm.zeros(NC, dtype=bm.float64) -dce = bm.ones(NC, dtype=bm.float64) -ce = bm.ones(NC, dtype=bm.float64) -while change > 0.01 and loop < 2000: - loop += 1 - - # Setup and solve FE problem - uh = tensor_space.function() - E = material_model_SIMP(rho=rho_Phys, penal=penal, E0=1.0, Emin=1e-9) - integrator_bi = LinearElasticityIntegrator(E=1.0, nu=0.3, coef=E, q=5) - KK = integrator_bi.assembly(space=tensor_space) - bform = BilinearForm(tensor_space) - bform.add_integrator(integrator_bi) - K = bform.assembly() - - F = tensor_space.interpolate(source) - - dbc = DBC(space=tensor_space, gd=dirichlet, left=False) - isDDof = tensor_space.is_boundary_dof(threshold=is_dirichlet_boundary) - face2cell = mesh.face_to_cell() - - F = dbc.check_vector(F) - uh = tensor_space.boundary_interpolate(gD=dirichlet, uh=uh, threshold=is_dirichlet_boundary) - F = F - K.matmul(uh[:]) - F[isDDof] = uh[isDDof] - - K = dbc.check_matrix(K) - kwargs = K.values_context() - indices = K.indices() - new_values = bm.copy(K.values()) - IDX = isDDof[indices[0, :]] | isDDof[indices[1, :]] - new_values[IDX] = 0 - - K = COOTensor(indices, new_values, K.sparse_shape) - index, = bm.nonzero(isDDof) - one_values = bm.ones(len(index), **kwargs) - one_indices = bm.stack([index, index], axis=0) - K1 = COOTensor(one_indices, one_values, K.sparse_shape) - K = K.add(K1).coalesce() - K_dense = K.to_dense() - - uh[:] = cg(K, F, maxiter=5000, atol=1e-14, rtol=1e-14) - - # Objective and sensitivity - c = 0 - cell2ldof = tensor_space.cell_to_dof() - uhe = uh[cell2ldof] - ce[:] = bm.einsum('ci, cik, ck -> c', uhe, KE, uhe) - c += bm.einsum('c, c -> ', E, ce) - - dE = material_model_SIMP_derivative(rho=rho_Phys, penal=penal, E0=1.0, Emin=1e-9) - dce[:] = bm.einsum('c, c -> c', dE, ce) - - dve[:] = bm.ones(NC, dtype=bm.float64) - - # Sensitivity filtering - # asarray 函数的作用是将输入数据转换为一个数组,它的一个重要特性是: - # 如果输入已经是一个数组, 它不会复制数据, 而是返回输入的原始数组. - # 这种行为可以节省内存和提高效率, 特别是在处理大数据时. - if ft == 0: - dce[:] = bm.asarray(H * (rho * dce)[bm.newaxis].T / Hs)[:, 0] / bm.maximum(0.001, rho) - elif ft == 1: - dce[:] = bm.asarray(H * (dce[bm.newaxis].T / Hs))[:, 0] - dve[:] = bm.asarray(H * (dve[bm.newaxis].T / Hs))[:, 0] - - # Optimality criteria - rho_old[:] = rho - rho, g = oc(rho=rho, dce=dce, dve=dve, g=g) - - # Filter design variables - if ft == 0: - rho_Phys[:] = rho - elif ft == 1: - rho_Phys[:] = bm.asarray(H * rho[bm.newaxis].T / Hs)[:, 0] - - # Compute the change by the inf. norm - change = bm.linalg.norm(rho.reshape(NC, 1) - rho_old.reshape(NC, 1), bm.inf) - - # Write iteration history to screen - print("it.: {0} , c.: {1:.3f} Vol.: {2:.3f}, ch.: {3:.3f}".format( - loop, c, (g + volfrac * NC) / NC, change)) diff --git a/app/soptx/simp/utilfuncs.py b/app/soptx/simp/utilfuncs.py deleted file mode 100644 index 063e7c0c2..000000000 --- a/app/soptx/simp/utilfuncs.py +++ /dev/null @@ -1,33 +0,0 @@ -import jax.numpy as jnp - -class Mesher: - def getMeshStructure(self, mesh): - edofMat = jnp.zeros((mesh['nelx']*mesh['nely'], 8), dtype=int) - for elx in range(mesh['nelx']): - for ely in range(mesh['nely']): - el = ely + elx * mesh['nely'] - n1 = (mesh['nely'] + 1) * elx + ely - n2 = (mesh['nely'] + 1) * (elx + 1) + ely - edofMat = edofMat.at[el, :].set(jnp.array([2*n1+2, 2*n1+3, 2*n2+2, - 2*n2+3, 2*n2, 2*n2+1, 2*n1, 2*n1+1])) - iK = tuple(jnp.kron(edofMat, jnp.ones((8, 1))).flatten().astype(int)) - jK = tuple(jnp.kron(edofMat, jnp.ones((1, 8))).flatten().astype(int)) - idx = (iK, jK) - return edofMat, idx - - def getK0(self, matProp): - E = 1. - nu = matProp['nu'] - k = jnp.array([1/2-nu/6, 1/8+nu/8, -1/4-nu/12, -1/8+3*nu/8, \ - -1/4+nu/12, -1/8-nu/8, nu/6, 1/8-3*nu/8]) - KE = E / (1-nu**2) *\ - jnp.array([ [k[0], k[1], k[2], k[3], k[4], k[5], k[6], k[7]], - [k[1], k[0], k[7], k[6], k[5], k[4], k[3], k[2]], - [k[2], k[7], k[0], k[5], k[6], k[3], k[4], k[1]], - [k[3], k[6], k[5], k[0], k[7], k[2], k[1], k[4]], - [k[4], k[5], k[6], k[7], k[0], k[1], k[2], k[3]], - [k[5], k[4], k[3], k[2], k[1], k[0], k[7], k[6]], - [k[6], k[3], k[4], k[1], k[2], k[7], k[0], k[5]], - [k[7], k[2], k[1], k[4], k[3], k[6], k[5], k[0]] ]) - - return KE diff --git a/app/soptx/soptx/opt/oc.py b/app/soptx/soptx/opt/oc.py index 1542688ab..9f14c0cf7 100644 --- a/app/soptx/soptx/opt/oc.py +++ b/app/soptx/soptx/opt/oc.py @@ -130,7 +130,6 @@ def optimize(self, rho: TensorLike, **kwargs) -> TensorLike: # 二分法求解拉格朗日乘子 l1, l2 = 0.0, self.options.initial_lambda - print(f"rho: {bm.mean(rho[:]):.12f}") while (l2 - l1) / (l2 + l1) > bisection_tol: lmid = 0.5 * (l2 + l1) rho_new = self._update_density(rho, obj_grad, con_grad, lmid) @@ -156,7 +155,7 @@ def optimize(self, rho: TensorLike, **kwargs) -> TensorLike: iteration_time = time() - start_time print(f"Iteration: {iter_idx + 1}, " f"Objective: {obj_val:.3f}, " - f"Volume: {bm.mean(rho_new[:]):.12f}, " + f"Volume: {bm.mean(rho_phys[:]):.12f}, " f"Change: {change:.3f}, " f"Time: {iteration_time:.3f} sec") diff --git a/app/soptx/soptx/tests/test_oc.py b/app/soptx/soptx/tests/test_oc.py index c8bfe871d..dda35dfc8 100644 --- a/app/soptx/soptx/tests/test_oc.py +++ b/app/soptx/soptx/tests/test_oc.py @@ -33,6 +33,8 @@ def test_oc_optimizer(): ipoints_ordering='yx', flip_direction='y', device='cpu' ) + node = mesh.entity('node') + cell = mesh.entity('cell') # 创建函数空间 p = 1 @@ -143,6 +145,9 @@ def test_oc_optimizer(): # 运行优化 print("\n开始优化迭代...") rho_opt = oc_optimizer.optimize(rho=rho[:]) + mesh.celldata['density'] = rho_opt[:] + mesh.to_vtk(filename='/home/heliang/FEALPy_Development/fealpy/app/soptx/soptx/tests/density.vts') + # 输出优化结果统计 print("\n优化结果统计:") diff --git a/fealpy/mesh/uniform_mesh_2d.py b/fealpy/mesh/uniform_mesh_2d.py index a53f5f90d..0b16073f5 100644 --- a/fealpy/mesh/uniform_mesh_2d.py +++ b/fealpy/mesh/uniform_mesh_2d.py @@ -846,6 +846,78 @@ def uniform_refine(self, n: int=1): self.clear() + def to_vtk(self, filename, celldata=None, nodedata=None): + """ + @brief: Converts the mesh data to a VTK structured grid format and writes to a VTS file + """ + import vtk + from vtk.util.numpy_support import numpy_to_vtk + + if celldata is None: + celldata = self.celldata + + if nodedata is None: + nodedata = self.nodedata + + # 网格参数 + nx, ny = self.nx, self.ny + h = self.h + origin = self.origin + + # 创建坐标点 + x = bm.linspace(origin[0], origin[0] + nx * h[0], nx + 1) + y = bm.linspace(origin[1], origin[1] + ny * h[1], ny + 1) + z = bm.zeros(1) + + # 按 y, x 顺序重新组织坐标数组(左上到右下) + xy_x, xy_y = bm.meshgrid(x, y, indexing='ij') + xy_z = bm.zeros_like(xy_x) + + if self.flip_direction == 'y': + # 左上到右下 + yx_x = xy_x[:, ::-1].flatten() + yx_y = xy_y[:, ::-1].flatten() + yx_z = xy_z[:, ::-1].flatten() + else: + # 默认:左下到右上 + yx_x = xy_x.flatten() + yx_y = xy_y.flatten() + yx_z = xy_z.flatten() + + # 创建 VTK 网格对象 + rectGrid = vtk.vtkStructuredGrid() + rectGrid.SetDimensions(ny + 1, nx + 1, 1) + + # 创建点 + points = vtk.vtkPoints() + for i in range(len(yx_x)): + points.InsertNextPoint(yx_x[i], yx_y[i], yx_z[i]) + rectGrid.SetPoints(points) + + # 添加节点数据 + if nodedata is not None: + for name, data in nodedata.items(): + data_array = numpy_to_vtk(data, deep=True) + data_array.SetName(name) + rectGrid.GetPointData().AddArray(data_array) + + # 添加单元格数据 + if celldata is not None: + for name, data in celldata.items(): + data_array = numpy_to_vtk(data, deep=True) + data_array.SetName(name) + rectGrid.GetCellData().AddArray(data_array) + + # 写入 VTK 文件 + print("Writting to vtk...") + writer = vtk.vtkXMLStructuredGridWriter() + writer.SetInputData(rectGrid) + writer.SetFileName(filename) + writer.Write() + + return filename + + # 界面网格 def is_cut_cell(self, phi: Callable, *, eps=1e-10) -> TensorLike: """Return a bool tensor on cells indicating whether each cell is cut From 20f436fd2de52ae44560afbfb15d416494eed9f1 Mon Sep 17 00:00:00 2001 From: BenHBLiu Date: Tue, 19 Nov 2024 21:26:26 +0800 Subject: [PATCH 11/63] update --- app/PathPlanning/Grid_maps.py | 156 +++++++++++++++++++++++++++++ app/PathPlanning/main.py | 119 +++++++++++++++++++--- fealpy/opt/hippopotamus_opt_alg.py | 2 +- 3 files changed, 262 insertions(+), 15 deletions(-) create mode 100644 app/PathPlanning/Grid_maps.py diff --git a/app/PathPlanning/Grid_maps.py b/app/PathPlanning/Grid_maps.py new file mode 100644 index 000000000..b921a0965 --- /dev/null +++ b/app/PathPlanning/Grid_maps.py @@ -0,0 +1,156 @@ +from fealpy.backend import backend_manager as bm +from fealpy.opt.optimizer_base import opt_alg_options +import scipy.sparse as sp +import networkx as nx +from scipy.ndimage import label +from scipy.spatial.distance import pdist, squareform +import matplotlib.pyplot as plt + +# 二值栅格地图数据 +def grid_0(): + Map = bm.array([[0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0], + [1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0], + [0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0], + [0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1], + [1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1], + [0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0], + [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1], + [0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1], + [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1], + [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]) + return Map + + +MAP_data = [ + { + 'map': grid_0, + 'start': [0, 0], + 'goal': [19,19], + 'dim': 20, + }, +] + + +class GridProblem: + def __init__(self, MAP, start, goal): + self.MAP = MAP + self.start = start + self.goal = goal + self.data = {} + + def builddata(self): + self.data["R"] = 1 + self.data['map'] = self.MAP + L, _ = label(self.MAP) + + indices = bm.where(bm.array(L) > 0) + landmark = bm.concatenate([bm.array(L[i]) for i in indices]) + self.data['landmark'] = bm.array(landmark) + + node = [[j, i] for i in range(self.MAP.shape[0]) for j in range(self.MAP.shape[1]) if self.MAP[i, j] == 0] + self. data['node'] = bm.array(node) + + self.data['D'] = squareform(pdist(self.data['node'])) + + p1, p2 = bm.where(bm.array(self.data['D']) <= bm.array(self.data['R'])) + + D = self.data['D'][(p1, p2)].reshape(-1, 1) + self.data['net'] = sp.csr_matrix((D.flatten(), (p1, p2)), shape = self.data['D'].shape) + + self.data['noS'] = bm.where((self.data['node'][:, 0] == self.start[0]) & (self.data['node'][:, 1] == self.start[1]))[0][0] + self.data['noE'] = bm.where((self.data['node'][:, 0] == self.goal[0]) & (self.data['node'][:, 1] == self.goal[1]))[0][0] + self.data['numLM0'] = 1 + return self.data + + def fitness(self, X): + sorted_numbers = bm.argsort(X) + G = nx.DiGraph(self.data["net"]) + sorted_numbers_flat = sorted_numbers[:, :self.data['numLM0']] + noS = bm.full((X.shape[0],), self.data['noS']).reshape(-1, 1) + noE = bm.full((X.shape[0],), self.data['noE']).reshape(-1, 1) + path0 = bm.concatenate((noS, sorted_numbers_flat, noE), axis=1) + distances = bm.zeros((X.shape[0], path0.shape[1] - 1)) + for j in range(0, X.shape[0]): + distance = [nx.shortest_path_length(G, source=int(path0[j][i]), target=int(path0[j][i + 1]), weight=None) for i in range(path0.shape[1] - 1)] + distances[j, :] = bm.tensor(distance) + fit = bm.sum(distances, axis=1) + return fit + + def calresult(self, X): + result = {} + sorted_numbers = bm.argsort(X) + G = nx.DiGraph(self.data["net"]) + distances = [] + paths = [] + sorted_numbers_flat = sorted_numbers[0:self.data['numLM0']] + sorted_numbers_flat = [element for element in sorted_numbers_flat] + path0 = [self.data['noS']] + sorted_numbers_flat + [self.data['noE']] + for i in range(0, len(path0) - 1): + source = path0[i] + target = path0[i + 1] + source = int(source) + target = int(target) + path = nx.shortest_path(G, source = source, target = target) + distance = nx.shortest_path_length(G, source = source, target = target, weight = None) + distances.append(distance) + paths.append(path) + fit = sum(distances) + combined_list = [] + for sublist in paths: + combined_list.extend(sublist) + result["fit"] = fit + result["path"] = combined_list + return result + + def printMAP(self, result): + b = self.MAP.shape + self.MAP = 1 - self.MAP + + paths = {key: bm.array(value['path']) for key, value in result.items()} + result_paths = bm.array([paths[algo] for algo in paths]) + alg_name = list(result.keys()) + + for j in range(result_paths.shape[0]): + plt.scatter(self.start[0], self.start[1], color = 'blue', s = 100) + plt.scatter(self.goal[0], self.goal[1], color = 'green', s = 100) + + plt.imshow(self.MAP[::1], cmap = 'gray') + + xx = bm.linspace(0,b[1],b[1]) - 0.5 + yy = bm.zeros(b[1]) - 0.5 + for i in range(0, b[0]): + yy = yy + 1 + plt.plot(xx, yy,'-',color = 'black') + x = bm.zeros(b[0])-0.5 + y = bm.linspace(0,b[0],b[0])-0.5 + + for i in range(0, b[1]): + x = x + 1 + plt.plot(x,y,'-',color = 'black') + + xpath = self.data["node"][result_paths[j], 0] + ypath = self.data["node"][result_paths[j], 1] + plt.plot(xpath, ypath, '-', color = 'red') + plt.plot([xpath[-1], self.goal[0]], [ypath[-1], self.goal[1]], '-', color = 'red') + + plt.xticks([]) + plt.yticks([]) + plt.legend(loc='upper right') + plt.title(f'Optimal Paths from {alg_name[j]}') + plt.show() + + + + +if __name__ == "__main": + pass \ No newline at end of file diff --git a/app/PathPlanning/main.py b/app/PathPlanning/main.py index d9700a82b..2cd6fb226 100644 --- a/app/PathPlanning/main.py +++ b/app/PathPlanning/main.py @@ -6,8 +6,12 @@ from TSP_citys import TSP_data as TSPdata from TSP_citys import TravellingSalesmanProblem from TSP_citys import gbestroute, soler_tsp_with_algorithm, printroute +from Grid_maps import MAP_data as MAPdata +from Grid_maps import GridProblem + # bm.set_backend('pytorch') + class TSPOptimizerApp: def __init__(self, num, NP=130, lb=0, ub=1, MaxIters = 10000): self.num = num @@ -22,23 +26,28 @@ def __init__(self, num, NP=130, lb=0, ub=1, MaxIters = 10000): self.fobj = lambda x: self.test.fitness(x) self.optimizers = { - 'SAO': SnowmeltOptAlg, + 'SAO': SnowAblationOpt, 'COA': CrayfishOptAlg, - 'HBO': HoneybadgerOptAlg, - 'QPSO': QuantumParticleSwarmOptAlg, - 'PSO': ParticleSwarmOptAlg, + 'HBA': HoneybadgerAlg, + 'QPSO': QuantumParticleSwarmOpt, + 'PSO': ParticleSwarmOpt, 'GWO': GreyWolfOptimizer, 'ACO': AntColonyOptAlg, - 'Ho': HippopotamusOptAlg, + 'HO': HippopotamusOptAlg, 'CPO': CrestedPorcupineOpt, + 'BKA':BlackwingedKiteAlg, + 'BOA':ButterflyOptAlg, + 'CS':CuckooSearchOpt, + 'DE':DifferentialEvolution, + 'ETO':ExponentialTrigonometricOptAlg, } - self.results = {} def optimize(self): start_time = time.perf_counter() for algorithm, optimizer in self.optimizers.items(): + algo_start = time.perf_counter() if algorithm == 'ACO': NP = 10 MaxIters = 100 @@ -50,22 +59,28 @@ def optimize(self): gbest, gbest_f = soler_tsp_with_algorithm(optimizer, self.fobj, self.lb, self.ub, self.NP, self.citys.shape[0], self.MaxIters) gbest = bm.argsort(gbest) - if isinstance(gbest_f, (float, int)): - gbest_f = bm.array([gbest_f]) + # gbest_f = bm.array([gbest_f]) if isinstance(gbest_f, (float, int)) else gbest_f route, route_citys = gbestroute(gbest, self.citys) - self.results[algorithm] = {'route': route, 'route_citys': route_citys, 'gbest_f': gbest_f} + algo_end = time.perf_counter() + algo_runtime = algo_end - algo_start + + self.results[algorithm] = { + 'route': route, + 'route_citys': route_citys, + 'gbest_f': gbest_f, + 'time': algo_runtime} + end_time = time.perf_counter() running_time = end_time - start_time self.print_results() - print("Running time: ", running_time) + print("Total runtime: ", running_time) self.visualize_routes() - + def print_results(self): for algorithm, result in self.results.items(): - # print(f'The best solution obtained by {algorithm} is:', result['route']) - print(f'The best optimal value of the objective function found by {algorithm} is:', result['gbest_f']) + print(f"The best optimal value by {algorithm} is : {float(result['gbest_f']):.4f} Time: {result['time']:.4f} seconds") def visualize_routes(self): route_citys_all = bm.array([result['route_citys'].tolist() for result in self.results.values()]) @@ -73,7 +88,83 @@ def visualize_routes(self): printroute(route_citys_all, self.citys, alg_name) +class ShortestPathApp: + def __init__(self, num, NP=20, lb=0, ub=1, MaxIters = 50): + self.num = num + self.NP = NP + self.lb = lb + self.ub = ub + self.MaxIters = MaxIters + self.maps = MAPdata[self.num]['map']() + self.dim = self.maps.shape[0]^2 + self.start = MAPdata[self.num]['start'] + self.goal = MAPdata[self.num]['goal'] + + self.optimizers = { + 'SAO': SnowAblationOpt, + 'COA': CrayfishOptAlg, + 'HBA': HoneybadgerAlg, + 'QPSO': QuantumParticleSwarmOpt, + 'PSO': ParticleSwarmOpt, + 'GWO': GreyWolfOptimizer, + 'ACO': AntColonyOptAlg, + 'HO': HippopotamusOptAlg, + # 'CPO': CrestedPorcupineOpt, + 'BKA': BlackwingedKiteAlg, + 'BOA': ButterflyOptAlg, + 'CS': CuckooSearchOpt, + 'DE': DifferentialEvolution, + 'ETO': ExponentialTrigonometricOptAlg, + } + self.results = {} + + def optimize(self): + + if self.maps[self.start[0]][self.start[1]] != 0 or self.maps[self.goal[0]][self.goal[1]] != 0: + print("Error: Wrong start point or end point") + return + + textMAP = GridProblem(self.maps, self.start, self.goal) + textMAP.builddata() + D = textMAP.data['D'] + + fobj = lambda x: textMAP.fitness(x) + for algo_name, algorithm in self.optimizers.items(): + x0 = self.lb + bm.random.rand(self.NP, self.dim) * (self.ub - self.lb) + option = opt_alg_options(x0, fobj, (self.lb, self.ub), self.NP, self.MaxIters) + + + if algo_name == 'ACO': + optimizer = algorithm(option, D) + else: + optimizer = algorithm(option) + + start_time = time.perf_counter() + gbest, gbest_f = optimizer.run() + end_time = time.perf_counter() + running_time = end_time - start_time + print(gbest) + result = textMAP.calresult(gbest) + result["path"] = [x for x, y in zip(result["path"], result["path"][1:] + [None]) if x != y] + + self.results[algo_name] = { + "distance": gbest_f, + "path": result["path"], + "time": running_time + } + print(f'The best optimal value by {algo_name} is: {float(gbest_f):.4f} Time: {end_time - start_time:.4f} seconds') + + # textMAP.printMAP(self.results) + + + if __name__ == "__main__": - num = 2 # Example city index [0 - 6] + num = 0 + + #Traveling Salesman Problem tsp_optimizer = TSPOptimizerApp(num) tsp_optimizer.optimize() + + #Shortest path problem + # short_optimizer = ShortestPathApp(num) + # short_optimizer.optimize() \ No newline at end of file diff --git a/fealpy/opt/hippopotamus_opt_alg.py b/fealpy/opt/hippopotamus_opt_alg.py index bcdd12d85..02763ed73 100644 --- a/fealpy/opt/hippopotamus_opt_alg.py +++ b/fealpy/opt/hippopotamus_opt_alg.py @@ -130,4 +130,4 @@ def run(self): gbest_idx = bm.argmin(fit) (gbest, gbest_f) = (x[gbest_idx], fit[gbest_idx]) if fit[gbest_idx] < gbest_f else (gbest, gbest_f) # Convergence_curve[0, it] = bm.copy(gbest_f[0]) - return gbest, gbest_f + return gbest[0], gbest_f From 6e3776c106fb29e8291ff37e5d44f101e2313b9f Mon Sep 17 00:00:00 2001 From: Mihe0904 <1614827225@qq.com> Date: Thu, 21 Nov 2024 11:22:10 +0800 Subject: [PATCH 12/63] (mesh/.)triangle/hexahedron/quadrangle_mesh.uniform_refine and (test/.)triangle/hexahedron/quadrangle_mesh_data IM --- fealpy/mesh/hexahedron_mesh.py | 41 ++++++++- fealpy/mesh/quadrangle_mesh.py | 41 ++++++++- fealpy/mesh/triangle_mesh.py | 7 +- test/mesh/hexahedron_mesh_data.py | 144 +++++++++++++++++++++++++++++- test/mesh/quadrangle_mesh_data.py | 121 +++++++++++++++++++++++++ test/mesh/triangle_mesh_data.py | 81 ++++++++++++++++- use_test/corsen.py | 115 ++++++++++++++++++++++++ use_test/test_hexa.py | 79 ++++++++++++++++ use_test/test_quad.py | 77 ++++++++++++++++ use_test/test_t_data.py | 35 ++++++++ use_test/test_tetra.py | 21 +++++ use_test/test_tri.py | 51 +++++++++++ use_test/triangle_mesh_IM.py | 67 ++++++++++++++ 13 files changed, 872 insertions(+), 8 deletions(-) create mode 100644 use_test/corsen.py create mode 100644 use_test/test_hexa.py create mode 100644 use_test/test_quad.py create mode 100644 use_test/test_t_data.py create mode 100644 use_test/test_tetra.py create mode 100644 use_test/test_tri.py create mode 100644 use_test/triangle_mesh_IM.py diff --git a/fealpy/mesh/hexahedron_mesh.py b/fealpy/mesh/hexahedron_mesh.py index 8f8089e0a..c1bf95a20 100644 --- a/fealpy/mesh/hexahedron_mesh.py +++ b/fealpy/mesh/hexahedron_mesh.py @@ -6,6 +6,7 @@ from ..typing import TensorLike, Index, _S from .plot import Plotable +from scipy.sparse import coo_matrix, csc_matrix, csr_matrix class HexahedronMesh(TensorMesh, Plotable): def __init__(self, node, cell): @@ -234,10 +235,17 @@ def cell_to_ipoint(self, p, index=_S): return cell2ipoint[index] - def uniform_refine(self, n=1): + def uniform_refine(self, n=1, surface=None, interface=None, returnim=False): """ - @brief Uniformly refine the hexahedral mesh n times + @brief Uniform refine the hexahedron mesh n times. + + Parameters: + n (int): times refine the hexahedron mesh. + surface (function): the surface function. + returnim (bool): return the interpolation matrix or not. """ + if returnim is True: + IM = [] for i in range(n): NN = self.number_of_nodes() NE = self.number_of_edges() @@ -264,6 +272,32 @@ def uniform_refine(self, n=1): c2e = self.cell_to_edge() + NN c2f = self.cell_to_face() + (NN + NE) c2c = bm.arange(NC, device=bm.get_device(cell)) + (NN + NE + NF) + edge2node = self.edge_to_node() + face2node = self.face_to_node() + cell2node = self.cell_to_node() + if returnim is True: + new_node_num = NN+NE+NF+NC + nonzeros = NN+2*NE+4*NF+8*NC + data = bm.zeros(nonzeros,dtype=bm.float64) + data[:NN] = 1 + data[NN:NN+2*NE] = 1/2 + data[NN+2*NE:NN+2*NE+4*NE] = 1/4 + data[NN+2*NE+4*NF:] = 1/8 + + indices = bm.zeros(nonzeros,dtype=bm.int32) + indices[:NN] = bm.arange(NN) + indices[NN:NN+2*NE] = edge2node.flatten() + indices[NN+2*NE:NN+2*NE+4*NF] = face2node.flatten() + indices[NN+2*NE+4*NF:] = cell2node.flatten() + + indptr = bm.zeros(new_node_num+1,dtype=bm.int32) + indptr[:NN+1] = bm.arange(NN+1) + indptr[NN+1:NN+NE+1]=bm.arange(NN+2,NN+2*NE+1,step=2) + indptr[NN+NE+1:NN+NE+NF+1] = bm.arange(NN+2*NE+4,NN+2*NE+4*NF+1,step=4) + indptr[NN+NE+NF+1:] = bm.arange(NN+2*NE+4*NF+8,nonzeros+1,step=8) + + A = csr_matrix((data,indices,indptr),dtype=bm.float64) + IM.append(A) cell[0::8, 0] = c2n[:, 0] cell[0::8, 1] = c2e[:, 0] @@ -341,6 +375,9 @@ def uniform_refine(self, n=1): self.cell = cell self.construct() + if returnim is True: + return IM + @classmethod def from_one_hexahedron(cls, twist=False): diff --git a/fealpy/mesh/quadrangle_mesh.py b/fealpy/mesh/quadrangle_mesh.py index a8051fa85..952dc162a 100644 --- a/fealpy/mesh/quadrangle_mesh.py +++ b/fealpy/mesh/quadrangle_mesh.py @@ -9,6 +9,10 @@ from .plot import Plotable +from scipy.sparse import coo_matrix, csc_matrix, csr_matrix +from scipy.sparse import spdiags, eye, tril, triu, bmat + + class QuadrangleMesh(TensorMesh, Plotable): def __init__(self, node, cell): """ @@ -336,14 +340,45 @@ def reorder_cell(self, idx): # cell = cell[bm.arange(NC).reshape(-1, 1), self.localCell[idx]] # self.ds.reinit(NN, cell) - def uniform_refine(self, n:int=1) -> 'QuadrangleMesh': + def uniform_refine(self, n=1, surface=None, interface=None, returnim=False) -> 'QuadrangleMesh': """ - @brief Uniformly refine the quadrilateral mesh + Uniform refine the triangle mesh n times. + + Parameters: + n (int): times refine the triangle mesh. + surface (function): the surface function. + returnim (bool): return the interpolation matrix or not. """ + if returnim is True: + IM = [] for i in range(n): NN = self.number_of_nodes() NE = self.number_of_edges() NC = self.number_of_cells() + edge2node = self.edge_to_node() + cell2node = self.cell_to_node() + if returnim is True: + nonzeros = NN+2*NE+4*NC + num_new_node = NN+NE+NC + + data = bm.zeros(nonzeros,dtype=bm.float64) + indices = bm.zeros(nonzeros,dtype=bm.int32) + indptr = bm.zeros(num_new_node+1,dtype=bm.int32) + + #赋值 + data[:NN] = 1 + data[NN:NN+2*NE] = 1/2 + data[NN+2*NE:] = 1/4 + + indices[:NN] = bm.arange(NN) + indices[NN:NN+2*NE] = edge2node.flatten() + indices[NN+2*NE:] = cell2node.flatten() + + indptr[:NN+1] = bm.arange(NN+1) + indptr[NN+1:NN+NE+1]=bm.arange(NN+2,NN+2*NE+1,step=2) + indptr[NN+NE+1:] = bm.arange(NN+2*NE+4,NN+2*NE+4*NC+1,step=4) + A = csr_matrix((data,indices,indptr),dtype=bm.float64) + IM.append(A) # Find the cutted edge cell2edge = self.cell2edge @@ -393,6 +428,8 @@ def uniform_refine(self, n:int=1) -> 'QuadrangleMesh': self.cell = cell self.construct() + if returnim is True: + return IM def vtk_cell_type(self, etype='cell'): if etype in {'cell', 2}: diff --git a/fealpy/mesh/triangle_mesh.py b/fealpy/mesh/triangle_mesh.py index b83e71362..17cf3b4a0 100644 --- a/fealpy/mesh/triangle_mesh.py +++ b/fealpy/mesh/triangle_mesh.py @@ -291,10 +291,12 @@ def uniform_refine(self, n=1, surface=None, interface=None, returnim=False): Parameters: n (int): times refine the triangle mesh. surface (function): the surface function. - returnim (bool): return the interpolation matrix or not. + returnim (bool): return the interpolation matrix or not. """ if returnim is True: IM = [] + if returnrm is True: + RM = [] for i in range(n): NN = self.number_of_nodes() @@ -315,7 +317,10 @@ def uniform_refine(self, n=1, surface=None, interface=None, returnim=False): shape=(NN + NE, NN)) A += coo_matrix((0.5 * bm.ones(NE, dtype=self.ftype), (bm.arange(NN, NN + NE), edge[:, 1])), shape=(NN + NE, NN)) + IM.append(A.tocsr()) + + self.node = bm.concatenate((node, newNode), axis=0) p = bm.concatenate((cell, edge2newNode[cell2edge]), axis=1) diff --git a/test/mesh/hexahedron_mesh_data.py b/test/mesh/hexahedron_mesh_data.py index b8fb7c84f..5cfd89c1c 100644 --- a/test/mesh/hexahedron_mesh_data.py +++ b/test/mesh/hexahedron_mesh_data.py @@ -2045,7 +2045,149 @@ [119, 185, 196, 130, 120, 186, 197, 131]], dtype=np.int64), }, ] - +interpolation_matrix_data = [ + "node": np.array([[0. 0. 0.],[1. 0. 0.],[1. 1. 0.],[0. 1. 0.], + [0. 0. 1.],[1. 0. 1.],[1. 1. 1.],[0. 1. 1.]], dtype=np.float64), + "edge": np.array([[0 1],[0 3],[0 4],[1 2],[1 5],[2 3],[2 6], + [3 7],[4 5],[4 7],[5 6],[6 7]], dtype=np.int32), + "face": np.array([[0 3 2 1],[0 1 5 4],[0 4 7 3], + [1 2 6 5],[2 3 7 6],[4 5 6 7]], dtype=np.int32), + "cell": np.array([[0 ,1, 2, 3, 4, 5, 6, 7]], dtype=np.int32), + { + + "n":2 + "func":2*node[:,0]+2*node[:,1]+2*node[:,2]+1 + "IM": + + Coords Values + (0, 0) 1.0 + (1, 1) 1.0 + (2, 2) 1.0 + (3, 3) 1.0 + (4, 4) 1.0 + (5, 5) 1.0 + (6, 6) 1.0 + (7, 7) 1.0 + (8, 0) 0.5 + (8, 1) 0.5 + (9, 0) 0.5 + (9, 3) 0.5 + (10, 0) 0.5 + (10, 4) 0.5 + (11, 1) 0.5 + (11, 2) 0.5 + (12, 1) 0.5 + (12, 5) 0.5 + (13, 2) 0.5 + (13, 3) 0.5 + (14, 2) 0.5 + (14, 6) 0.5 + (15, 3) 0.5 + (15, 7) 0.5 + (16, 4) 0.5 + : : + (21, 4) 0.25 + (22, 0) 0.25 + (22, 4) 0.25 + (22, 7) 0.25 + (22, 3) 0.25 + (23, 1) 0.25 + (23, 2) 0.25 + (23, 6) 0.25 + (23, 5) 0.25 + (24, 2) 0.25 + (24, 3) 0.25 + (24, 7) 0.25 + (24, 6) 0.25 + (25, 4) 0.25 + (25, 5) 0.25 + (25, 6) 0.25 + (25, 7) 0.25 + (26, 0) 0.125 + (26, 1) 0.125 + (26, 2) 0.125 + (26, 3) 0.125 + (26, 4) 0.125 + (26, 5) 0.125 + (26, 6) 0.125 + (26, 7) 0.125 + + Coords Value + (0, 0) 1.0 + (1, 1) 1.0 + (2, 2) 1.0 + (3, 3) 1.0 + (4, 4) 1.0 + (5, 5) 1.0 + (6, 6) 1.0 + (7, 7) 1.0 + (8, 8) 1.0 + (9, 9) 1.0 + (10, 10) 1.0 + (11, 11) 1.0 + (12, 12) 1.0 + (13, 13) 1.0 + (14, 14) 1.0 + (15, 15) 1.0 + (16, 16) 1.0 + (17, 17) 1.0 + (18, 18) 1.0 + (19, 19) 1.0 + (20, 20) 1.0 + (21, 21) 1.0 + (22, 22) 1.0 + (23, 23) 1.0 + (24, 24) 1.0 + : : + (121, 21) 0.125 + (122, 5) 0.125 + (122, 16) 0.125 + (122, 25) 0.125 + (122, 18) 0.125 + (122, 12) 0.125 + (122, 21) 0.125 + (122, 26) 0.125 + (122, 23) 0.125 + (123, 6) 0.125 + (123, 18) 0.125 + (123, 25) 0.125 + (123, 19) 0.125 + (123, 14) 0.125 + (123, 23) 0.125 + (123, 26) 0.125 + (123, 24) 0.125 + (124, 7) 0.125 + (124, 19) 0.125 + (124, 25) 0.125 + (124, 17) 0.125 + (124, 15) 0.125 + (124, 24) 0.125 + (124, 26) 0.125 + (124, 22) 0.125 + "excat_value": + [[1. 3. 5. 3. 3. 5. 7. 5. 2. 2. 2. 4. 4. 4. 6. 4. 4. 4. + 6. 6. 3. 3. 3. 5. 5. 5. 4. 1.5 1.5 1.5 2.5 3.5 3.5 4.5 4.5 5.5 + 2.5 3.5 3.5 2.5 3.5 3.5 4.5 4.5 5.5 6.5 6.5 6.5 4.5 4.5 5.5 2.5 2.5 2.5 + 2.5 2.5 2.5 3.5 4.5 3.5 4.5 3.5 4.5 5.5 5.5 3.5 4.5 3.5 4.5 3.5 4.5 5.5 + 5.5 5.5 5.5 3.5 3.5 3.5 4.5 4.5 4.5 2. 2. 2. 3. 3. 4. 4. 5. 5. + 3. 3. 4. 3. 3. 4. 4. 5. 5. 6. 6. 6. 4. 5. 5. 3. 3. 3. + 4. 4. 4. 5. 4. 4. 4. 5. 5. 2.5 3.5 4.5 3.5 3.5 4.5 5.5 4.5], + dtype = np.float64] + "test_value": + [[1. 3. 5. 3. 3. 5. 7. 5. 2. 2. 2. 4. 4. 4. 6. 4. 4. 4. + 6. 6. 3. 3. 3. 5. 5. 5. 4. 1.5 1.5 1.5 2.5 3.5 3.5 4.5 4.5 5.5 + 2.5 3.5 3.5 2.5 3.5 3.5 4.5 4.5 5.5 6.5 6.5 6.5 4.5 4.5 5.5 2.5 2.5 2.5 + 2.5 2.5 2.5 3.5 4.5 3.5 4.5 3.5 4.5 5.5 5.5 3.5 4.5 3.5 4.5 3.5 4.5 5.5 + 5.5 5.5 5.5 3.5 3.5 3.5 4.5 4.5 4.5 2. 2. 2. 3. 3. 4. 4. 5. 5. + 3. 3. 4. 3. 3. 4. 4. 5. 5. 6. 6. 6. 4. 5. 5. 3. 3. 3. + 4. 4. 4. 5. 4. 4. 4. 5. 5. 2.5 3.5 4.5 3.5 3.5 4.5 5.5 4.5], + dtype = np.float64] + "is_excat_close_test":True + } + +] diff --git a/test/mesh/quadrangle_mesh_data.py b/test/mesh/quadrangle_mesh_data.py index d14fd8b8d..0dd7fa411 100644 --- a/test/mesh/quadrangle_mesh_data.py +++ b/test/mesh/quadrangle_mesh_data.py @@ -438,3 +438,124 @@ ], "domain_boundary": np.array([0, 1, 2, 3]), }] + +interpolation_matrix_data = [ + "node": np.array([[0. 0.],[1. 0.],[1. 1.],[0. 1.]], dtype=np.float64), + "edge": np.array([[0, 1], [3, 0], [1, 2], [2, 3]], dtype=np.int32), + "cell": np.array([[0, 1, 2, 3]], dtype=np.int32), + { + "n":1 + "func":node[:,0]+2*node[:,1]+3 + "IM": + + Coords Values + (0, 0) 1.0 + (1, 1) 1.0 + (2, 2) 1.0 + (3, 3) 1.0 + (4, 0) 0.5 + (4, 1) 0.5 + (5, 3) 0.5 + (5, 0) 0.5 + (6, 1) 0.5 + (6, 2) 0.5 + (7, 2) 0.5 + (7, 3) 0.5 + (8, 0) 0.25 + (8, 1) 0.25 + (8, 2) 0.25 + (8, 3) 0.25 + + "excat_value": + [[3. 4. 6. 5. 3.5 4. 5. 5.5 4.5], + dtype = np.float64] + "test_value": + [[3. 4. 6. 5. 3.5 4. 5. 5.5 4.5], + dtype = np.float64] + "is_excat_close_test":True + } + { + "n":3 + "func":2*node[:,0]+2*node[:,1]+2 + "IM": + + Coords Values + (0, 0) 1.0 + (1, 1) 1.0 + (2, 2) 1.0 + (3, 3) 1.0 + (4, 0) 0.5 + (4, 1) 0.5 + (5, 3) 0.5 + (5, 0) 0.5 + (6, 1) 0.5 + (6, 2) 0.5 + (7, 2) 0.5 + (7, 3) 0.5 + (8, 0) 0.25 + (8, 1) 0.25 + (8, 2) 0.25 + (8, 3) 0.25 + + Coords Values + (0, 0) 1.0 + (1, 1) 1.0 + (2, 2) 1.0 + (3, 3) 1.0 + (4, 4) 1.0 + (5, 5) 1.0 + (6, 6) 1.0 + (7, 7) 1.0 + (8, 8) 1.0 + (9, 0) 0.5 + (9, 4) 0.5 + (10, 5) 0.5 + (10, 0) 0.5 + (11, 4) 0.5 + (11, 1) 0.5 + (12, 1) 0.5 + (12, 6) 0.5 + (13, 6) 0.5 + (13, 2) 0.5 + (14, 2) 0.5 + (14, 7) 0.5 + (15, 3) 0.5 + (15, 5) 0.5 + (16, 7) 0.5 + (16, 3) 0.5 + (17, 4) 0.5 + (17, 8) 0.5 + (18, 8) 0.5 + (18, 5) 0.5 + (19, 6) 0.5 + (19, 8) 0.5 + (20, 7) 0.5 + (20, 8) 0.5 + (21, 0) 0.25 + (21, 4) 0.25 + (21, 8) 0.25 + (21, 5) 0.25 + (22, 4) 0.25 + (22, 1) 0.25 + (22, 6) 0.25 + (22, 8) 0.25 + (23, 8) 0.25 + (23, 6) 0.25 + (23, 2) 0.25 + (23, 7) 0.25 + (24, 5) 0.25 + (24, 8) 0.25 + (24, 7) 0.25 + (24, 3) 0.25 + "excat_value": + [[2. 4. 6. 4. 3. 3. 5. 5. 4. 2.5 2.5 3.5 4.5 5.5 5.5 3.5 4.5 3.5 + 3.5 4.5 4.5 3. 4. 5. 4. ], + dtype = np.float64] + "test_value": + [[2. 4. 6. 4. 3. 3. 5. 5. 4. 2.5 2.5 3.5 4.5 5.5 5.5 3.5 4.5 3.5 + 3.5 4.5 4.5 3. 4. 5. 4. ], + dtype = np.float64] + "is_excat_close_test":True + } + + ] \ No newline at end of file diff --git a/test/mesh/triangle_mesh_data.py b/test/mesh/triangle_mesh_data.py index c5d5e17c6..18eedfb8d 100644 --- a/test/mesh/triangle_mesh_data.py +++ b/test/mesh/triangle_mesh_data.py @@ -2791,5 +2791,82 @@ ] interpolation_matrix_data = [ - - ] + "node": np.array([[0, 0], [1, 0], [0, 1]], dtype=np.float64), + "edge": np.array([[0, 1], [2, 0], [1, 2]], dtype=np.int32), + "cell": np.array([[0, 1, 2]], dtype=np.int32), + { + "n":1 + "func":node[:,0]+2*node[:,1]+3 + "IM": + + Coords Values + (0, 0) 1.0 + (1, 1) 1.0 + (2, 2) 1.0 + (3, 0) 0.5 + (3, 1) 0.5 + (4, 0) 0.5 + (4, 2) 0.5 + (5, 1) 0.5 + (5, 2) 0.5 + "excat_value": + [[3. 4. 5. 3.5 4. 4.5], + dtype = np.float64] + "test_value": + [[3. 4. 5. 3.5 4. 4.5], + dtype = np.float64] + "is_excat_close_test":True + } + { + "n":3 + "func":2*node[:,0]+2*node[:,1]+2 + "IM": + + Coords Values + (0, 0) 1.0 + (1, 1) 1.0 + (2, 2) 1.0 + (3, 0) 0.5 + (3, 1) 0.5 + (4, 0) 0.5 + (4, 2) 0.5 + (5, 1) 0.5 + (5, 2) 0.5 + + Coords Values + (0, 0) 1.0 + (1, 1) 1.0 + (2, 2) 1.0 + (3, 3) 1.0 + (4, 4) 1.0 + (5, 5) 1.0 + (6, 0) 0.5 + (6, 3) 0.5 + (7, 0) 0.5 + (7, 4) 0.5 + (8, 1) 0.5 + (8, 3) 0.5 + (9, 1) 0.5 + (9, 5) 0.5 + (10, 2) 0.5 + (10, 4) 0.5 + (11, 2) 0.5 + (11, 5) 0.5 + (12, 3) 0.5 + (12, 4) 0.5 + (13, 3) 0.5 + (13, 5) 0.5 + (14, 4) 0.5 + (14, 5) 0.5 + "excat_value": + [[2. 4. 4. 3. 3. 4. 2.5 2.5 3.5 4. 3.5 4. 3. 3.5 3.5], + dtype = np.float64] + "test_value": + [[2. 4. 4. 3. 3. 4. 2.5 2.5 3.5 4. 3.5 4. 3. 3.5 3.5], + dtype = np.float64] + "is_excat_close_test":True + } +] \ No newline at end of file diff --git a/use_test/corsen.py b/use_test/corsen.py new file mode 100644 index 000000000..ca062075c --- /dev/null +++ b/use_test/corsen.py @@ -0,0 +1,115 @@ +from fealpy.mesh import TriangleMesh +from fealpy.backend import backend_manager as bm +from fealpy.sparse import CSRTensor +import matplotlib.pyplot as plt +from sympy import symbols, Eq, solve + +#生成三角形 +node = bm.tensor([[0,0],[1,0],[0,1]],dtype=bm.float64) +cell=bm.tensor([[0,1,2]],dtype=bm.int32) +mesh = TriangleMesh(node, cell) +mesh.uniform_refine() + +#确定粗网格节点 +cell_h = mesh.number_of_cells() +cell_H = 0.25*cell_h +n_1 = symbols('n_1') +equation = Eq(n_1**2, cell_H) +solutions = solve(equation, n_1) +n_1 = bm.int32(solutions[1])+1 +node_H = int((n_1**2+n_1)/2) +node_h = mesh.number_of_nodes() +node_new = mesh.entity('node')[:node_H] + +edge2node = mesh.edge_to_node() + +def Resriction_matrix(mesh,row1,row2): + valence = bm.zeros(node_h, dtype=mesh.itype, device=mesh.device) + bm.add_at(valence, edge2node, 1) + row3 = node_H+bm.sum(valence[0:node_H]) + + #indptr + indptr = bm.zeros(row1+1,dtype=bm.int32) + indptr[0]= 1 + for i in range(1,row1+1): + indptr[i] = indptr[i-1]+1+valence[i-1] + + #indices + indices = bm.zeros(row3,dtype=bm.int32) + for k in range(row1): + t_list = [] + for element in edge2node: + if k in element: + other_number = [x for x in element if x != k] + t_list.append(other_number) + t_list.sort() + t = bm.array(t_list) + indices[indptr[k]:indptr[k+1]-1] = t.flatten() + indices[indptr[k]-1] = k + + data = bm.zeros(row3,dtype=bm.float64) + for i in range(row1): + print(indptr[i+1]-indptr[i]) + data[indptr[i]-1] = 2/(indptr[i+1]-indptr[i]+1) + data[indptr[i]:indptr[i+1]-1] = 1/(indptr[i+1]-indptr[i]+1) + + R = CSRTensor(indptr,indices,data) + return R + +R = Resriction_matrix(mesh,node_H,node_h) + +# # 求解方程 +# sol = sp.solve([eq], [0]) +# edge = mesh.entity('edge') +# edge2node = mesh.edge_to_node() +# NE = mesh.number_of_edges() + +# for i in range(NE): +# if (node[edge[i],:][0][0]!=node[edge[i],:][1][0]) and (node[edge[i],:][0][1]!=node[edge[i],:][1][1]): +# line[i]=(node[edge[i],:][0][0]-node[edge[i],:][1][0])/(node[edge[i],:][0][1]-node[edge[i],:][1][1]) +# isGEdge = (line!=0) +# print(edge[isGEdge]) + +# cell2edge = mesh.cell_to_edge() +# GEdge= bm.zeros(NE, dtype=mesh.itype, device=mesh.device) +# bm.add_at(GEdge, cell2edge, 1) +# print(GEdge) +# isGEdge = (GEdge==2) +# print(edge[isGEdge]) + + +# GNode = bm.zeros(NE, dtype=mesh.itype, device=mesh.device) +# bm.add_at(GNode, cell2edge, 1) +# print(GNode) +# isGNode = (GNode != 0) +# print(isGNode) +# print(node[isGNode]) + +# print(mesh.cell_to_node()) +# valence = bm.zeros(NN, dtype=mesh.itype, device=mesh.device) +# bm.add_at(valence, cell, 1) +# isBNode = (valence==1) +# BNode = node[isBNode] +# print(BNode) + +print(f"限制矩阵为:{R}") +print(f"粗化新坐标点:{node_new}") +def linear_function(coodrs): + return coodrs[:,0]*coodrs[:,0] + 2*coodrs[:,1]+3 +value_check = linear_function(mesh.node) +value_new = R.matmul(value_check) +print(f"限制矩阵下的插值结果:{value_new}") +print(f"精确值:{linear_function(node_new)}") + + +fig = plt.figure() +axes = fig.gca() +mesh.add_plot(axes) +mesh.find_node(axes, showindex=True) +mesh.find_edge(axes, showindex=True) +plt.show() + +# try_1 = bm.tensor([3.875,4.5,2.125],dtype = bm.float64) +# try_2 = CSRTensor(crow=bm.tensor([0 ,1 ,2 ,3 ,5 ,7 ,9]), col=bm.tensor([0 ,1 ,2 ,0 ,1 ,2 ,0 ,1 ,2]), +# values=bm.tensor([1. ,1. ,1. ,0.5 ,0.5 ,0.5 ,0.5 ,0.5 ,0.5])) +# print(f"check:{try_2.matmul(try_1)}") \ No newline at end of file diff --git a/use_test/test_hexa.py b/use_test/test_hexa.py new file mode 100644 index 000000000..fec03255d --- /dev/null +++ b/use_test/test_hexa.py @@ -0,0 +1,79 @@ +from fealpy.mesh import HexahedronMesh +import numpy as np +from scipy.sparse import csr_matrix +import matplotlib.pyplot as plt +from mpl_toolkits.mplot3d import Axes3D + +mesh = HexahedronMesh.from_one_hexahedron() +node = mesh.entity('node') +edge = mesh.entity('edge') +face = mesh.entity('face') +cell = mesh.entity('cell') +print(node) +print(edge) +print(face) +print(cell) + +# def Prolongation_matrix(mesh,numnode,numedge,numface,numcell): +# new_node_num = numnode+numedge+numface+numcell +# nonzeros = numnode+2*numedge+4*numface+8*numcell +# data = np.zeros(nonzeros,dtype=np.float64) +# data[:numnode] = 1 +# data[numnode:numnode+2*numedge] = 1/2 +# data[2*numedge:numnode+2*numedge+4*numface] = 1/4 +# data[numnode+2*numedge+4*numface:] = 1/8 + +# indices = np.zeros(nonzeros,dtype=np.int32) +# indices[:numnode] = np.arange(numnode) +# indices[numnode:numnode+2*numedge] = edge2node.flatten() +# indices[numnode+2*numedge:numnode+2*numedge+4*numface] = face2node.flatten() +# indices[numnode+2*numedge+4*numface:] = cell2node.flatten() + +# indptr = np.zeros(new_node_num+1,dtype=np.int32) +# indptr[:numnode+1] = np.arange(numnode+1) +# indptr[numnode+1:numnode+numedge+1]=np.arange(numnode+2,numnode+2*numedge+1,step=2) +# indptr[numnode+numedge+1:numnode+numedge+numface+1] = np.arange(numnode+2*numedge+4,numnode+2*numedge+4*numface+1,step=4) +# indptr[numnode+numedge+numface+1:] = np.arange(numnode+2*numedge+4*numface+8,nonzeros+1,step=8) + +# A = csr_matrix((data,indices,indptr),dtype=np.float64) +# return A + +# for i in np.arange(n): +# ''' +# n:加密次数 +# ''' +# edge2node = mesh.edge_to_node() +# face2node = mesh.face_to_node() +# cell2node = mesh.cell_to_node() +# NN = mesh.number_of_nodes() +# NE = mesh.number_of_edges() +# NF = mesh.number_of_faces() +# NC= mesh.number_of_cells() +# mesh.uniform_refine() + +# A = Prolongation_matrix(mesh,NN,NE,NF,NC) +# IM.append(A) + +# print(IM[1]) + +n = 2 +A = mesh.uniform_refine(n,returnim=True) + +def linear_function(coodrs): + return 2*coodrs[:,0] + 2*coodrs[:,1]+2*coodrs[:,2]+1 +excat_value = linear_function(mesh.node) +val = linear_function(node) +test_value = val +for i in range(n): + print(A[i]) + test_value = A[i].dot(test_value) +print(excat_value) +print(test_value) + +# fig = plt.figure(1) +# axes = fig.add_subplot(111, projection='3d') +# mesh.add_plot(axes) +# plt.title("Grid Image") +# mesh.find_node(axes, showindex=True) +# mesh.find_edge(axes, showindex=True) +# plt.show() \ No newline at end of file diff --git a/use_test/test_quad.py b/use_test/test_quad.py new file mode 100644 index 000000000..82eb9f296 --- /dev/null +++ b/use_test/test_quad.py @@ -0,0 +1,77 @@ +from fealpy.mesh import QuadrangleMesh +from fealpy.backend import backend_manager as bm +from fealpy.sparse import CSRTensor +import matplotlib.pyplot as plt + +mesh = QuadrangleMesh.from_one_quadrangle() +node = mesh.entity('node') +edge = mesh.entity('edge') +cell = mesh.entity('cell') +print(node) +print(edge) +print(cell) + +n=2 +A = mesh.uniform_refine(n,returnim=True) +print(A[0],A[1]) +def linear_function(coodrs): + return 2*coodrs[:,0] + 2*coodrs[:,1]+2 +excat_value = linear_function(mesh.node) +val = linear_function(node) +test_value = val +for i in range(n): + test_value = A[i].dot(test_value) +print(excat_value) +print(test_value) + +# def Prolongation_matrix(mesh,numnode,numedge,numcell): +# ''' +# l:the nodes of new mesh +# ''' + +# l = numnode+numedge+numcell + +# data = bm.zeros(numnode+2*numedge+4*numcell,dtype=bm.float64) +# indices = bm.zeros(numnode+2*numedge+4*numcell,dtype=bm.int32) +# indptr = bm.zeros(l+1,dtype=bm.int32) + +# #赋值 +# data[:numnode] = 1 +# data[numnode:numnode+2*numedge] = 1/2 +# data[numnode+2*numedge:] = 1/4 + +# indices[:numnode] = bm.arange(numnode) +# indices[numnode:numnode+2*numedge] = edge2node.flatten() +# indices[numnode+2*numedge:] = cell2node.flatten() + +# indptr[:numnode+1] = bm.arange(numnode+1) +# indptr[numnode+1:numnode+numedge+1]=bm.arange(numnode+2,numnode+2*numedge+1,step=2) +# indptr[numnode+numedge+1:] = bm.arange(numnode+2*numedge+4,numnode+2*numedge+4*numcell+1,step=4) +# A = CSRTensor(indptr,indices,data) +# return A + + +# A = Prolongation_matrix(mesh,node_old,edge_old,cell_old) +# print(f"加密一次后的插值矩阵是:{A}") + +# #带入线性函数验证 +# def linear_function(coords): +# return 5*coords[:, 0] + 4*coords[:, 1] + 3 +# #计算函数在插值点数值 +# value_check = linear_function(node2) +# #运用插值矩阵生成加密后的插值点函数值 +# value_old = linear_function(node1) +# value_new = A.matmul(value_old) +# print(f"真函数值:{value_check}") +# print(f"加密后的插值点函数值:{value_new}") +# are_close = bm.allclose(value_check, value_new) +# print(f"是否为插值矩阵(考虑精度):{are_close}\n") + +# fig = plt.figure() +# axes = fig.gca() +# mesh.add_plot(axes) +# mesh.find_node(axes, showindex=True) +# mesh.find_edge(axes, showindex=True) +# mesh.find_cell(axes, showindex=True) +# print(mesh.number_of_nodes()) +# plt.show() diff --git a/use_test/test_t_data.py b/use_test/test_t_data.py new file mode 100644 index 000000000..bbe4e1c63 --- /dev/null +++ b/use_test/test_t_data.py @@ -0,0 +1,35 @@ +from fealpy.mesh import TriangleMesh +from fealpy.backend import backend_manager as bm +from fealpy.sparse.coo_tensor import COOTensor +import matplotlib.pyplot as plt + +node = bm.tensor([[0,0],[1,0],[0,1]],dtype=bm.float64) +cell=bm.tensor([[0,1,2]],dtype=bm.int32) +mesh = TriangleMesh(node,cell) +mesh.uniform_refine() +mesh.uniform_refine() +numcell = mesh.number_of_cells() +cell_new = bm.ones(numcell) +isMarkedCell = (cell_new == 1.0).astype(bm.bool) +mesh.coarsen(isMarkedCell=isMarkedCell) +n=2 + +# A = mesh.uniform_refine(n,returnim=True) +# def linear_function(coodrs): +# return 2*coodrs[:,0] + 2*coodrs[:,1]+2 +# excat_value = linear_function(mesh.node) +# val = linear_function(node) +# test_value = val +# for i in range(n): +# print(A[i]) +# test_value = A[i].dot(test_value) +# print(excat_value) +# print(test_value) + + +fig = plt.figure() +axes = fig.gca() +mesh.add_plot(axes) +mesh.find_node(axes, showindex=True) +mesh.find_edge(axes, showindex=True) +plt.show() \ No newline at end of file diff --git a/use_test/test_tetra.py b/use_test/test_tetra.py new file mode 100644 index 000000000..c412dd51e --- /dev/null +++ b/use_test/test_tetra.py @@ -0,0 +1,21 @@ +from fealpy.mesh import TetrahedronMesh +from fealpy.backend import backend_manager as bm +from scipy.sparse import csr_matrix +import matplotlib.pyplot as plt +from mpl_toolkits.mplot3d import Axes3D + +mesh = TetrahedronMesh.from_one_tetrahedron() +node = mesh.entity('node') +mesh.uniform_refine() +NN = mesh.number_of_nodes() +NE = mesh.number_of_edges() +NF = mesh.number_of_faces() +NC = mesh.number_of_cells() + +fig = plt.figure(1) +axes = fig.add_subplot(111, projection='3d') +mesh.add_plot(axes) +plt.title("Grid Image") +mesh.find_node(axes, showindex=True) +mesh.find_edge(axes, showindex=True) +plt.show() \ No newline at end of file diff --git a/use_test/test_tri.py b/use_test/test_tri.py new file mode 100644 index 000000000..5d7dfb3a4 --- /dev/null +++ b/use_test/test_tri.py @@ -0,0 +1,51 @@ +from fealpy.mesh import TriangleMesh +from fealpy.backend import backend_manager as bm +from fealpy.sparse import CSRTensor +import matplotlib.pyplot as plt + +#生成三角形 +node = bm.tensor([[0,0],[1,0],[0,1]],dtype=bm.float64) +cell=bm.tensor([[0,1,2]],dtype=bm.int32) +mesh = TriangleMesh(node, cell) +NN = mesh.number_of_nodes() + +#定义插值矩阵 +def Prolongation_matrix(mesh,numnode,numedge): + l = numnode+numedge + data = bm.zeros(numnode+2*numedge,dtype=bm.float64) + indices = bm.zeros(numnode+2*numedge,dtype=bm.int32) + indptr = bm.zeros(l+1,dtype=bm.int32) + sparse_shape = bm.tensor([l, numnode]) + data[:numnode] = 1 + data[numnode:] = 0.5 + indices[:numnode] = bm.arange(numnode) + indices[numnode:] = edge.flatten() + indptr[:numnode+1] = bm.arange(numnode+1) + indptr[numnode+1:]=bm.arange(numnode+2,numnode+2*numedge+1,step=2) + A = CSRTensor(indptr,indices,data,sparse_shape) + return A + +#网格加密 +k = 1#设置加密次数 +for i in range(k): + edge = mesh.entity('edge') + nn = mesh.number_of_nodes() + ng = mesh.number_of_edges() + mesh.uniform_refine() + node_new = mesh.entity('node') + A = Prolongation_matrix(mesh,numnode=nn,numedge=ng) + #print(A) +print(f"加密1次后的插值矩阵:{A}") + +#带入线性函数验证 +def linear_function(coords): + return coords[:, 0]**2 + 2*coords[:, 1] + 3 +#计算函数在插值点数值 +value_check = linear_function(node_new) +#运用插值矩阵生成加密后的插值点函数值 +value = linear_function(node) +value_new = A.matmul(value) +print(f"真函数值:{value_check}") +print(f"加密后的插值点函数值:{value_new}") +are_close = bm.allclose(value_check, value_new) +print(f"是否为插值矩阵(考虑精度):{are_close}\n") \ No newline at end of file diff --git a/use_test/triangle_mesh_IM.py b/use_test/triangle_mesh_IM.py new file mode 100644 index 000000000..e7288c540 --- /dev/null +++ b/use_test/triangle_mesh_IM.py @@ -0,0 +1,67 @@ +from fealpy.mesh import TriangleMesh +from fealpy.backend import backend_manager as bm +from fealpy.sparse import CSRTensor +import matplotlib.pyplot as plt +from sympy import symbols, Eq, solve + +#生成三角形 +node = bm.tensor([[0,0],[1,0],[0,1]],dtype=bm.float64) +cell=bm.tensor([[0,1,2]],dtype=bm.int32) +mesh = TriangleMesh(node, cell) +mesh.uniform_refine() +mesh.uniform_refine() +#确定粗网格节点 +cell_h = mesh.number_of_cells() +cell_H = 0.25*cell_h +n_1 = symbols('n_1') +equation = Eq(n_1**2, cell_H) +solutions = solve(equation, n_1) +n_1 = bm.int32(solutions[1])+1 +node_H = int((n_1**2+n_1)/2) +node_h = mesh.number_of_nodes() +node_new = mesh.entity('node')[:node_H] + +edge2node = mesh.edge_to_node() + +def Resriction_matrix(mesh,row1,row2): + valence = bm.zeros(node_h, dtype=mesh.itype, device=mesh.device) + bm.add_at(valence, edge2node, 1) + row3 = node_H+bm.sum(valence[0:node_H]) + + #indptr + indptr = bm.zeros(row1+1,dtype=bm.int32) + indptr[0]= 1 + for i in range(1,row1+1): + indptr[i] = indptr[i-1]+1+valence[i-1] + + #indices + indices = bm.zeros(row3,dtype=bm.int32) + for k in range(row1): + t_list = [] + for element in edge2node: + if k in element: + other_number = [x for x in element if x != k] + t_list.append(other_number) + t_list.sort() + t = bm.array(t_list) + indices[indptr[k]:indptr[k+1]-1] = t.flatten() + indices[indptr[k]-1] = k + + data = bm.zeros(row3,dtype=bm.float64) + for i in range(row1): + data[indptr[i]-1] = 2/(indptr[i+1]-indptr[i]+1) + data[indptr[i]:indptr[i+1]-1] = 1/(indptr[i+1]-indptr[i]+1) + + R = CSRTensor(indptr,indices,data) + return R + +R = Resriction_matrix(mesh,node_H,node_h) + +def linear_function(coodrs): + return coodrs[:,0] + 2*coodrs[:,1]+3 +value_check = linear_function(mesh.node) +value_new = R.matmul(value_check) +value_real = linear_function(node_new) + +print(f"限制矩阵:{R}\n") +print(f"精确值:{value_real},检验限制矩阵的值:{value_new}") From 7b2f469a167cd9b8ead283cfe12c9f25ce77237e Mon Sep 17 00:00:00 2001 From: Mihe0904 <1614827225@qq.com> Date: Thu, 21 Nov 2024 11:26:03 +0800 Subject: [PATCH 13/63] (mesh/.)triangle/hexahedron/quadrangle_mesh.uniform_refine and (test/.)triangle/hexahedron/quadrangle_mesh_data IM --- use_test/corsen.py | 115 ----------------------------------- use_test/test_hexa.py | 79 ------------------------ use_test/test_quad.py | 77 ----------------------- use_test/test_t_data.py | 35 ----------- use_test/test_tetra.py | 21 ------- use_test/test_tri.py | 51 ---------------- use_test/triangle_mesh_IM.py | 67 -------------------- 7 files changed, 445 deletions(-) delete mode 100644 use_test/corsen.py delete mode 100644 use_test/test_hexa.py delete mode 100644 use_test/test_quad.py delete mode 100644 use_test/test_t_data.py delete mode 100644 use_test/test_tetra.py delete mode 100644 use_test/test_tri.py delete mode 100644 use_test/triangle_mesh_IM.py diff --git a/use_test/corsen.py b/use_test/corsen.py deleted file mode 100644 index ca062075c..000000000 --- a/use_test/corsen.py +++ /dev/null @@ -1,115 +0,0 @@ -from fealpy.mesh import TriangleMesh -from fealpy.backend import backend_manager as bm -from fealpy.sparse import CSRTensor -import matplotlib.pyplot as plt -from sympy import symbols, Eq, solve - -#生成三角形 -node = bm.tensor([[0,0],[1,0],[0,1]],dtype=bm.float64) -cell=bm.tensor([[0,1,2]],dtype=bm.int32) -mesh = TriangleMesh(node, cell) -mesh.uniform_refine() - -#确定粗网格节点 -cell_h = mesh.number_of_cells() -cell_H = 0.25*cell_h -n_1 = symbols('n_1') -equation = Eq(n_1**2, cell_H) -solutions = solve(equation, n_1) -n_1 = bm.int32(solutions[1])+1 -node_H = int((n_1**2+n_1)/2) -node_h = mesh.number_of_nodes() -node_new = mesh.entity('node')[:node_H] - -edge2node = mesh.edge_to_node() - -def Resriction_matrix(mesh,row1,row2): - valence = bm.zeros(node_h, dtype=mesh.itype, device=mesh.device) - bm.add_at(valence, edge2node, 1) - row3 = node_H+bm.sum(valence[0:node_H]) - - #indptr - indptr = bm.zeros(row1+1,dtype=bm.int32) - indptr[0]= 1 - for i in range(1,row1+1): - indptr[i] = indptr[i-1]+1+valence[i-1] - - #indices - indices = bm.zeros(row3,dtype=bm.int32) - for k in range(row1): - t_list = [] - for element in edge2node: - if k in element: - other_number = [x for x in element if x != k] - t_list.append(other_number) - t_list.sort() - t = bm.array(t_list) - indices[indptr[k]:indptr[k+1]-1] = t.flatten() - indices[indptr[k]-1] = k - - data = bm.zeros(row3,dtype=bm.float64) - for i in range(row1): - print(indptr[i+1]-indptr[i]) - data[indptr[i]-1] = 2/(indptr[i+1]-indptr[i]+1) - data[indptr[i]:indptr[i+1]-1] = 1/(indptr[i+1]-indptr[i]+1) - - R = CSRTensor(indptr,indices,data) - return R - -R = Resriction_matrix(mesh,node_H,node_h) - -# # 求解方程 -# sol = sp.solve([eq], [0]) -# edge = mesh.entity('edge') -# edge2node = mesh.edge_to_node() -# NE = mesh.number_of_edges() - -# for i in range(NE): -# if (node[edge[i],:][0][0]!=node[edge[i],:][1][0]) and (node[edge[i],:][0][1]!=node[edge[i],:][1][1]): -# line[i]=(node[edge[i],:][0][0]-node[edge[i],:][1][0])/(node[edge[i],:][0][1]-node[edge[i],:][1][1]) -# isGEdge = (line!=0) -# print(edge[isGEdge]) - -# cell2edge = mesh.cell_to_edge() -# GEdge= bm.zeros(NE, dtype=mesh.itype, device=mesh.device) -# bm.add_at(GEdge, cell2edge, 1) -# print(GEdge) -# isGEdge = (GEdge==2) -# print(edge[isGEdge]) - - -# GNode = bm.zeros(NE, dtype=mesh.itype, device=mesh.device) -# bm.add_at(GNode, cell2edge, 1) -# print(GNode) -# isGNode = (GNode != 0) -# print(isGNode) -# print(node[isGNode]) - -# print(mesh.cell_to_node()) -# valence = bm.zeros(NN, dtype=mesh.itype, device=mesh.device) -# bm.add_at(valence, cell, 1) -# isBNode = (valence==1) -# BNode = node[isBNode] -# print(BNode) - -print(f"限制矩阵为:{R}") -print(f"粗化新坐标点:{node_new}") -def linear_function(coodrs): - return coodrs[:,0]*coodrs[:,0] + 2*coodrs[:,1]+3 -value_check = linear_function(mesh.node) -value_new = R.matmul(value_check) -print(f"限制矩阵下的插值结果:{value_new}") -print(f"精确值:{linear_function(node_new)}") - - -fig = plt.figure() -axes = fig.gca() -mesh.add_plot(axes) -mesh.find_node(axes, showindex=True) -mesh.find_edge(axes, showindex=True) -plt.show() - -# try_1 = bm.tensor([3.875,4.5,2.125],dtype = bm.float64) -# try_2 = CSRTensor(crow=bm.tensor([0 ,1 ,2 ,3 ,5 ,7 ,9]), col=bm.tensor([0 ,1 ,2 ,0 ,1 ,2 ,0 ,1 ,2]), -# values=bm.tensor([1. ,1. ,1. ,0.5 ,0.5 ,0.5 ,0.5 ,0.5 ,0.5])) -# print(f"check:{try_2.matmul(try_1)}") \ No newline at end of file diff --git a/use_test/test_hexa.py b/use_test/test_hexa.py deleted file mode 100644 index fec03255d..000000000 --- a/use_test/test_hexa.py +++ /dev/null @@ -1,79 +0,0 @@ -from fealpy.mesh import HexahedronMesh -import numpy as np -from scipy.sparse import csr_matrix -import matplotlib.pyplot as plt -from mpl_toolkits.mplot3d import Axes3D - -mesh = HexahedronMesh.from_one_hexahedron() -node = mesh.entity('node') -edge = mesh.entity('edge') -face = mesh.entity('face') -cell = mesh.entity('cell') -print(node) -print(edge) -print(face) -print(cell) - -# def Prolongation_matrix(mesh,numnode,numedge,numface,numcell): -# new_node_num = numnode+numedge+numface+numcell -# nonzeros = numnode+2*numedge+4*numface+8*numcell -# data = np.zeros(nonzeros,dtype=np.float64) -# data[:numnode] = 1 -# data[numnode:numnode+2*numedge] = 1/2 -# data[2*numedge:numnode+2*numedge+4*numface] = 1/4 -# data[numnode+2*numedge+4*numface:] = 1/8 - -# indices = np.zeros(nonzeros,dtype=np.int32) -# indices[:numnode] = np.arange(numnode) -# indices[numnode:numnode+2*numedge] = edge2node.flatten() -# indices[numnode+2*numedge:numnode+2*numedge+4*numface] = face2node.flatten() -# indices[numnode+2*numedge+4*numface:] = cell2node.flatten() - -# indptr = np.zeros(new_node_num+1,dtype=np.int32) -# indptr[:numnode+1] = np.arange(numnode+1) -# indptr[numnode+1:numnode+numedge+1]=np.arange(numnode+2,numnode+2*numedge+1,step=2) -# indptr[numnode+numedge+1:numnode+numedge+numface+1] = np.arange(numnode+2*numedge+4,numnode+2*numedge+4*numface+1,step=4) -# indptr[numnode+numedge+numface+1:] = np.arange(numnode+2*numedge+4*numface+8,nonzeros+1,step=8) - -# A = csr_matrix((data,indices,indptr),dtype=np.float64) -# return A - -# for i in np.arange(n): -# ''' -# n:加密次数 -# ''' -# edge2node = mesh.edge_to_node() -# face2node = mesh.face_to_node() -# cell2node = mesh.cell_to_node() -# NN = mesh.number_of_nodes() -# NE = mesh.number_of_edges() -# NF = mesh.number_of_faces() -# NC= mesh.number_of_cells() -# mesh.uniform_refine() - -# A = Prolongation_matrix(mesh,NN,NE,NF,NC) -# IM.append(A) - -# print(IM[1]) - -n = 2 -A = mesh.uniform_refine(n,returnim=True) - -def linear_function(coodrs): - return 2*coodrs[:,0] + 2*coodrs[:,1]+2*coodrs[:,2]+1 -excat_value = linear_function(mesh.node) -val = linear_function(node) -test_value = val -for i in range(n): - print(A[i]) - test_value = A[i].dot(test_value) -print(excat_value) -print(test_value) - -# fig = plt.figure(1) -# axes = fig.add_subplot(111, projection='3d') -# mesh.add_plot(axes) -# plt.title("Grid Image") -# mesh.find_node(axes, showindex=True) -# mesh.find_edge(axes, showindex=True) -# plt.show() \ No newline at end of file diff --git a/use_test/test_quad.py b/use_test/test_quad.py deleted file mode 100644 index 82eb9f296..000000000 --- a/use_test/test_quad.py +++ /dev/null @@ -1,77 +0,0 @@ -from fealpy.mesh import QuadrangleMesh -from fealpy.backend import backend_manager as bm -from fealpy.sparse import CSRTensor -import matplotlib.pyplot as plt - -mesh = QuadrangleMesh.from_one_quadrangle() -node = mesh.entity('node') -edge = mesh.entity('edge') -cell = mesh.entity('cell') -print(node) -print(edge) -print(cell) - -n=2 -A = mesh.uniform_refine(n,returnim=True) -print(A[0],A[1]) -def linear_function(coodrs): - return 2*coodrs[:,0] + 2*coodrs[:,1]+2 -excat_value = linear_function(mesh.node) -val = linear_function(node) -test_value = val -for i in range(n): - test_value = A[i].dot(test_value) -print(excat_value) -print(test_value) - -# def Prolongation_matrix(mesh,numnode,numedge,numcell): -# ''' -# l:the nodes of new mesh -# ''' - -# l = numnode+numedge+numcell - -# data = bm.zeros(numnode+2*numedge+4*numcell,dtype=bm.float64) -# indices = bm.zeros(numnode+2*numedge+4*numcell,dtype=bm.int32) -# indptr = bm.zeros(l+1,dtype=bm.int32) - -# #赋值 -# data[:numnode] = 1 -# data[numnode:numnode+2*numedge] = 1/2 -# data[numnode+2*numedge:] = 1/4 - -# indices[:numnode] = bm.arange(numnode) -# indices[numnode:numnode+2*numedge] = edge2node.flatten() -# indices[numnode+2*numedge:] = cell2node.flatten() - -# indptr[:numnode+1] = bm.arange(numnode+1) -# indptr[numnode+1:numnode+numedge+1]=bm.arange(numnode+2,numnode+2*numedge+1,step=2) -# indptr[numnode+numedge+1:] = bm.arange(numnode+2*numedge+4,numnode+2*numedge+4*numcell+1,step=4) -# A = CSRTensor(indptr,indices,data) -# return A - - -# A = Prolongation_matrix(mesh,node_old,edge_old,cell_old) -# print(f"加密一次后的插值矩阵是:{A}") - -# #带入线性函数验证 -# def linear_function(coords): -# return 5*coords[:, 0] + 4*coords[:, 1] + 3 -# #计算函数在插值点数值 -# value_check = linear_function(node2) -# #运用插值矩阵生成加密后的插值点函数值 -# value_old = linear_function(node1) -# value_new = A.matmul(value_old) -# print(f"真函数值:{value_check}") -# print(f"加密后的插值点函数值:{value_new}") -# are_close = bm.allclose(value_check, value_new) -# print(f"是否为插值矩阵(考虑精度):{are_close}\n") - -# fig = plt.figure() -# axes = fig.gca() -# mesh.add_plot(axes) -# mesh.find_node(axes, showindex=True) -# mesh.find_edge(axes, showindex=True) -# mesh.find_cell(axes, showindex=True) -# print(mesh.number_of_nodes()) -# plt.show() diff --git a/use_test/test_t_data.py b/use_test/test_t_data.py deleted file mode 100644 index bbe4e1c63..000000000 --- a/use_test/test_t_data.py +++ /dev/null @@ -1,35 +0,0 @@ -from fealpy.mesh import TriangleMesh -from fealpy.backend import backend_manager as bm -from fealpy.sparse.coo_tensor import COOTensor -import matplotlib.pyplot as plt - -node = bm.tensor([[0,0],[1,0],[0,1]],dtype=bm.float64) -cell=bm.tensor([[0,1,2]],dtype=bm.int32) -mesh = TriangleMesh(node,cell) -mesh.uniform_refine() -mesh.uniform_refine() -numcell = mesh.number_of_cells() -cell_new = bm.ones(numcell) -isMarkedCell = (cell_new == 1.0).astype(bm.bool) -mesh.coarsen(isMarkedCell=isMarkedCell) -n=2 - -# A = mesh.uniform_refine(n,returnim=True) -# def linear_function(coodrs): -# return 2*coodrs[:,0] + 2*coodrs[:,1]+2 -# excat_value = linear_function(mesh.node) -# val = linear_function(node) -# test_value = val -# for i in range(n): -# print(A[i]) -# test_value = A[i].dot(test_value) -# print(excat_value) -# print(test_value) - - -fig = plt.figure() -axes = fig.gca() -mesh.add_plot(axes) -mesh.find_node(axes, showindex=True) -mesh.find_edge(axes, showindex=True) -plt.show() \ No newline at end of file diff --git a/use_test/test_tetra.py b/use_test/test_tetra.py deleted file mode 100644 index c412dd51e..000000000 --- a/use_test/test_tetra.py +++ /dev/null @@ -1,21 +0,0 @@ -from fealpy.mesh import TetrahedronMesh -from fealpy.backend import backend_manager as bm -from scipy.sparse import csr_matrix -import matplotlib.pyplot as plt -from mpl_toolkits.mplot3d import Axes3D - -mesh = TetrahedronMesh.from_one_tetrahedron() -node = mesh.entity('node') -mesh.uniform_refine() -NN = mesh.number_of_nodes() -NE = mesh.number_of_edges() -NF = mesh.number_of_faces() -NC = mesh.number_of_cells() - -fig = plt.figure(1) -axes = fig.add_subplot(111, projection='3d') -mesh.add_plot(axes) -plt.title("Grid Image") -mesh.find_node(axes, showindex=True) -mesh.find_edge(axes, showindex=True) -plt.show() \ No newline at end of file diff --git a/use_test/test_tri.py b/use_test/test_tri.py deleted file mode 100644 index 5d7dfb3a4..000000000 --- a/use_test/test_tri.py +++ /dev/null @@ -1,51 +0,0 @@ -from fealpy.mesh import TriangleMesh -from fealpy.backend import backend_manager as bm -from fealpy.sparse import CSRTensor -import matplotlib.pyplot as plt - -#生成三角形 -node = bm.tensor([[0,0],[1,0],[0,1]],dtype=bm.float64) -cell=bm.tensor([[0,1,2]],dtype=bm.int32) -mesh = TriangleMesh(node, cell) -NN = mesh.number_of_nodes() - -#定义插值矩阵 -def Prolongation_matrix(mesh,numnode,numedge): - l = numnode+numedge - data = bm.zeros(numnode+2*numedge,dtype=bm.float64) - indices = bm.zeros(numnode+2*numedge,dtype=bm.int32) - indptr = bm.zeros(l+1,dtype=bm.int32) - sparse_shape = bm.tensor([l, numnode]) - data[:numnode] = 1 - data[numnode:] = 0.5 - indices[:numnode] = bm.arange(numnode) - indices[numnode:] = edge.flatten() - indptr[:numnode+1] = bm.arange(numnode+1) - indptr[numnode+1:]=bm.arange(numnode+2,numnode+2*numedge+1,step=2) - A = CSRTensor(indptr,indices,data,sparse_shape) - return A - -#网格加密 -k = 1#设置加密次数 -for i in range(k): - edge = mesh.entity('edge') - nn = mesh.number_of_nodes() - ng = mesh.number_of_edges() - mesh.uniform_refine() - node_new = mesh.entity('node') - A = Prolongation_matrix(mesh,numnode=nn,numedge=ng) - #print(A) -print(f"加密1次后的插值矩阵:{A}") - -#带入线性函数验证 -def linear_function(coords): - return coords[:, 0]**2 + 2*coords[:, 1] + 3 -#计算函数在插值点数值 -value_check = linear_function(node_new) -#运用插值矩阵生成加密后的插值点函数值 -value = linear_function(node) -value_new = A.matmul(value) -print(f"真函数值:{value_check}") -print(f"加密后的插值点函数值:{value_new}") -are_close = bm.allclose(value_check, value_new) -print(f"是否为插值矩阵(考虑精度):{are_close}\n") \ No newline at end of file diff --git a/use_test/triangle_mesh_IM.py b/use_test/triangle_mesh_IM.py deleted file mode 100644 index e7288c540..000000000 --- a/use_test/triangle_mesh_IM.py +++ /dev/null @@ -1,67 +0,0 @@ -from fealpy.mesh import TriangleMesh -from fealpy.backend import backend_manager as bm -from fealpy.sparse import CSRTensor -import matplotlib.pyplot as plt -from sympy import symbols, Eq, solve - -#生成三角形 -node = bm.tensor([[0,0],[1,0],[0,1]],dtype=bm.float64) -cell=bm.tensor([[0,1,2]],dtype=bm.int32) -mesh = TriangleMesh(node, cell) -mesh.uniform_refine() -mesh.uniform_refine() -#确定粗网格节点 -cell_h = mesh.number_of_cells() -cell_H = 0.25*cell_h -n_1 = symbols('n_1') -equation = Eq(n_1**2, cell_H) -solutions = solve(equation, n_1) -n_1 = bm.int32(solutions[1])+1 -node_H = int((n_1**2+n_1)/2) -node_h = mesh.number_of_nodes() -node_new = mesh.entity('node')[:node_H] - -edge2node = mesh.edge_to_node() - -def Resriction_matrix(mesh,row1,row2): - valence = bm.zeros(node_h, dtype=mesh.itype, device=mesh.device) - bm.add_at(valence, edge2node, 1) - row3 = node_H+bm.sum(valence[0:node_H]) - - #indptr - indptr = bm.zeros(row1+1,dtype=bm.int32) - indptr[0]= 1 - for i in range(1,row1+1): - indptr[i] = indptr[i-1]+1+valence[i-1] - - #indices - indices = bm.zeros(row3,dtype=bm.int32) - for k in range(row1): - t_list = [] - for element in edge2node: - if k in element: - other_number = [x for x in element if x != k] - t_list.append(other_number) - t_list.sort() - t = bm.array(t_list) - indices[indptr[k]:indptr[k+1]-1] = t.flatten() - indices[indptr[k]-1] = k - - data = bm.zeros(row3,dtype=bm.float64) - for i in range(row1): - data[indptr[i]-1] = 2/(indptr[i+1]-indptr[i]+1) - data[indptr[i]:indptr[i+1]-1] = 1/(indptr[i+1]-indptr[i]+1) - - R = CSRTensor(indptr,indices,data) - return R - -R = Resriction_matrix(mesh,node_H,node_h) - -def linear_function(coodrs): - return coodrs[:,0] + 2*coodrs[:,1]+3 -value_check = linear_function(mesh.node) -value_new = R.matmul(value_check) -value_real = linear_function(node_new) - -print(f"限制矩阵:{R}\n") -print(f"精确值:{value_real},检验限制矩阵的值:{value_new}") From 83a4fe952996c3c53c5f808a2c165b2304c4de09 Mon Sep 17 00:00:00 2001 From: tiantian0347 Date: Thu, 21 Nov 2024 15:32:40 +0800 Subject: [PATCH 14/63] update --- .../crack_surface_density_function.py | 94 +++++++++++++++- .../phasefield/energy_degradation_function.py | 38 ++++++- .../fracturex/phasefield/main_solver.py | 102 +++++++++++++----- 3 files changed, 205 insertions(+), 29 deletions(-) diff --git a/app/fracturex/fracturex/phasefield/crack_surface_density_function.py b/app/fracturex/fracturex/phasefield/crack_surface_density_function.py index 764106aab..0b1a872d6 100644 --- a/app/fracturex/fracturex/phasefield/crack_surface_density_function.py +++ b/app/fracturex/fracturex/phasefield/crack_surface_density_function.py @@ -1 +1,93 @@ -from fealpy.backend import backend_manager as bm \ No newline at end of file +from fealpy.backend import backend_manager as bm + + +class CrackSurfaceDensityFunction: + def __init__(self, density_type='AT2', **kwargs): + self.density_type = density_type + self.params = kwargs + + + def density_function(self, d): + if self.density_type == 'AT2': + return self._AT2_density(d) + elif self.density_type == 'AT1': + return self._AT1_density(d) + elif self.density_type == 'user_defined': + return self._user_defined_density(d) + else: + raise ValueError(f"Unknown density type: {self.density_type}") + + def grad_density_function(self, d): + if self.density_type == 'AT2': + return self._AT2_grad_density(d) + elif self.density_type == 'AT1': + return self._AT1_grad_density(d) + elif self.density_type == 'user_defined': + return self._user_defined_grad_density(d) + else: + raise ValueError(f"Unknown density type: {self.density_type}") + + def grad_grad_density_function(self, d): + if self.density_type == 'AT2': + return self._AT2_grad_grad_density(d) + elif self.density_type == 'AT1': + return self._AT1_grad_grad_density(d) + elif self.density_type == 'user_defined': + return self._user_defined_grad_grad_density(d) + else: + raise ValueError(f"Unknown density type: {self.density_type}") + + def _AT2_density(self, d): + """ + The AT2 crack surface density function h(d) = d^2, c_d=2. + """ + return d**2, 2 + + def _AT2_grad_density(self, d): + """ + The derivative of the AT2 crack surface density function h'(d) = 2d, c_d=2. + """ + return 2*d, 2 + + def _AT2_grad_grad_density(self, d): + """ + The second derivative of the AT2 crack surface density function h''(d) = 2. + """ + return 2, 2 + + def _AT1_density(self, d): + """ + The AT1 crack surface density function g(d) = d. + """ + return d, 8/3 + + def _AT1_grad_density(self, d): + """ + The derivative of the AT1 crack surface density function g'(d) = 1. + """ + return 1, 8/3 + + def _AT1_grad_grad_density(self, d): + """ + The second derivative of the AT1 crack surface density function g''(d) = 0. + """ + return 0, 8/3 + + def _user_defined_density(self, d): + """ + The user defined crack surface density function g(d). + """ + raise NotImplementedError() + + def _user_defined_grad_density(self, d): + """ + The derivative of the user defined crack surface density function g'(d). + """ + raise NotImplementedError() + + def _user_defined_grad_grad_density(self, d): + """ + The second derivative of the user defined crack surface density function g''(d). + """ + raise NotImplementedError() + \ No newline at end of file diff --git a/app/fracturex/fracturex/phasefield/energy_degradation_function.py b/app/fracturex/fracturex/phasefield/energy_degradation_function.py index 9ebcb0bb8..b5cc1298b 100644 --- a/app/fracturex/fracturex/phasefield/energy_degradation_function.py +++ b/app/fracturex/fracturex/phasefield/energy_degradation_function.py @@ -45,6 +45,23 @@ def grad_grad_degradation_function(self, d): return self._user_defined_grad_grad_degradation(d) else: raise ValueError(f"Unknown degradation type: {self.degradation_type}") + + def grad_degradation_function_constant_coef(self): + """ + Get the constant coefficient in the gradient of the energy degradation function. + + Parameters: + d (float or numpy array): phase field value. + + return: + c (float or numpy array): The constant coefficient in the gradient of the energy degradation function. + """ + if self.degradation_type == 'quadratic': + return -2 + elif self.degradation_type == 'user_defined': + return self.params.get('constant_coef') + else: + raise ValueError(f"Unknown degradation type: {self.degradation_type}") def _quadratic_degradation(self, d): @@ -65,8 +82,7 @@ def _quadratic_grad_degradation(self, d): """ The derivative of the quadratic energy degradation function g'(d) = -2(1 - d)。 """ - eps = 1e-10 - g_gd = -2*(1 - d) + g_gd = -2 + 2*d return g_gd def _quadratice_grad_grad_degradation(self, d): @@ -103,6 +119,24 @@ def _user_defined_degradation(self, d): if custom_function is None: raise ValueError("For user_defined degradation, 'custom_function' must be provided.") return custom_function(d) + + def _user_defined_grad_degradation(self, d): + """ + The derivative of the user-defined energy degradation function. + """ + custom_grad_function = self.params.get('custom_grad_function') + if custom_grad_function is None: + raise ValueError("For user_defined degradation, 'custom_grad_function' must be provided.") + return custom_grad_function(d) + + def _user_defined_grad_grad_degradation(self, d): + """ + The second derivative of the user-defined energy degradation function. + """ + custom_grad_grad_function = self.params.get('custom_grad_grad_function') + if custom_grad_grad_function is None: + raise ValueError("For user_defined degradation, 'custom_grad_grad_function' must be provided.") + return custom_grad_grad_function(d) def plot_degradation_function(self, d_values): """ diff --git a/app/fracturex/fracturex/phasefield/main_solver.py b/app/fracturex/fracturex/phasefield/main_solver.py index 412510059..4794aeb69 100644 --- a/app/fracturex/fracturex/phasefield/main_solver.py +++ b/app/fracturex/fracturex/phasefield/main_solver.py @@ -21,6 +21,7 @@ from scipy.sparse.linalg import lgmres from app.fracturex.fracturex.phasefield.energy_degradation_function import EnergyDegradationFunction as EDFunc +from app.fracturex.fracturex.phasefield.crack_surface_density_function import CrackSurfaceDensityFunction as CSDFunc from app.fracturex.fracturex.phasefield.phase_fracture_material import PhaseFractureMaterialFactory from app.fracturex.fracturex.phasefield.adaptive_refinement import AdaptiveRefinement from app.fracturex.fracturex.phasefield.vector_Dirichlet_bc import VectorDirichletBC @@ -41,20 +42,20 @@ def __init__(self, mesh, material_params: Dict, p : int, optional Polynomial order for the function space, by default 1. q : int, optional - Quadrature degree, by default p + 2. + Quadrature degree, by default p + 3. model_type : str, optional Stress decomposition model, by default 'HybridModel'. method : str, optional The method for solving the problem, by default 'lfem'. """ self.mesh = mesh - self.p = 1 - self.q = self.p + 2 -# self.model = model + self.p = p + self.q = self.p + 3 if q is None else q # Material and energy degradation function self.set_energy_degradation(degradation_type='quadratic') - self.EDFunc = EDFunc(degradation_type='quadratic') + self.set_crack_surface_density(density_type='AT2') + self.model_type = model_type self.pfcm = PhaseFractureMaterialFactory.create(model_type, material_params, self.EDFunc) @@ -249,18 +250,26 @@ def solve_phase_field(self) -> float: The norm of the residual. """ Gc, l0, d = self.Gc, self.l0, self.d - + @barycentric def coef(bc, index): - return 2 * self.pfcm.maximum_historical_field(bc) + gg_gd = self.EDFunc.grad_grad_degradation_function(d) + return gg_gd * self.pfcm.maximum_historical_field(bc) + + @barycentric + def source_coef(bc, index): + gg_gd = self.EDFunc.grad_degradation_function_constant_coef() + return -1 * gg_gd * self.pfcm.maximum_historical_field(bc) tmr = self.tmr tmr.send('phase_start') + gg_hd, c_d = self.CSDFunc.grad_grad_density_function(d) + dbform = BilinearForm(self.space) - dbform.add_integrator(ScalarDiffusionIntegrator(coef=Gc * l0, q=self.q)) - dbform.add_integrator(ScalarMassIntegrator(coef=Gc / l0, q=self.q)) - dbform.add_integrator(ScalarMassIntegrator(coef=coef, q=self.q)) + dbform.add_integrator(ScalarDiffusionIntegrator(coef=Gc * l0 * 2 / c_d, q=self.q)) + dbform.add_integrator(ScalarMassIntegrator(coef=gg_hd * Gc / (l0 * c_d), q=self.q)) + dbform.add_integrator(ScalarMassIntegrator(coef=source_coef, q=self.q)) A = dbform.assembly() tmr.send('phase_matrix_assemble') @@ -357,33 +366,51 @@ def solve_phase_field_auto(self) -> float: The norm of the residual. """ Gc, l0, d = self.Gc, self.l0, self.d + + c_d = self.CSDFunc.grad_density_function(d)[1] @barycentric def diffusion_coef(bc, **kwargs): - return Gc * l0 + return Gc * l0 * 2 / c_d @barycentric - def mass_coef(bc, **kwargs): - return 2 * self.pfcm.maximum_historical_field(bc) + Gc / l0 + def mass_coef1(bc, **kwargs): + return Gc / (l0 * c_d) + + @barycentric + def mass_coef2(bc, **kwargs): + return self.pfcm.maximum_historical_field(bc) + + @barycentric + def mass_kernel_func2(u): + return self.EDFunc.grad_degradation_function(u) + + @barycentric + def mass_grad_kernel_func2(u): + return self.EDFunc.grad_grad_degradation_function(u) @barycentric def kernel_func(u): - return u + return self.CSDFunc.grad_density_function(u)[0] def grad_kernel_func(u): - return bm.ones_like(u) + return self.CSDFunc.grad_grad_density_function(u)[0] @barycentric def source_coef(bc, index): - return 2 * self.pfcm.maximum_historical_field(bc) + gg_gd = self.EDFunc.grad_degradation_function_constant_coef() + return -1 * gg_gd * self.pfcm.maximum_historical_field(bc) diffusion_coef.kernel_func = kernel_func - mass_coef.kernel_func = kernel_func + mass_coef1.kernel_func = kernel_func + mass_coef2.kernel_func = mass_kernel_func2 if bm.backend_name == 'numpy': diffusion_coef.grad_kernel_func = grad_kernel_func - mass_coef.grad_kernel_func = grad_kernel_func + mass_coef1.grad_kernel_func = grad_kernel_func + mass_coef2.grad_kernel_func = mass_grad_kernel_func2 - mass_coef.uh = d + mass_coef1.uh = d + mass_coef2.uh = d diffusion_coef.uh = d tmr = self.tmr @@ -392,8 +419,9 @@ def source_coef(bc, index): # using automatic differentiation to assemble the phase field system dform = NonlinearForm(self.space) dform.add_integrator(ScalarNonlinearDiffusionIntegrator(diffusion_coef, q=self.q)) - dform.add_integrator(ScalarNonlinearMassIntegrator(mass_coef, q=self.q)) - dform.add_integrator(ScalarSourceIntegrator(source_coef, q=self.q)) + dform.add_integrator(ScalarNonlinearMassIntegrator(mass_coef1, q=self.q)) + dform.add_integrator(ScalarNonlinearMassIntegrator(mass_coef2, q=self.q)) + #dform.add_integrator(ScalarSourceIntegrator(source_coef, q=self.q)) A, R = dform.assembly() tmr.send('phase_matrix_assemble') @@ -609,19 +637,41 @@ def output_timer(self): self._timer = True - def set_energy_degradation(self, EDfunc=None): + def set_energy_degradation(self, degradation_type='quadratic', EDfunc=None, **kwargs): """ Set the energy degradation function. Parameters ---------- - EDfunc : object - Energy degradation function. + EDfunc : callable, optional + Energy degradation function class or factory. If None, a default energy degradation function is used. + degradation_type : str, optional + Type of energy degradation function. Default is 'quadratic'. + **kwargs : dict + Additional parameters passed to the energy degradation function. """ if EDfunc is not None: - self.EDFunc = EDfunc + self.EDFunc = EDfunc(degradation_type=degradation_type, **kwargs) + else: + self.EDFunc = EDFunc(degradation_type=degradation_type) + + def set_crack_surface_density(self, density_type='AT2', CSDfunc=None, **kwargs): + """ + Set the crack surface density function. + + Parameters + ---------- + CSDFunc : callable, optional + Crack surface density function class or factory. If None, a default crack surface density function is used. + density_type : str, optional + Type of crack surface density function. Default is 'AT2'. + **kwargs : dict + Additional parameters passed to the crack surface density function. + """ + if CSDfunc is not None: + self.CSDFunc = CSDfunc(density_type=density_type, **kwargs) else: - self.EDFunc = EDFunc + self.CSDFunc = CSDFunc(density_type=density_type) def set_cupy_solver(self): """ From b8f5d9525359ffc8dae69e053a2a208275f9de1e Mon Sep 17 00:00:00 2001 From: wpx <1143615697@qq.com> Date: Thu, 21 Nov 2024 15:44:21 +0800 Subject: [PATCH 15/63] update --- app/tssim/ocp_opt/jovan/ocp_opt.py | 112 +++++++++++------------ app/tssim/ocp_opt/jovan/solver_update.py | 19 ++-- 2 files changed, 59 insertions(+), 72 deletions(-) diff --git a/app/tssim/ocp_opt/jovan/ocp_opt.py b/app/tssim/ocp_opt/jovan/ocp_opt.py index aa6e097ae..2dfb89d9f 100644 --- a/app/tssim/ocp_opt/jovan/ocp_opt.py +++ b/app/tssim/ocp_opt/jovan/ocp_opt.py @@ -15,12 +15,12 @@ logger.setLevel('ERROR') #积分子问题 bm.set_backend("numpy") -pde = example_1() -n = 20 -q = 4 +pde = example_1(c=0) +n = 10 +q = 3 T = 1 -nt = 30 -maxit = 3 +nt = 200 +maxit = 15 mesh = TriangleMesh.from_box(pde.domain(), nx=n, ny=n) timeline = UniformTimeLine(0, T, nt) @@ -29,63 +29,53 @@ yspace= LagrangeFESpace(mesh, p=1) space = LagrangeFESpace(mesh, p=1) pspace = TensorFunctionSpace(space, (2,-1)) -solver = ocp_opt_solver(mesh, yspace, pspace, pde, timeline) +solver = ocp_opt_solver(mesh, yspace, pspace, pde, timeline, q=q) -ygodf = yspace.number_of_global_dofs() +ygdof = yspace.number_of_global_dofs() pgdof = pspace.number_of_global_dofs() -yisbdof = yspace.is_boundary_dof() -pisbdof = pspace.is_boundary_dof() -isbdof = bm.concatenate([yisbdof, pisbdof], axis=0)#(362,) - ally = [None]*(nt+1) allp = [None]*(nt+1) allu = [None]*(nt+1) +allz = [None]*(nt+1) y0 = yspace.function(yspace.interpolate(partial(pde.y_solution, time=0))) y0t = yspace.interpolate(partial(pde.y_t_solution, time=0)) -p0 = pspace.function(pspace.interpolate(partial(pde.p_solution, time=0))) #(242,) p0x1,p0x2 +p0 = pspace.function(pspace.interpolate(partial(pde.p_solution, time=0))) zn = yspace.function(yspace.interpolate(partial(pde.z_solution, time=T))) -znt = yspace.interpolate(partial(pde.z_t_solution, time=0)) -zn0 = yspace.function() -zn0[:] = zn[:] -zn1 = yspace.function() -zn2 = yspace.function() q2 = pspace.function() -un = yspace.function() -un[:] = solver.z_to_u(zn) #积分子 -allu[-1] = un ally[0] = y0 allp[0] = p0 +allz[-1] = zn A0 = solver.Forward_BForm_A0().assembly() b0_LForm = solver.Forward_LForm_b0() -A = solver.Forward_BForm_A().assembly() +FA = solver.Forward_BForm_A().assembly() Forward_b_LForm = solver.Forward_LForm_b() An = solver.Forward_BForm_A0().assembly() bn_LForm = solver.Backward_LForm_bn() -Backward_A = solver.Forward_BForm_A().assembly() +BA = solver.Forward_BForm_A().assembly() Backward_b_LForm = solver.Backward_LForm_b() for k in range(maxit): y1 = yspace.function() p1 = pspace.function() - + + ## 正向求解第0步 solver.Forward_0_update(allu[1]) - b0 = b0_LForm.assembly() - + Fb0 = b0_LForm.assembly() BC = DirichletBC(space=(yspace,pspace), threshold=(None, None), gd=(partial(pde.y_solution,time=dt), partial(pde.p_solution,time=dt)),method='interp') - A0, b0 = BC.apply(A0, b0) - x0 = spsolve(A0, b0, solver='scipy') + A0, b0 = BC.apply(A0, Fb0) + x0 = spsolve(A0, b0, solver='mumps') - y1[:] = x0[:ygodf] + y1[:] = x0[:ygdof] p1[:] = x0[-pgdof:] ally[1] = y1 allp[1] = p1 @@ -101,15 +91,15 @@ p2 = pspace.function() solver.Forward_update(ally[t1index-1], ally[t1index], allu[t1index+1], t1+dt) - Forward_b = Forward_b_LForm.assembly() + Fb = Forward_b_LForm.assembly() BC = DirichletBC(space=(yspace,pspace), threshold=(None, None), gd=(partial(pde.y_solution,time=t1+dt), partial(pde.p_solution,time=t1+dt)),method='interp') - Forward_A, Forward_b = BC.apply(A, Forward_b) - Forward_x = spsolve(Forward_A, Forward_b, solver='scipy') + Forward_A, Forward_b = BC.apply(FA, Fb) + Forward_x = spsolve(Forward_A, Forward_b, solver='mumps') - y2[:] = Forward_x[:ygodf] + y2[:] = Forward_x[:ygdof] p2[:] = Forward_x[-pgdof:] ally[t1index+1] = y2 allp[t1index+1] = p2 @@ -119,52 +109,54 @@ t1 = timeline.current_time_level() t1index = timeline.current_time_level_index() + zn1 = yspace.function() solver.Backward_n_update(ally[t1index-1], allp[t1index-1]) bn = bn_LForm.assembly() BC = DirichletBC(space=(yspace,pspace), threshold=(None, None), - gd=(partial(pde.y_solution,time = T-dt), - partial(pde.p_solution,time = T-dt)),method='interp') + gd=(partial(pde.z_solution,time = T-dt), + partial(pde.q_solution,time = T-dt)),method='interp') An, bn = BC.apply(An, bn) - xn = spsolve(An, bn, solver='scipy') - zn1[:] = xn[:ygodf] + xn = spsolve(An, bn, solver='mumps') + zn1[:] = xn[:ygdof] q2[:] = xn[-pgdof:] - - un1 = yspace.function() - un1[:] = solver.z_to_u(zn1) - allu[t1index-1] = un1 + allz[t1index-1] = zn1 + timeline.backward() ## 反向求解 for i in bm.arange(nt-1, 0, -1): t1 = timeline.current_time_level() t1index = timeline.current_time_level_index() - - + zn2 = yspace.function() + ##求第i-1步的z,q - solver.Backward_update(zn0, zn1, ally[t1index-1], allp[t1index-1], t1-dt) - Backward_b = Backward_b_LForm.assembly() + solver.Backward_update(allz[t1index+1], allz[t1index], ally[t1index-1], allp[t1index-1], t1-dt) + Bb = Backward_b_LForm.assembly() BC = DirichletBC(space=(yspace,pspace), threshold=(None, None), - gd=(partial(pde.y_solution,time = t1-dt), - partial(pde.p_solution,time = t1-dt)),method='interp') - Backward_A, Backward_b = BC.apply(Backward_A, Backward_b) - Backward_x = spsolve(Backward_A, Backward_b, solver='scipy') + gd=(partial(pde.z_solution,time = t1-dt), + partial(pde.q_solution,time = t1-dt)),method='interp') + Backward_A, Backward_b = BC.apply(BA, Bb) + Backward_x = spsolve(Backward_A, Backward_b, solver='mumps') - zn2[:] = Backward_x[:ygodf] + zn2[:] = Backward_x[:ygdof] q2 = Backward_x[-pgdof:] - - ##求第i-1步的u - un2 = yspace.function() - un2[:] = solver.z_to_u(zn2) - allu[t1index-1] = un2 - - zn0[:] = zn1[:] - zn1[:] = zn2[:] + allz[t1index-1] = zn2 timeline.backward() -ysolution = yspace.function(yspace.interpolate(partial(pde.y_solution, time=T))) + z_bar = solver.solve_z_bar(allz) + un_pre = allu[0] + if un_pre == None: + un_pre = 0 + for i in range(nt+1): + ufunction = yspace.function() + ufunction[:] = bm.max((0,z_bar)) - allz[i] + allu[i] = ufunction + print(f"第{k}次的前后u差别",bm.sum(allu[0] - un_pre)) + +#ysolution = yspace.function(yspace.interpolate(partial(pde.y_solution, time=T))) usolution = yspace.function(yspace.interpolate(partial(pde.u_solution, time=0))) -errory = mesh.error(ally[-1], ysolution) +#errory = mesh.error(ally[-1], ysolution) erroru = mesh.error(allu[0], usolution) -print(errory) +#print(errory) print(erroru) diff --git a/app/tssim/ocp_opt/jovan/solver_update.py b/app/tssim/ocp_opt/jovan/solver_update.py index 5085a5a1e..d196370b9 100644 --- a/app/tssim/ocp_opt/jovan/solver_update.py +++ b/app/tssim/ocp_opt/jovan/solver_update.py @@ -6,7 +6,6 @@ from fealpy.backend import backend_manager as bm from fealpy.sparse import COOTensor from functools import partial -from tensor_mass_integrator import TensorMassIntegrator from ocp_opt_pde import example_1 class ocp_opt_solver(): @@ -89,7 +88,6 @@ def coef(p, index=None): Ly.add_integrator(self.forward_0_b_coef) Lp = LinearForm(pspace) - #TODO:检查是不是一直是0 L = LinearBlockForm([Ly, Lp]) return L @@ -103,7 +101,7 @@ def coef_u1(bcs, index=None): result = (dt**2)*u1(bcs) return result self.forward_0_b_coef.source = coef_u1 - + self.forward_0_b_coef.clear() def Forward_LForm_b(self): yspace = self.yspace @@ -145,12 +143,6 @@ def coef_c(p, index=None): self.c_coef.clear() - ### 反向求解 - def z_to_u(self, z1): - result = bm.max(self.mesh.integral(z1), 0) - z1 #积分子 - return result - - ## TODO:Pd Yd 的求解 def Backward_LForm_bn(self): yspace = self.yspace pspace = self.pspace @@ -253,6 +245,9 @@ def coef_b_q(bcs, index=None): self.backward_q_b_coef.source = coef_b_q self.backward_q_b_coef.clear() - - - + def solve_z_bar(self, allz): + dt = self.dt + integral_z = bm.array([self.mesh.integral(i, q=self.q) for i in allz],dtype=bm.float64) + z_bar = (dt/2)*(integral_z[:-1] + integral_z[1:]) + return bm.sum(z_bar) + From fb28a335a647b689085e71f6873b729a947fce21 Mon Sep 17 00:00:00 2001 From: wpx <1143615697@qq.com> Date: Thu, 21 Nov 2024 20:48:05 +0800 Subject: [PATCH 16/63] update --- app/tssim/ocp_opt/jovan/ocp_opt.py | 27 ++++++++++++++++---------- app/tssim/ocp_opt/jovan/ocp_opt_pde.py | 5 +++-- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/app/tssim/ocp_opt/jovan/ocp_opt.py b/app/tssim/ocp_opt/jovan/ocp_opt.py index 2dfb89d9f..20f9c1560 100644 --- a/app/tssim/ocp_opt/jovan/ocp_opt.py +++ b/app/tssim/ocp_opt/jovan/ocp_opt.py @@ -4,23 +4,21 @@ from fealpy.old.timeintegratoralg import UniformTimeLine from fealpy.functionspace import TensorFunctionSpace from ocp_opt_pde import example_1 -#from solver import ocp_opt_solver from solver_update import ocp_opt_solver from fealpy.fem import DirichletBC -from scipy.sparse import coo_array, bmat from functools import partial from fealpy import logger from fealpy.solver import spsolve logger.setLevel('ERROR') #积分子问题 bm.set_backend("numpy") -pde = example_1(c=0) -n = 10 +pde = example_1() +n = 20 q = 3 T = 1 -nt = 200 -maxit = 15 +nt = 100 +maxit = 10 mesh = TriangleMesh.from_box(pde.domain(), nx=n, ny=n) timeline = UniformTimeLine(0, T, nt) @@ -154,9 +152,18 @@ allu[i] = ufunction print(f"第{k}次的前后u差别",bm.sum(allu[0] - un_pre)) -#ysolution = yspace.function(yspace.interpolate(partial(pde.y_solution, time=T))) +ysolution = yspace.function(yspace.interpolate(partial(pde.y_solution, time=T))) +psolution = pspace.function(pspace.interpolate(partial(pde.p_solution, time=T))) usolution = yspace.function(yspace.interpolate(partial(pde.u_solution, time=0))) -#errory = mesh.error(ally[-1], ysolution) +zsolution = yspace.function(yspace.interpolate(partial(pde.z_solution, time=0))) +qsolution = pspace.function(pspace.interpolate(partial(pde.q_solution, time=0))) +errory = mesh.error(ally[-1], ysolution) +errorp = mesh.error(allp[-1], psolution) erroru = mesh.error(allu[0], usolution) -#print(errory) -print(erroru) +errorz = mesh.error(allz[0], zsolution) +errorq = mesh.error(q2, qsolution) +print("y误差",errory) +print("p误差",errorp) +print("u误差",erroru) +print("z误差",errorz) +print("q误差",errorq) diff --git a/app/tssim/ocp_opt/jovan/ocp_opt_pde.py b/app/tssim/ocp_opt/jovan/ocp_opt_pde.py index 83ee4f5a3..c52347168 100644 --- a/app/tssim/ocp_opt/jovan/ocp_opt_pde.py +++ b/app/tssim/ocp_opt/jovan/ocp_opt_pde.py @@ -37,7 +37,7 @@ def y_solution(self, space, time): x1 = self.x1 x2 = self.x2 t = self.t - result = sp.lambdify([x1,x2,t], self.p[0], self.manager) + result = sp.lambdify([x1,x2,t], self.y, self.manager) return result(space[...,0], space[...,1], time) @cartesian @@ -160,6 +160,7 @@ def y_d_fun(self, space, time): t = self.t x1 = self.x1 x2 = self.x2 - self.y_d = y - sp.diff(z, t, 2) + sp.diff(q[0], x1) + sp.diff(q[1], x2) + #self.y_d = y - sp.diff(z, t, 2) + sp.diff(q[0], x1) + sp.diff(q[1], x2) + self.y_d = y - sp.diff(z, t, 2) - sp.diff(q[0], x1) - sp.diff(q[1], x2) - z result = sp.lambdify([x1,x2,t], self.y_d, self.manager) return result(space[...,0], space[...,1], time) From 25e7d9c34f76212109ad4f89f9310b526a4e9267 Mon Sep 17 00:00:00 2001 From: "brighthe98@gmail.com" Date: Thu, 21 Nov 2024 21:04:28 +0800 Subject: [PATCH 17/63] update --- app/gearx/utils.py | 6 +- ...p_detail_linear_element_hexahedron_mesh.py | 4 +- app/soptx/linear_elasticity/test_abaqus.py | 166 ++++++++ .../test_node_load_dirichlet_bc.py | 193 --------- .../short_cantilever_uniform_mesh_2d_OC.py | 2 +- app/soptx/soptx/opt/__init__.py | 5 +- app/soptx/soptx/opt/compliance.py | 3 +- app/soptx/soptx/opt/oc.py | 86 ++-- app/soptx/soptx/opt/volume.py | 13 +- app/soptx/soptx/pde/__init__.py | 6 +- app/soptx/soptx/pde/cantilever_2d.py | 70 ++++ app/soptx/soptx/pde/cantilever_3d.py | 84 +++- app/soptx/soptx/pde/short_cantilever_2d.py | 110 ----- app/soptx/soptx/solver/elastic_fem_solver.py | 50 ++- .../soptx/tests/filter_parameters_data.py | 27 -- app/soptx/soptx/tests/test_compliance.py | 376 ++++++++++-------- .../soptx/tests/test_filter_parameters.py | 65 --- app/soptx/soptx/tests/test_oc.py | 305 ++++++++------ fealpy/mesh/uniform_mesh_2d.py | 2 +- fealpy/mesh/uniform_mesh_3d.py | 98 ++++- 20 files changed, 890 insertions(+), 781 deletions(-) create mode 100644 app/soptx/linear_elasticity/test_abaqus.py delete mode 100644 app/soptx/linear_elasticity/test_node_load_dirichlet_bc.py create mode 100644 app/soptx/soptx/pde/cantilever_2d.py delete mode 100644 app/soptx/soptx/pde/short_cantilever_2d.py delete mode 100644 app/soptx/soptx/tests/filter_parameters_data.py delete mode 100644 app/soptx/soptx/tests/test_filter_parameters.py diff --git a/app/gearx/utils.py b/app/gearx/utils.py index 16f425ae5..219ac8b79 100644 --- a/app/gearx/utils.py +++ b/app/gearx/utils.py @@ -381,14 +381,16 @@ def export_to_inp(filename, nodes, elements, fixed_nodes, load_nodes, loads, you for i, load_node in enumerate(load_nodes): node_id = load_node forces = loads[i] - file.write(f"GearInstance.{node_id}, 1, {forces[0]}, 2, {forces[1]}, 3, {forces[2]}\n") + file.write(f"GearInstance.{node_id}, 1, {forces[0]}\n") + file.write(f"GearInstance.{node_id}, 2, {forces[1]}\n") + file.write(f"GearInstance.{node_id}, 3, {forces[2]}\n") file.write("*Output, field, variable=PRESELECT\n") file.write("*Output, history, variable=PRESELECT\n") file.write("*End Step\n") file.write("** Output Global Stiffness Matrix\n") file.write("*Step, name=Global_Stiffness_Matrix\n") - file.write("*MATRIX GENERATE, STIFFNESS\n") + file.write("*MATRIX GENERATE, STIFFNESS, element by element\n") file.write("*MATRIX OUTPUT, STIFFNESS, FORMAT=COORDINATE\n") file.write("*End Step\n") diff --git a/app/soptx/linear_elasticity/exp_detail_linear_element_hexahedron_mesh.py b/app/soptx/linear_elasticity/exp_detail_linear_element_hexahedron_mesh.py index 5547b49b9..760b34bd2 100644 --- a/app/soptx/linear_elasticity/exp_detail_linear_element_hexahedron_mesh.py +++ b/app/soptx/linear_elasticity/exp_detail_linear_element_hexahedron_mesh.py @@ -59,12 +59,14 @@ def dirichlet(self, points: TensorLike) -> TensorLike: bm.set_backend('numpy') -nx, ny, nz = 3, 3, 3 +nx, ny, nz = 2, 2, 2 mesh = HexahedronMesh.from_box(box=[0, 1, 0, 1, 0, 1], nx=nx, ny=ny, nz=nz, device=bm.get_device('cpu')) NC = mesh.number_of_cells() cm = mesh.cell_volume() +node = mesh.entity('node') +cell = mesh.entity('cell') space = LagrangeFESpace(mesh, p=1, ctype='C') diff --git a/app/soptx/linear_elasticity/test_abaqus.py b/app/soptx/linear_elasticity/test_abaqus.py new file mode 100644 index 000000000..32cdc9013 --- /dev/null +++ b/app/soptx/linear_elasticity/test_abaqus.py @@ -0,0 +1,166 @@ +from fealpy.backend import backend_manager as bm + +from fealpy.mesh import HexahedronMesh +from fealpy.material.elastic_material import LinearElasticMaterial +from fealpy.fem.linear_elastic_integrator import LinearElasticIntegrator + +from fealpy.functionspace import LagrangeFESpace, TensorFunctionSpace +from fealpy.typing import TensorLike +from fealpy.decorator import cartesian +from fealpy.sparse import COOTensor +from fealpy.solver import cg + +class BoxDomainPolyLoaded3d(): + def domain(self): + return [0, 1, 0, 1, 0, 1] + + @cartesian + def source(self, points: TensorLike): + x = points[..., 0] + y = points[..., 1] + z = points[..., 2] + val = bm.zeros(points.shape, + dtype=points.dtype, device=bm.get_device(points)) + mu = 1 + factor1 = -400 * mu * (2 * y - 1) * (2 * z - 1) + term1 = 3 * (x ** 2 - x) ** 2 * (y ** 2 - y + z ** 2 - z) + term2 = (1 - 6 * x + 6 * x ** 2) * (y ** 2 - y) * (z ** 2 - z) + val[..., 0] = factor1 * (term1 + term2) + + factor2 = 200 * mu * (2 * x - 1) * (2 * z - 1) + term1 = 3 * (y ** 2 - y) ** 2 * (x ** 2 - x + z ** 2 - z) + term2 = (1 - 6 * y + 6 * y ** 2) * (x ** 2 - x) * (z ** 2 - z) + val[..., 1] = factor2 * (term1 + term2) + + factor3 = 200 * mu * (2 * x - 1) * (2 * y - 1) + term1 = 3 * (z ** 2 - z) ** 2 * (x ** 2 - x + y ** 2 - y) + term2 = (1 - 6 * z + 6 * z ** 2) * (x ** 2 - x) * (y ** 2 - y) + val[..., 2] = factor3 * (term1 + term2) + + return val + + @cartesian + def solution(self, points: TensorLike): + x = points[..., 0] + y = points[..., 1] + z = points[..., 2] + val = bm.zeros(points.shape, + dtype=points.dtype, device=bm.get_device(points)) + + mu = 1 + val[..., 0] = 200*mu*(x-x**2)**2 * (2*y**3-3*y**2+y) * (2*z**3-3*z**2+z) + val[..., 1] = -100*mu*(y-y**2)**2 * (2*x**3-3*x**2+x) * (2*z**3-3*z**2+z) + val[..., 2] = -100*mu*(z-z**2)**2 * (2*y**3-3*y**2+y) * (2*x**3-3*x**2+x) + + return val + + def dirichlet(self, points: TensorLike) -> TensorLike: + + return bm.zeros(points.shape, + dtype=points.dtype, device=bm.get_device(points)) + + +bm.set_backend('numpy') +nx, ny, nz = 1, 1, 1 +mesh = HexahedronMesh.from_box(box=[0, 1, 0, 1, 0, 1], + nx=nx, ny=ny, nz=nz, device=bm.get_device('cpu')) + +NC = mesh.number_of_cells() +cm = mesh.cell_volume() +node = mesh.entity('node') +cell = mesh.entity('cell') + +space = LagrangeFESpace(mesh, p=1, ctype='C') + +q = 2 +qf = mesh.quadrature_formula(q) +bcs, ws = qf.get_quadrature_points_and_weights() +phi = space.basis(bcs) # (1, NQ, ldof) +gphi = space.grad_basis(bc=bcs) # (NC, NQ, ldof, GD) + +tensor_space = TensorFunctionSpace(space, shape=(3, -1)) +tgdof = tensor_space.number_of_global_dofs() +phi_tensor = tensor_space.basis(bcs) # (1, NQ, tldof, GD) +cell2tdof = tensor_space.cell_to_dof() # (NC, tldof) + +E = 206e3 +nu = 0.3 +lam = (E * nu) / ((1.0 + nu) * (1.0 - 2.0 * nu)) +mu = E / (2.0 * (1.0 + nu)) +linear_elastic_material = LinearElasticMaterial(name='lam1_mu1', + lame_lambda=lam, shear_modulus=mu, + hypo='3D', device=bm.get_device(mesh)) + +B = linear_elastic_material.strain_matrix(dof_priority=True, + gphi=gphi, shear_order=['xy', 'yz', 'zx']) +D = linear_elastic_material.elastic_matrix(bcs) +KE = bm.einsum('q, c, cqki, cqkl, cqlj -> cij', ws, cm, B, D, B) + +integrator_K = LinearElasticIntegrator(material=linear_elastic_material, q=tensor_space.p+3) +KE_maual = integrator_K.assembly(space=tensor_space) + +KE0 = KE[0] + +I = bm.broadcast_to(cell2tdof[:, :, None], shape=KE.shape) +J = bm.broadcast_to(cell2tdof[:, None, :], shape=KE.shape) +K = COOTensor( + indices = bm.empty((2, 0), dtype=bm.int32, device=bm.get_device(mesh)), + values = bm.empty((0, ), dtype=bm.float64, device=bm.get_device(mesh)), + spshape = (tgdof, tgdof)) +indices = bm.stack([I.ravel(), J.ravel()], axis=0) +K = K.add(COOTensor(indices, KE.reshape(-1), (tgdof, tgdof))) + +pde = BoxDomainPolyLoaded3d() +ps = mesh.bc_to_point(bc=bcs) +f = pde.source(ps) # (NC, NQ, GD) +FE = bm.einsum('q, c, cqid, cqd -> ci', ws, cm, phi_tensor, f) # (NC, tldof) + +F = COOTensor( + indices = bm.empty((1, 0), dtype=bm.int32, device=bm.get_device(mesh)), + values = bm.empty((0, ), dtype=bm.float64, device=bm.get_device(mesh)), + spshape = (tgdof, )) +indices = cell2tdof.reshape(1, -1) +F = F.add(COOTensor(indices, FE.reshape(-1), (tgdof, ))).to_dense() + +from app.gearx.utils import * +F_load_nodes = bm.transpose(F.reshape(3, -1)) +load_node_indices = cell[0] +fixed_node_index = bm.tensor([0]) +export_to_inp(filename='/home/heliang/FEALPy_Development/fealpy/app/soptx/linear_elasticity/local_stiffness_matrix.inp', + nodes=node, elements=cell, fixed_nodes=fixed_node_index, load_nodes=load_node_indices, loads=F_load_nodes, + young_modulus=206e3, poisson_ratio=0.3, density=7850.0) + + + +isDDof = tensor_space.is_boundary_dof(threshold=None, method='interp') +kwargs = K.values_context() +# 1. 移除边界自由度相关的非零元素 +indices = K.indices() +remove_flag = bm.logical_or(isDDof[indices[0, :]], isDDof[indices[1, :]]) +retain_flag = bm.logical_not(remove_flag) +new_indices = indices[:, retain_flag] +new_values = K.values()[..., retain_flag] +K = COOTensor(new_indices, new_values, K.sparse_shape) + +# 2. 在边界自由度位置添加单位对角元素 +index = bm.nonzero(isDDof)[0] +shape = new_values.shape[:-1] + (len(index), ) +one_values = bm.ones(shape, **kwargs) +one_indices = bm.stack([index, index], axis=0) +K1 = COOTensor(one_indices, one_values, K.sparse_shape) +K = K.add(K1).coalesce() + +# 1. 边界插值 +uh_bd = bm.zeros(tensor_space.number_of_global_dofs(), + dtype=bm.float64, device=bm.get_device(mesh)) +uh_bd, isDDof = tensor_space.boundary_interpolate(gd=pde.dirichlet, uh=uh_bd, + threshold=None, method='interp') +# 2. 修改右端向量 +F = F - K.matmul(uh_bd) +F = bm.set_at(F, isDDof, uh_bd[isDDof]) + +uh = tensor_space.function() +uh[:] = cg(K, F, maxiter=1000, atol=1e-14, rtol=1e-14) +u_exact = tensor_space.interpolate(pde.solution) +error = mesh.error(u=uh, v=pde.solution, q=tensor_space.p+3, power=2) +print("----------------------") \ No newline at end of file diff --git a/app/soptx/linear_elasticity/test_node_load_dirichlet_bc.py b/app/soptx/linear_elasticity/test_node_load_dirichlet_bc.py deleted file mode 100644 index a59da2d63..000000000 --- a/app/soptx/linear_elasticity/test_node_load_dirichlet_bc.py +++ /dev/null @@ -1,193 +0,0 @@ -from fealpy.backend import backend_manager as bm - -from fealpy.mesh import HexahedronMesh -from fealpy.material.elastic_material import LinearElasticMaterial -from fealpy.functionspace import LagrangeFESpace, TensorFunctionSpace -from fealpy.fem.linear_elastic_integrator import LinearElasticIntegrator -from fealpy.fem.vector_source_integrator import VectorSourceIntegrator -from fealpy.fem.dirichlet_bc import DirichletBC - -from fealpy.fem.bilinear_form import BilinearForm -from fealpy.fem.linear_form import LinearForm -from fealpy.typing import TensorLike -from fealpy.decorator import cartesian -from fealpy.sparse import COOTensor, CSRTensor - -class BoxDomainPolyLoaded3d(): - def domain(self): - return [0, 1, 0, 1, 0, 1] - - @cartesian - def source(self, points: TensorLike): - x = points[..., 0] - y = points[..., 1] - z = points[..., 2] - val = bm.zeros(points.shape, - dtype=points.dtype, device=bm.get_device(points)) - mu = 1 - factor1 = -400 * mu * (2 * y - 1) * (2 * z - 1) - term1 = 3 * (x ** 2 - x) ** 2 * (y ** 2 - y + z ** 2 - z) - term2 = (1 - 6 * x + 6 * x ** 2) * (y ** 2 - y) * (z ** 2 - z) - val[..., 0] = factor1 * (term1 + term2) - - factor2 = 200 * mu * (2 * x - 1) * (2 * z - 1) - term1 = 3 * (y ** 2 - y) ** 2 * (x ** 2 - x + z ** 2 - z) - term2 = (1 - 6 * y + 6 * y ** 2) * (x ** 2 - x) * (z ** 2 - z) - val[..., 1] = factor2 * (term1 + term2) - - factor3 = 200 * mu * (2 * x - 1) * (2 * y - 1) - term1 = 3 * (z ** 2 - z) ** 2 * (x ** 2 - x + y ** 2 - y) - term2 = (1 - 6 * z + 6 * z ** 2) * (x ** 2 - x) * (y ** 2 - y) - val[..., 2] = factor3 * (term1 + term2) - - return val - - @cartesian - def solution(self, points: TensorLike): - x = points[..., 0] - y = points[..., 1] - z = points[..., 2] - val = bm.zeros(points.shape, - dtype=points.dtype, device=bm.get_device(points)) - - mu = 1 - val[..., 0] = 200*mu*(x-x**2)**2 * (2*y**3-3*y**2+y) * (2*z**3-3*z**2+z) - val[..., 1] = -100*mu*(y-y**2)**2 * (2*x**3-3*x**2+x) * (2*z**3-3*z**2+z) - val[..., 2] = -100*mu*(z-z**2)**2 * (2*y**3-3*y**2+y) * (2*x**3-3*x**2+x) - - return val - - def dirichlet(self, points: TensorLike) -> TensorLike: - - return bm.zeros(points.shape, - dtype=points.dtype, device=bm.get_device(points)) - -bm.set_backend('numpy') -nx, ny, nz = 3, 3, 3 -mesh = HexahedronMesh.from_box(box=[0, 1, 0, 1, 0, 1], nx=nx, ny=ny, nz=nz) -NC = mesh.number_of_cells() -cm = mesh.entity_measure('cell') - -cell = mesh.entity("cell") -cell_indices = [10, 13, 16] -cell_target = cell[cell_indices, :] - -node = mesh.entity("node") - -# 载荷点 a 的坐标 -a = bm.array([0.5, 0.16666667, 0.16666667], dtype=bm.float64) - -# 载荷点 b 的坐标 -b = bm.array([0.5, 0.5, 0.16666667], dtype=bm.float64) - -# 载荷点 c 的坐标 -c = bm.array([0.5, 0.83333333, 0.16666667], dtype=bm.float64) - -# 如果您想将三个点放在一个数组中 -load_points = bm.array([a, b, c], dtype=bm.float64) - -load_values = bm.array([2.0, 3.0, 4.0], dtype=bm.float64) # 对应的载荷大小 - -def physical_to_local_coords(point, cell_coords): - """ - 将物理坐标转换为局部坐标(参考单元坐标)。 - - 参数: - - point: ndarray,形状为 (3,),待转换的物理坐标 (x, y, z)。 - - cell_coords: ndarray,形状为 (8, 3),单元的8个节点的物理坐标。 - - 返回: - - xi, eta, zeta: float,局部坐标。 - - 注意: - 该函数假定单元是线性的八节点六面体单元,参考单元的局部坐标范围为 [0, 1]。 - """ - # 计算单元在各个方向的最小值和最大值 - x_min, y_min, z_min = cell_coords.min(axis=0) - x_max, y_max, z_max = cell_coords.max(axis=0) - - # 计算局部坐标 ξ, η, ζ - xi = (point[0] - x_min) / (x_max - x_min) - eta = (point[1] - y_min) / (y_max - y_min) - zeta = (point[2] - z_min) / (z_max - z_min) - - return xi, eta, zeta - -cell_coords_a = node[cell[9]] -cell_coords_b = node[cell[12]] -cell_coords_c = node[cell[15]] -# 计算局部坐标 -xi_a, eta_a, zeta_a = physical_to_local_coords(a, cell_coords_a) -xi_b, eta_b, zeta_b = physical_to_local_coords(b, cell_coords_b) -xi_c, eta_c, zeta_c = physical_to_local_coords(c, cell_coords_c) - -q = 1 -qf = mesh.quadrature_formula(q) -bcs_a = (bm.tensor([[xi_a, 1-xi_a]]), bm.tensor([[eta_a, 1-eta_a]]), bm.tensor([[zeta_a, 1-zeta_a]])) -bcs_b = (bm.tensor([[xi_b, 1-xi_b]]), bm.tensor([[eta_b, 1-eta_b]]), bm.tensor([[zeta_b, 1-zeta_b]])) -bcs_c = (bm.tensor([[xi_c, 1-xi_c]]), bm.tensor([[eta_c, 1-eta_c]]), bm.tensor([[zeta_c, 1-zeta_c]])) - -# bcs, ws = qf.get_quadrature_points_and_weights() # bcs ((1, 2), (1, 2), (1, 2)), ws (NQ, ) - -space = LagrangeFESpace(mesh, p=1, ctype='C') -tensor_space_dof = TensorFunctionSpace(space, shape=(3, -1)) -tgdof = tensor_space_dof.number_of_global_dofs() -tldof = tensor_space_dof.number_of_local_dofs() -GD = 3 -cell2dof_dof = tensor_space_dof.cell_to_dof() # (NC, tldof) - -phi_a_dof = tensor_space_dof.basis(bcs_a) # (1, 1, tldof, GD) -phi_b_dof = tensor_space_dof.basis(bcs_b) # (1, 1, tldof, GD) -phi_c_dof = tensor_space_dof.basis(bcs_c) # (1, 1, tldof, GD) - -FE_a_dof = load_values[0] * phi_a_dof # (1, 1, tldof, GD) -FE_b_dof = load_values[1] * phi_b_dof # (1, 1, tldof, GD) -FE_c_dof = load_values[2] * phi_c_dof # (1, 1, tldof, GD) - -FE_a_dof = bm.einsum("qcid -> qci", FE_a_dof) # (1, 1, tldof) -FE_b_dof = bm.einsum("qcid -> qci", FE_b_dof) # (1, 1, tldof) -FE_c_dof = bm.einsum("qcid -> qci", FE_c_dof) # (1, 1, tldof) - -FE_dof = bm.concatenate([FE_a_dof, FE_b_dof, FE_c_dof], axis=1) # (1, 3, tldof) -FE_all_dof = bm.zeros((1, NC, tldof), dtype=bm.float64) -FE_all_dof[0, cell_indices, :] = FE_dof[0, :, :] # (1, NC, tldof) - -F_dof = COOTensor( - indices = bm.empty((1, 0), dtype=bm.int32, device=bm.get_device(space)), - values = bm.empty((0, ), dtype=bm.float64, device=bm.get_device(space)), - spshape = (tgdof, )) -indices = cell2dof_dof.reshape(1, -1) -test = FE_all_dof.reshape(-1) -F_dof = F_dof.add(COOTensor(indices, FE_all_dof.reshape(-1), (tgdof, ))).to_dense() - -def is_dirichlet_boundary_dof(points: TensorLike) -> TensorLike: - domain = [0, 1, 0, 1, 0, 1] - eps = 1e-12 - x = points[..., 0] - - coord = bm.abs(x - domain[0]) < eps - - return coord -pde = BoxDomainPolyLoaded3d() - -isDDof_dof = tensor_space_dof.is_boundary_dof(threshold=is_dirichlet_boundary_dof, method='interp') - -linear_elastic_material = LinearElasticMaterial(name='lam1_mu1', - lame_lambda=1, shear_modulus=1, - hypo='3D', device=bm.get_device(mesh)) - -integrator_K = LinearElasticIntegrator(material=linear_elastic_material, q=2) - -bform = BilinearForm(tensor_space_dof) -bform.add_integrator(integrator_K) -K = bform.assembly(format='csr') -K_dense = K.to_dense() - -dbc = DirichletBC(space=tensor_space_dof, - gd=pde.dirichlet, - threshold=isDDof_dof, - method='interp') -K, F = dbc.apply(A=K, f=F_dof, check=True) - -print('---------------------------------------------') - diff --git a/app/soptx/soptx/examples/compliance_minimization/short_cantilever_uniform_mesh_2d_OC.py b/app/soptx/soptx/examples/compliance_minimization/short_cantilever_uniform_mesh_2d_OC.py index 185b2e375..da1885b78 100644 --- a/app/soptx/soptx/examples/compliance_minimization/short_cantilever_uniform_mesh_2d_OC.py +++ b/app/soptx/soptx/examples/compliance_minimization/short_cantilever_uniform_mesh_2d_OC.py @@ -8,7 +8,7 @@ from app.soptx.soptx.material.material_properties import ElasticMaterialProperties, SIMPInterpolation -from app.soptx.soptx.pde.short_cantilever_2d import ShortCantilever2dOneData +from soptx.pde.cantilever_2d import ShortCantilever2dOneData from app.soptx.soptx.opt.volume_objective import VolumeConstraint from app.soptx.soptx.opt.compliance_objective import ComplianceObjective diff --git a/app/soptx/soptx/opt/__init__.py b/app/soptx/soptx/opt/__init__.py index 9d5a26f6e..8e21a5f0d 100644 --- a/app/soptx/soptx/opt/__init__.py +++ b/app/soptx/soptx/opt/__init__.py @@ -1,7 +1,7 @@ from .base import ObjectiveBase, ConstraintBase, OptimizerBase from .compliance import ComplianceObjective from .volume import VolumeConstraint -from .oc import OCOptimizer +from .oc import OCOptimizer, save_optimization_history __all__ = [ 'ObjectiveBase', @@ -9,5 +9,6 @@ 'OptimizerBase', 'ComplianceObjective', 'VolumeConstraint' - 'OCOptimizer' + 'OCOptimizer', + 'save_optimization_history', ] \ No newline at end of file diff --git a/app/soptx/soptx/opt/compliance.py b/app/soptx/soptx/opt/compliance.py index 064501f3a..e59882799 100644 --- a/app/soptx/soptx/opt/compliance.py +++ b/app/soptx/soptx/opt/compliance.py @@ -65,7 +65,8 @@ def _update_u(self, rho: TensorLike) -> TensorLike: # 更新求解器中的密度并求解 self.solver.update_density(rho) - self._current_u = self.solver.solve_cg().displacement + self._current_u = self.solver.solve().displacement + # self._current_u = self.solver.solve_cg().displacement # self._current_rho = bm.copy(rho) # 这里的 copy 导致内部状态与外部不同步 self._current_rho = rho # 直接引用,内部状态会随外部更新 diff --git a/app/soptx/soptx/opt/oc.py b/app/soptx/soptx/opt/oc.py index 9f14c0cf7..b894a64f6 100644 --- a/app/soptx/soptx/opt/oc.py +++ b/app/soptx/soptx/opt/oc.py @@ -17,6 +17,31 @@ class OCOptions: initial_lambda: float = 1e9 # 初始 lambda 值 bisection_tol: float = 1e-3 # 二分法收敛容差 +@dataclass +class OptimizationHistory: + """优化过程的历史记录""" + densities: list # 密度场历史 + + def __init__(self): + """初始化各个记录列表""" + self.densities = [] + + def log_iteration(self, iter_idx: int, obj_val: float, volume: float, + change: float, time: float, density: TensorLike): + """记录一次迭代的信息 + + Parameters + ---------- + density : 当前密度场 + """ + self.densities.append(density.copy()) + + print(f"Iteration: {iter_idx + 1}, " + f"Objective: {obj_val:.3f}, " + f"Volume: {volume:.12f}, " + f"Change: {change:.3f}, " + f"Time: {time:.3f} sec") + class OCOptimizer(OptimizerBase): """Optimality Criteria (OC) 优化器 @@ -68,25 +93,19 @@ def _update_density(self, move = self.options.move_limit # OC update scheme - rho_new = bm.maximum(bm.tensor(0.0, dtype=rho.dtype), - bm.maximum(rho - move, - bm.minimum(bm.tensor(1.0, dtype=rho.dtype), - bm.minimum(rho + move, rho * bm.sqrt(-dc / dg / lmid)))) + rho_new = bm.maximum( + bm.tensor(0.0, dtype=rho.dtype), + bm.maximum( + rho - move, + bm.minimum( + bm.tensor(1.0, dtype=rho.dtype), + bm.minimum( + rho + move, + rho * bm.sqrt(-dc / dg / lmid) + ) ) - # OC update scheme - # rho_new = bm.maximum( - # bm.tensor(0.0, dtype=rho.dtype), - # bm.maximum( - # rho - move, - # bm.minimum( - # bm.tensor(1.0, dtype=rho.dtype), - # bm.minimum( - # rho + move, - # rho * bm.sqrt(-dc / dg / lmid) - # ) - # ) - # ) - # ) + ) + ) return rho_new @@ -97,7 +116,7 @@ def optimize(self, rho: TensorLike, **kwargs) -> TensorLike: ---------- rho : 初始密度场 **kwargs : 其他参数,例如: - - beta: Heaviside投影参数 + - beta: Heaviside 投影参数 Returns ------- @@ -115,6 +134,9 @@ def optimize(self, rho: TensorLike, **kwargs) -> TensorLike: rho_phys = (self.filter.get_physical_density(rho, filter_params) if self.filter is not None else rho) + # 初始化历史记录 + history = OptimizationHistory() + # 优化主循环 for iter_idx in range(max_iters): start_time = time() @@ -151,17 +173,27 @@ def optimize(self, rho: TensorLike, **kwargs) -> TensorLike: # 更新设计变量,确保目标函数内部状态同步 rho = rho_new - # 输出迭代信息 iteration_time = time() - start_time - print(f"Iteration: {iter_idx + 1}, " - f"Objective: {obj_val:.3f}, " - f"Volume: {bm.mean(rho_phys[:]):.12f}, " - f"Change: {change:.3f}, " - f"Time: {iteration_time:.3f} sec") - + + history.log_iteration(iter_idx, obj_val, bm.mean(rho_phys[:]), + change, iteration_time, rho_phys[:]) + # 收敛检查 if change <= tol: print(f"Converged after {iter_idx + 1} iterations") break - return rho \ No newline at end of file + return rho, history + +def save_optimization_history(mesh, history: OptimizationHistory, save_path: str): + """保存优化过程的所有迭代结果 + + Parameters + ---------- + mesh : 有限元网格对象 + history : 优化历史记录 + save_path : 保存路径 + """ + for i, density in enumerate(history.densities): + mesh.celldata['density'] = density + mesh.to_vtk(f"{save_path}/density_iter_{i:03d}.vts") \ No newline at end of file diff --git a/app/soptx/soptx/opt/volume.py b/app/soptx/soptx/opt/volume.py index 66cfffd21..a88c7a299 100644 --- a/app/soptx/soptx/opt/volume.py +++ b/app/soptx/soptx/opt/volume.py @@ -30,7 +30,9 @@ def __init__(self, self.volume_fraction = volume_fraction self.filter = filter - def fun(self, rho: TensorLike) -> float: + def fun(self, + rho: TensorLike, + u: Optional[TensorLike] = None) -> float: """计算体积约束函数值 Parameters @@ -48,9 +50,8 @@ def fun(self, rho: TensorLike) -> float: volfrac_true = (bm.einsum('c, c -> ', cell_measure, rho) / bm.sum(cell_measure)) - # gneq = volfrac_true - self.volume_fraction - # gneq = (volfrac_true - self.volume_fraction) * NC - gneq = bm.sum(rho[:]) - self.volume_fraction * NC + gneq = (volfrac_true - self.volume_fraction) * NC + # gneq = bm.sum(rho[:]) - self.volume_fraction * NC return gneq @@ -78,10 +79,6 @@ def jac(self, # 明确指定这是约束函数的梯度 return self.filter.filter_sensitivity(gradient, rho, 'constraint', filter_params) - # 对梯度应用滤波 - # gradient = self.filter.filter_sensitivity(gradient, rho, filter_params) - - return gradient def hess(self, rho: TensorLike, lambda_: Dict[str, Any]) -> TensorLike: """计算体积约束 Hessian 矩阵(未实现) diff --git a/app/soptx/soptx/pde/__init__.py b/app/soptx/soptx/pde/__init__.py index 7cb40323a..ae0f03341 100644 --- a/app/soptx/soptx/pde/__init__.py +++ b/app/soptx/soptx/pde/__init__.py @@ -1,6 +1,10 @@ # 导入需要公开的类 from .mbb_beam_2d import MBBBeam2dData1 +from .cantilever_2d import Cantilever2dData1 +from .cantilever_3d import Cantilever3dData1 # 指定可导出的内容 -__all__ = ['MBBBeam2dData1'] +__all__ = ['MBBBeam2dData1', + 'Cantilever2dData1', + 'Cantilever3dData1'] diff --git a/app/soptx/soptx/pde/cantilever_2d.py b/app/soptx/soptx/pde/cantilever_2d.py new file mode 100644 index 000000000..bbe2688f3 --- /dev/null +++ b/app/soptx/soptx/pde/cantilever_2d.py @@ -0,0 +1,70 @@ +from fealpy.backend import backend_manager as bm + +from fealpy.typing import TensorLike + +from typing import Tuple, Callable +from builtins import list + +class Cantilever2dData1: + def __init__(self, + xmin: float=0, xmax: float=160, + ymin: float=0, ymax: float=100): + """ + flip_direction = True + 0 ------- 3 ------- 6 + | 0 | 2 | + 1 ------- 4 ------- 7 + | 1 | 3 | + 2 ------- 5 ------- 8 + """ + self.xmin, self.xmax = xmin, xmax + self.ymin, self.ymax = ymin, ymax + self.eps = 1e-12 + + def domain(self) -> list: + + box = [self.xmin, self.xmax, self.ymin, self.ymax] + + return box + + def force(self, points: TensorLike) -> TensorLike: + domain = self.domain() + + x = points[..., 0] + y = points[..., 1] + + coord = ( + (bm.abs(x - domain[1]) < self.eps) & + (bm.abs(y - domain[2]) < self.eps) + ) + val = bm.zeros(points.shape, dtype=points.dtype, device=bm.get_device(points)) + val[coord, 1] = -1 + + return val + + def dirichlet(self, points: TensorLike) -> TensorLike: + + return bm.zeros(points.shape, dtype=points.dtype, device=bm.get_device(points)) + + def is_dirichlet_boundary_dof_x(self, points: TensorLike) -> TensorLike: + domain = self.domain() + + x = points[..., 0] + + coord = bm.abs(x - domain[0]) < self.eps + + return coord + + def is_dirichlet_boundary_dof_y(self, points: TensorLike) -> TensorLike: + domain = self.domain() + + x = points[..., 0] + + coord = bm.abs(x - domain[0]) < self.eps + + return coord + + def threshold(self) -> Tuple[Callable, Callable]: + + return (self.is_dirichlet_boundary_dof_x, + self.is_dirichlet_boundary_dof_y) diff --git a/app/soptx/soptx/pde/cantilever_3d.py b/app/soptx/soptx/pde/cantilever_3d.py index 888756c1b..02d18c270 100644 --- a/app/soptx/soptx/pde/cantilever_3d.py +++ b/app/soptx/soptx/pde/cantilever_3d.py @@ -1,28 +1,50 @@ from fealpy.backend import backend_manager as bm from fealpy.typing import TensorLike +from typing import Tuple, Callable -class Cantilever3dOneData: - def __init__(self, nx: int, ny: int, nz: int): + +class Cantilever3dData1: + def __init__(self, + xmin: float=0, xmax: float=60, + ymin: float=0, ymax: float=20, + zmin: float=0, zmax: float=4): """ flip_direction = 'y' - 1------- 5 - / | /| - 3 ------- 7 | - | | | | - | 0------|-4 - | / |/ - 2 ------- 6 + 1------- 5 + / | /| + 3 ------- 7 | + | | | | + | 0------|-4 + | / |/ + 2 ------- 6 """ - self.nx = nx - self.ny = ny - self.nz = nz + self.xmin, self.xmax = xmin, xmax + self.ymin, self.ymax = ymin, ymax + self.zmin, self.zmax = zmin, zmax + self.eps = 1e-12 + + def domain(self) -> list: + + box = [self.xmin, self.xmax, + self.ymin, self.ymax, + self.zmin, self.zmax] + + return box def force(self, points: TensorLike) -> TensorLike: + domain = self.domain() - val = bm.zeros(points.shape, dtype=points.dtype) + x = points[..., 0] + y = points[..., 1] + z = points[..., 2] - val[-(self.nz+1):, 1] = -1 + coord = ( + (bm.abs(x - domain[1]) < self.eps) & + (bm.abs(y - domain[2]) < self.eps) + ) + val = bm.zeros(points.shape, dtype=points.dtype, device=bm.get_device(points)) + val[coord, 1] = -1 return val @@ -30,9 +52,37 @@ def dirichlet(self, points: TensorLike) -> TensorLike: return bm.zeros(points.shape, dtype=points.dtype) - def is_dirichlet_boundary_face(self, face_centers: TensorLike) -> TensorLike: + def is_dirichlet_boundary_dof_x(self, points: TensorLike) -> TensorLike: + domain = self.domain() + + x = points[..., 0] + + coord = bm.abs(x - domain[0]) < self.eps + + return coord + + def is_dirichlet_boundary_dof_y(self, points: TensorLike) -> TensorLike: + domain = self.domain() + + x = points[..., 0] + + coord = bm.abs(x - domain[0]) < self.eps + + return coord + + def is_dirichlet_boundary_dof_z(self, points: TensorLike) -> TensorLike: + domain = self.domain() + + x = points[..., 0] + + coord = bm.abs(x - domain[0]) < self.eps + + return coord + + def threshold(self) -> Tuple[Callable, Callable]: - left_face = (face_centers[:, 0] == 0.0) + return (self.is_dirichlet_boundary_dof_x, + self.is_dirichlet_boundary_dof_y, + self.is_dirichlet_boundary_dof_z) - return left_face \ No newline at end of file diff --git a/app/soptx/soptx/pde/short_cantilever_2d.py b/app/soptx/soptx/pde/short_cantilever_2d.py deleted file mode 100644 index 822ae20cc..000000000 --- a/app/soptx/soptx/pde/short_cantilever_2d.py +++ /dev/null @@ -1,110 +0,0 @@ -from fealpy.backend import backend_manager as bm - -from fealpy.typing import TensorLike - -from typing import Tuple, Callable -from builtins import list - -class ShortCantilever2dData1: - def __init__(self): - """ - flip_direction = True - 0 ------- 3 ------- 6 - | 0 | 2 | - 1 ------- 4 ------- 7 - | 1 | 3 | - 2 ------- 5 ------- 8 - """ - self.eps = 1e-12 - - def domain(self, - xmin: float=0, xmax: float=4, - ymin: float=0, ymax: float=4) -> list: - - box = [xmin, xmax, ymin, ymax] - - return box - - def force(self, points: TensorLike) -> TensorLike: - domain = self.domain() - - x = points[..., 0] - y = points[..., 1] - - coord = ( - (bm.abs(x - domain[0]) < self.eps) & - (bm.abs(y - domain[3]) < self.eps) - ) - val = bm.zeros(points.shape, dtype=points.dtype, device=bm.get_device(points)) - val[coord, 1] = -1 - - return val - - def dirichlet(self, points: TensorLike) -> TensorLike: - - return bm.zeros(points.shape, dtype=points.dtype, device=bm.get_device(points)) - - def is_dirichlet_boundary_dof(self, points: TensorLike) -> TensorLike: - domain = self.domain() - - x = points[..., 0] - - coord = bm.abs(x - domain[0]) < self.eps - - return coord - - def threshold(self) -> Callable: - - return self.is_dirichlet_boundary_dof - -class ShortCantilever2dOneData: - def __init__(self, nx: int, ny: int): - """ - flip_direction = True - 0 ------- 3 ------- 6 - | 0 | 2 | - 1 ------- 4 ------- 7 - | 1 | 3 | - 2 ------- 5 ------- 8 - """ - self.nx = nx - self.ny = ny - - def domain(self): - xmin, xmax = 0, 160 - ymin, ymax = 0, 100 - return [xmin, xmax, ymin, ymax] - - def force(self, points: TensorLike) -> TensorLike: - - val = bm.zeros(points.shape, dtype=points.dtype) - val[-1, 1] = -1 - - return val - - def dirichlet(self, points: TensorLike) -> TensorLike: - - return bm.zeros(points.shape, dtype=points.dtype) - - def is_dirichlet_boundary_edge(self, edge_centers: TensorLike) -> TensorLike: - - left_edge = (edge_centers[:, 0] == 0.0) - - return left_edge - - def is_dirichlet_node(self) -> TensorLike: - - dirichlet_nodes = bm.zeros((self.nx+1)*(self.ny+1), dtype=bool) - - dirichlet_nodes[0:self.ny + 1] = True - - return dirichlet_nodes - - def is_dirichlet_direction(self) -> TensorLike: - - direction_flags = bm.zeros(((self.nx + 1) * (self.ny + 1), 2), dtype=bool) - - direction_flags[0:self.ny, :] = True - direction_flags[self.ny, 0] = True - - return direction_flags \ No newline at end of file diff --git a/app/soptx/soptx/solver/elastic_fem_solver.py b/app/soptx/soptx/solver/elastic_fem_solver.py index 34d272117..f2052904b 100644 --- a/app/soptx/soptx/solver/elastic_fem_solver.py +++ b/app/soptx/soptx/solver/elastic_fem_solver.py @@ -2,7 +2,7 @@ from typing import Optional from fealpy.backend import backend_manager as bm -from fealpy.typing import TensorLike +from fealpy.typing import TensorLike, Union from fealpy.functionspace import TensorFunctionSpace from fealpy.fem import LinearElasticIntegrator, BilinearForm, DirichletBC from fealpy.sparse import CSRTensor @@ -33,20 +33,25 @@ class ElasticFEMSolver: def __init__(self, material_properties: ElasticMaterialProperties, tensor_space: TensorFunctionSpace, - pde): + pde, + solver_type: str = 'cg', + solver_params: Optional[dict] = None): """ Parameters ---------- - material_properties : ElasticMaterialProperties - 材料属性计算器 - tensor_space : TensorFunctionSpace - 张量函数空间 - pde : object - 包含荷载和边界条件的PDE模型 + material_properties : 材料属性计算器 + tensor_space : 张量函数空间 + pde : 包含荷载和边界条件的PDE模型 + solver_type : 求解器类型, 'cg' 或 'direct' + solver_params : 求解器参数 + cg: maxiter, atol, rtol + direct: solver_type """ self.material_properties = material_properties self.tensor_space = tensor_space self.pde = pde + self.solver_type = solver_type + self.solver_params = solver_params or {} # 状态管理 self._current_density = None @@ -109,9 +114,9 @@ def get_base_local_stiffness_matrix(self) -> TensorLike: if self._base_local_stiffness_matrix is None: base_material = self.material_properties.get_base_material() integrator = LinearElasticIntegrator( - material=base_material, - q=self.tensor_space.p + 3 - ) + material=base_material, + q=self.tensor_space.p + 3 + ) self._base_local_stiffness_matrix = integrator.assembly(space=self.tensor_space) return self._base_local_stiffness_matrix @@ -121,9 +126,9 @@ def compute_local_stiffness_matrix(self) -> TensorLike: raise ValueError("Material not initialized. Call update_density first.") integrator = LinearElasticIntegrator( - material=self._current_material, - q=self.tensor_space.p + 3 - ) + material=self._current_material, + q=self.tensor_space.p + 3 + ) KE = integrator.assembly(space=self.tensor_space) @@ -138,9 +143,9 @@ def _assemble_global_stiffness_matrix(self) -> CSRTensor: raise ValueError("Material not initialized. Call update_density first.") integrator = LinearElasticIntegrator( - material=self._current_material, - q=self.tensor_space.p + 3 - ) + material=self._current_material, + q=self.tensor_space.p + 3 + ) bform = BilinearForm(self.tensor_space) bform.add_integrator(integrator) K = bform.assembly(format='csr') @@ -188,7 +193,16 @@ def _apply_boundary_conditions(self, K: CSRTensor, F: TensorLike) -> tuple[CSRTe #--------------------------------------------------------------------------- # 求解方法 - #--------------------------------------------------------------------------- + #--------------------------------------------------------------------------- + def solve(self) -> Union[IterativeSolverResult, DirectSolverResult]: + """统一的求解接口""" + if self.solver_type == 'cg': + return self.solve_cg(**self.solver_params) + elif self.solver_type == 'direct': + return self.solve_direct(**self.solver_params) + else: + raise ValueError(f"Unsupported solver type: {self.solver_type}") + def solve_cg(self, maxiter: int = 5000, atol: float = 1e-12, diff --git a/app/soptx/soptx/tests/filter_parameters_data.py b/app/soptx/soptx/tests/filter_parameters_data.py deleted file mode 100644 index a975f1af6..000000000 --- a/app/soptx/soptx/tests/filter_parameters_data.py +++ /dev/null @@ -1,27 +0,0 @@ -import numpy as np - -filter_data_2d = [{ - "nx": 6, - "ny": 2, - "rmin": 1.5, - "Hs": np.array([ - 2.58578644, 2.58578644, 3.17157288, 3.17157288, 3.17157288, 3.17157288, - 3.17157288, 3.17157288, 3.17157288, 3.17157288, 2.58578644, 2.58578644 - ], dtype=np.float64), - "dce": np.array([-990.53214519, -1014.10314435, -709.48123254, -667.32342978, - -424.36568914, -423.6264404, -231.43275072, -234.07756366, - -101.24930026, -107.39453977, -126.64893069, -31.48504138], dtype=np.float64), - "dve": np.array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.], dtype=np.float64), - "rho": np.array([0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5], dtype=np.float64), - "dce_sens_updated": np.array([-930.02178023, -932.38418163, -702.70186682, -692.39134161, - -440.2064151 , -434.81285623, -243.58570059, -245.12564555, - -128.45163799, -118.30606733, -102.69740686, -66.87911715], dtype=np.float64), -}] - -filter_data_3d = [{ - "nx": 4, - "ny": 1, - "nz": 2, - "rmin": 1.5, - "Hs": np.array([2.5858, 3.1716, 3.1716, 2.5858, 2.5858, 3.1716, 3.1716, 2.5858], dtype=np.float64) -}] diff --git a/app/soptx/soptx/tests/test_compliance.py b/app/soptx/soptx/tests/test_compliance.py index aa27fb38e..f0bc36247 100644 --- a/app/soptx/soptx/tests/test_compliance.py +++ b/app/soptx/soptx/tests/test_compliance.py @@ -1,8 +1,11 @@ -""" -测试 compliance.py 中柔度目标函数的功能 -""" +"""Test cases for compliance objective and volume constraint.""" + +from dataclasses import dataclass +from typing import Literal, Optional, Union, Dict, Any +from pathlib import Path + from fealpy.backend import backend_manager as bm -from fealpy.mesh import UniformMesh2d +from fealpy.mesh import UniformMesh2d, UniformMesh3d from fealpy.functionspace import LagrangeFESpace, TensorFunctionSpace from soptx.material import ( @@ -10,199 +13,220 @@ ElasticMaterialProperties, SIMPInterpolation ) - -from soptx.pde import MBBBeam2dData1 +from soptx.pde import MBBBeam2dData1, Cantilever2dData1, Cantilever3dData1 from soptx.solver import ElasticFEMSolver -from soptx.opt import ComplianceObjective +from soptx.opt import ComplianceObjective, VolumeConstraint from soptx.filter import Filter, FilterConfig -def test_compliance_objective(): - print("\n=== 柔度目标函数测试 ===") - - # 1. 创建网格 - nx, ny = 60, 20 - extent = [0, nx, 0, ny] - h = [1.0, 1.0] - origin = [0.0, 0.0] - mesh = UniformMesh2d( - extent=extent, h=h, origin=origin, - ipoints_ordering='yx', flip_direction='y', - device='cpu' - ) +@dataclass +class TestConfig: + """Configuration for topology optimization test cases.""" + # Required parameters (no default values) + problem_type: Literal['mbb_2d', 'cantilever_2d', 'cantilever_3d'] + nx: int + ny: int + volume_fraction: float + filter_radius: float + filter_type: Literal['sensitivity', 'density', 'heaviside'] + + # Optional parameters (with default values) + nz: Optional[int] = None # Only for 3D problems + elastic_modulus: float = 1.0 + poisson_ratio: float = 0.3 + minimal_modulus: float = 1e-9 + penalty_factor: float = 3.0 + projection_beta: Optional[float] = None # Only for Heaviside projection + move_limit: float = 0.2 + tolerance: float = 0.01 + initial_lambda: float = 1e9 + bisection_tol: float = 1e-3 + +def create_base_components(config: TestConfig): + """Create basic components needed for topology optimization based on configuration.""" + # Create mesh and determine dimensionality + if config.problem_type == 'cantilever_3d': + extent = [0, config.nx, 0, config.ny, 0, config.nz] + h = [1.0, 1.0, 1.0] + origin = [0.0, 0.0, 0.0] + mesh = UniformMesh3d( + extent=extent, h=h, origin=origin, + ipoints_ordering='zyx', flip_direction='y', + device='cpu' + ) + dimension = 3 + else: + extent = [0, config.nx, 0, config.ny] + h = [1.0, 1.0] + origin = [0.0, 0.0] + mesh = UniformMesh2d( + extent=extent, h=h, origin=origin, + ipoints_ordering='yx', flip_direction='y', + device='cpu' + ) + dimension = 2 - # 2. 创建函数空间 + # Create function spaces p = 1 space_C = LagrangeFESpace(mesh=mesh, p=p, ctype='C') - tensor_space_C = TensorFunctionSpace(space_C, (-1, 2)) + tensor_space_C = TensorFunctionSpace(space_C, (-1, dimension)) space_D = LagrangeFESpace(mesh=mesh, p=p-1, ctype='D') - # 3. 创建材料属性 - # 材料配置 + # Create material properties material_config = ElasticMaterialConfig( - elastic_modulus=1.0, - minimal_modulus=1e-9, - poisson_ratio=0.3, - plane_assumption="plane_stress" + elastic_modulus=config.elastic_modulus, + minimal_modulus=config.minimal_modulus, + poisson_ratio=config.poisson_ratio, + plane_assumption="3D" if dimension == 3 else "plane_stress" ) - - # 插值模型 - interpolation_model = SIMPInterpolation(penalty_factor=3.0) - - # 密度场(初始为均匀分布) - volfrac = 0.5 - array = volfrac * bm.ones(mesh.number_of_cells(), dtype=bm.float64) - rho_elem = space_D.function(array) - - # 创建材料属性对象 + interpolation_model = SIMPInterpolation(penalty_factor=config.penalty_factor) material_properties = ElasticMaterialProperties( config=material_config, interpolation_model=interpolation_model ) - E = material_properties.material_model(rho=rho_elem[:]) - print(f"\n单元杨氏模量信息:") - print(f"- 形状 - {E.shape}:\n {E}") - print(f"杨氏模量均值: {bm.mean(E)}") - print(f"杨氏模量最大值: {bm.max(E)}") - print(f"杨氏模量最小值: {bm.min(E)}") - # 4. 创建PDE问题 - pde = MBBBeam2dData1(xmin=0, xmax=nx*h[0], ymin=0, ymax=ny*h[1]) + # Create PDE problem based on problem type + if config.problem_type == 'mbb_2d': + pde = MBBBeam2dData1( + xmin=0, xmax=config.nx*h[0], + ymin=0, ymax=config.ny*h[1] + ) + elif config.problem_type == 'cantilever_2d': + pde = Cantilever2dData1( + xmin=0, xmax=config.nx*h[0], + ymin=0, ymax=config.ny*h[1] + ) + else: # cantilever_3d + pde = Cantilever3dData1( + xmin=0, xmax=config.nx*h[0], + ymin=0, ymax=config.ny*h[1], + zmin=0, zmax=config.nz*h[2] + ) - # 5. 创建求解器 + # Create solver + # solver = ElasticFEMSolver( + # material_properties=material_properties, + # tensor_space=tensor_space_C, + # pde=pde + # ) + solver = ElasticFEMSolver( - material_properties=material_properties, - tensor_space=tensor_space_C, - pde=pde - ) + material_properties=material_properties, + tensor_space=tensor_space_C, + pde=pde, + solver_type='cg', # 添加默认求解器类型 + solver_params={'maxiter': 5000, 'atol': 1e-12, 'rtol': 1e-12} # 添加求解器参数 + ) + + # Initialize density field + array = config.volume_fraction * bm.ones(mesh.number_of_cells(), dtype=bm.float64) + rho = space_D.function(array) + + return mesh, space_D, material_properties, solver, rho - # 6. 创建目标函数对象 +def test_compliance_objective(config: TestConfig): + """Test compliance objective computation and sensitivity analysis.""" + print(f"\n=== Testing Compliance Objective with {config.filter_type} filter ===") + + # Create base components + mesh, space_D, material_properties, solver, rho = create_base_components(config) + + # Test solver + solver.update_density(rho[:]) + solver_result = solver.solve_cg() + displacement = solver_result.displacement + print(f"\nSolver information:") + print(f"- Displacement shape: {displacement.shape}:\n {displacement[:]}") + + # 获取求解后的全局矩阵和向量 + K = solver.get_global_stiffness_matrix() + F = solver.get_global_force_vector() + + # Create filter + filter_config = FilterConfig( + filter_type={'sensitivity': 0, 'density': 1, 'heaviside': 2}[config.filter_type], + filter_radius=config.filter_radius + ) + filter_obj = Filter(filter_config) + filter_obj.initialize(mesh) + + # Create objective and constraint objective = ComplianceObjective( material_properties=material_properties, - solver=solver + solver=solver, + filter=filter_obj ) - - print("\n=== 测试目标函数计算 ===") - try: - # 求解位移场 - solver_result = solver.solve_cg() - displacement = solver_result.displacement - - # 计算目标函数值 - obj_value = objective.fun(design_vars=rho_elem[:], - state_vars=displacement) - print(f"目标函数值: {obj_value:.6e}") - - # 获取单元柔顺度 - ce = objective.get_element_compliance() - print(f"\n单元柔顺度信息:") - print(f"- 形状 - {ce.shape}:\n {ce}") - print(f"- 最小值: {bm.min(ce):.6e}") - print(f"- 最大值: {bm.max(ce):.6e}") - print(f"- 平均值: {bm.mean(ce):.6e}") - - # 测试不同密度下的目标函数值 - print("\n=== 测试不同密度下的目标函数值 ===") - # 测试较小密度 - rho_small = 0.1 * bm.ones_like(rho_elem[:]) - obj_small = objective.fun(design_vars=rho_small[:], - state_vars=displacement) - print(f"密度=0.1时的目标函数值: {obj_small:.6e}") - - # 测试较大密度 - rho_large = 0.9 * bm.ones_like(rho_elem[:]) - obj_large = objective.fun(design_vars=rho_large[:], - state_vars=displacement) - print(f"密度=0.9时的目标函数值: {obj_large:.6e}") - - except Exception as e: - print(f"目标函数计算失败: {str(e)}") - - print("\n=== 测试目标函数梯度计算 ===") - try: - - # 计算单元灵敏度(不使用滤波器) - dce = objective.jac(design_vars=rho_elem[:], state_vars=displacement) - print(f"\n原始单元灵敏度信息:") - print(f"- 形状 - {dce.shape}:\n, {dce}") - print(f"- 最小值: {bm.min(dce):.6e}") - print(f"- 最大值: {bm.max(dce):.6e}") - print(f"- 平均值: {bm.mean(dce):.6e}") - - # 测试不同类型的滤波器 - print("\n=== 测试不同类型的滤波器 ===") - - # 灵敏度滤波 - filter_sens = Filter(FilterConfig(filter_type=0, filter_radius=2.4)) - filter_sens.initialize(mesh) - - objective_sens = ComplianceObjective( - material_properties=material_properties, - solver=solver, - filter=filter_sens - ) - - grad_filter_sens = objective_sens.jac(design_vars=rho_elem[:], state_vars=displacement) - print(f"\n灵敏度滤波后的梯度信息:") - print(f"- 形状 - {grad_filter_sens.shape}:\n, {grad_filter_sens}") - print(f"- 最小值: {bm.min(grad_filter_sens):.6e}") - print(f"- 最大值: {bm.max(grad_filter_sens):.6e}") - print(f"- 平均值: {bm.mean(grad_filter_sens):.6e}") - - # 密度滤波 - filter_dens = Filter(FilterConfig(filter_type=1, filter_radius=2.4)) - filter_dens.initialize(mesh) - - objective_dens = ComplianceObjective( - material_properties=material_properties, - solver=solver, - filter=filter_dens - ) - - grad_filter_dens = objective_dens.jac(design_vars=rho_elem[:], state_vars=displacement) - print(f"\n密度滤波后的梯度信息:") - print(f"- 形状 - {grad_filter_dens.shape}:\n, {grad_filter_dens}") - print(f"- 最小值: {bm.min(grad_filter_dens):.6e}") - print(f"- 最大值: {bm.max(grad_filter_dens):.6e}") - print(f"- 平均值: {bm.mean(grad_filter_dens):.6e}") - - # Heaviside投影滤波 - filter_heav = Filter(FilterConfig(filter_type=2, filter_radius=2.0)) - filter_heav.initialize(mesh) - - # objective_heav = ComplianceObjective( - # material_properties=material_properties, - # solver=solver, - # filter=filter_heav - # ) - - # # 创建Heaviside滤波参数 - # beta = 1.0 - # rho_tilde = rho_elem[:] # 这里简单使用原始密度作为示例 - - # gradient_heav = objective_heav.jac( - # design_vars=rho_elem[:], - # filter_params={'beta': beta, 'rho_tilde': rho_tilde} - # ) - # print(f"\nHeaviside投影滤波后的梯度信息:") - # print(f"- 形状: {gradient_heav.shape}") - # print(f"- 最小值: {bm.min(gradient_heav):.6e}") - # print(f"- 最大值: {bm.max(gradient_heav):.6e}") - # print(f"- 平均值: {bm.mean(gradient_heav):.6e}") - - # # 测试滤波矩阵属性 - # print("\n=== 测试滤波矩阵属性 ===") - # H = filter_sens.H - # Hs = filter_sens.Hs - # print(f"滤波矩阵 H 的形状: {H.shape}") - # print(f"滤波矩阵行和向量 Hs 的形状: {Hs.shape}") - # print(f"Hs 的最小值: {bm.min(Hs):.6e}") - # print(f"Hs 的最大值: {bm.max(Hs):.6e}") - - except Exception as e: - print(f"梯度计算失败: {str(e)}") - raise e - + constraint = VolumeConstraint( + mesh=mesh, + volume_fraction=config.volume_fraction, + filter=filter_obj + ) + + # Test objective function + obj_value = objective.fun(rho=rho[:], u=displacement) + print(f"Objective function value: {obj_value:.6e}") + + # Test element compliance + ce = objective.get_element_compliance() + print(f"\nElement compliance information:") + print(f"- Shape: {ce.shape}:\n {ce}") + print(f"- Min: {bm.min(ce):.6e}") + print(f"- Max: {bm.max(ce):.6e}") + print(f"- Mean: {bm.mean(ce):.6e}") + # Test sensitivity + dce = objective.jac(rho=rho[:], u=displacement) + print(f"\nElement sensitivity information:") + print(f"- Shape: {dce.shape}:\n, {dce}") + print(f"- Min: {bm.min(dce):.6e}") + print(f"- Max: {bm.max(dce):.6e}") + print(f"- Mean: {bm.mean(dce):.6e}") + + # Test constraint + constraint_value = constraint.fun(rho=rho[:]) + print(f"\nConstraint function value: {constraint_value:.6e}") + + gradient = constraint.jac(rho=rho[:]) + print(f"\nConstraint gradient information:") + print(f"- Shape: {gradient.shape}") + print(f"- Min: {bm.min(gradient):.6e}") + print(f"- Max: {bm.max(gradient):.6e}") + print(f"- Mean: {bm.mean(gradient):.6e}") + + return { + 'objective_value': obj_value, + 'element_compliance': ce, + 'sensitivity': dce, + 'constraint_value': constraint_value, + 'constraint_gradient': gradient + } if __name__ == "__main__": - test_compliance_objective() \ No newline at end of file + # # Test 3D case with density filter + # config_3d = TestConfig( + # problem_type='cantilever_3d', + # nx=60, ny=20, nz=4, + # volume_fraction=0.3, + # filter_radius=1.5, + # filter_type='density' + # ) + # results_3d = test_compliance_objective(config_3d) + + # # Test 2D case with sensitivity filter + # config_2d = TestConfig( + # problem_type='mbb_2d', + # nx=60, ny=20, + # volume_fraction=0.5, + # filter_radius=2.4, + # filter_type='sensitivity' + # ) + # results_2d = test_compliance_objective(config_2d) + + # Test 2D case with sensitivity filter + config_cantilever_2d = TestConfig( + problem_type='cantilever_2d', + nx=4, ny=3, + volume_fraction=0.4, + filter_radius=6, + filter_type='sensitivity' + ) + results_cantilever_2d = test_compliance_objective(config_cantilever_2d) \ No newline at end of file diff --git a/app/soptx/soptx/tests/test_filter_parameters.py b/app/soptx/soptx/tests/test_filter_parameters.py deleted file mode 100644 index 27a76b3fc..000000000 --- a/app/soptx/soptx/tests/test_filter_parameters.py +++ /dev/null @@ -1,65 +0,0 @@ -import pytest -import numpy as np -from fealpy.backend import backend_manager as bm - -from fealpy.sparse import COOTensor -from ..utils.filter_parameters import compute_filter, compute_filter_3d, apply_filter -from .filter_parameters_data import * - -from math import ceil, sqrt - -class TestFilter: - @pytest.mark.parametrize("filterdata", filter_data_2d) - @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) - def test_computer_filter(self, filterdata, backend): - bm.set_backend(backend) - nx = filterdata["nx"] - ny = filterdata["ny"] - rmin = filterdata["rmin"] - - H, Hs = compute_filter(nx=nx, ny=ny, rmin=rmin) - - Hs_true = filterdata["Hs"] - - np.testing.assert_almost_equal(bm.to_numpy(Hs), Hs_true, decimal=4) - - @pytest.mark.parametrize("filterdata", filter_data_2d) - @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) - def test_apply_filter(self, filterdata, backend): - bm.set_backend(backend) - - dce = bm.copy(bm.from_numpy(filterdata["dce"])) - dve = bm.copy(bm.from_numpy(filterdata["dve"])) - rho = bm.from_numpy(filterdata["rho"]) - nx = filterdata["nx"] - ny = filterdata["ny"] - rmin = filterdata["rmin"] - - H, Hs = compute_filter(nx=nx, ny=ny, rmin=rmin) - dce_sens_updated, _ = apply_filter( - ft=0, rho=rho, - dce=dce, dve=dve, - H=H, Hs=Hs) - dce_sens_updated_true = filterdata['dce_sens_updated'] - - np.testing.assert_almost_equal(bm.to_numpy(dce_sens_updated), - dce_sens_updated_true, decimal=4) - - @pytest.mark.parametrize("filterdata", filter_data_3d) - @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) - def test_computer_filter(self, filterdata, backend): - bm.set_backend(backend) - nx = filterdata["nx"] - ny = filterdata["ny"] - nz = filterdata["nz"] - rmin = filterdata["rmin"] - - H, Hs = compute_filter_3d(nx=nx, ny=ny, nz=nz, rmin=rmin) - - Hs_true = filterdata["Hs"] - - np.testing.assert_almost_equal(bm.to_numpy(Hs), Hs_true, decimal=4) - - - - diff --git a/app/soptx/soptx/tests/test_oc.py b/app/soptx/soptx/tests/test_oc.py index dda35dfc8..132e015ea 100644 --- a/app/soptx/soptx/tests/test_oc.py +++ b/app/soptx/soptx/tests/test_oc.py @@ -1,7 +1,11 @@ -"""测试 OC 优化算法的功能""" +"""Test cases for the OC optimization algorithm with configurable parameters and filtering schemes.""" + +from dataclasses import dataclass +from typing import Literal, Optional, Union, Dict, Any +from pathlib import Path from fealpy.backend import backend_manager as bm -from fealpy.mesh import UniformMesh2d +from fealpy.mesh import UniformMesh2d, UniformMesh3d from fealpy.functionspace import LagrangeFESpace, TensorFunctionSpace from soptx.material import ( @@ -9,155 +13,198 @@ ElasticMaterialProperties, SIMPInterpolation ) -from soptx.pde import MBBBeam2dData1 +from soptx.pde import MBBBeam2dData1, Cantilever2dData1, Cantilever3dData1 from soptx.solver import ElasticFEMSolver from soptx.opt import ComplianceObjective, VolumeConstraint from soptx.filter import Filter, FilterConfig -from soptx.opt import OCOptimizer - -def test_oc_optimizer(): - """测试 OC 优化器的主要功能""" +from soptx.opt import OCOptimizer, save_optimization_history + +@dataclass +class TestConfig: + """Configuration for topology optimization test cases.""" + # Required parameters (no default values) + problem_type: Literal['mbb_2d', 'cantilever_2d', 'cantilever_3d'] + nx: int + ny: int + volume_fraction: float + filter_radius: float + filter_type: Literal['sensitivity', 'density', 'heaviside'] + max_iterations: int + save_dir: Union[str, Path] - print("\n=== OC 优化器测试 ===") + # Optional parameters (with default values) + nz: Optional[int] = None # Only for 3D problems + elastic_modulus: float = 1.0 + poisson_ratio: float = 0.3 + minimal_modulus: float = 1e-9 + penalty_factor: float = 3.0 + projection_beta: Optional[float] = None # Only for Heaviside projection + move_limit: float = 0.2 + tolerance: float = 0.01 + initial_lambda: float = 1e9 + bisection_tol: float = 1e-3 - #--------------------------------------------------------------------------- - # 1. 创建计算网格和函数空间 - #--------------------------------------------------------------------------- - # 创建网格 - nx, ny = 60, 20 - extent = [0, nx, 0, ny] - h = [1.0, 1.0] - origin = [0.0, 0.0] - mesh = UniformMesh2d( - extent=extent, h=h, origin=origin, - ipoints_ordering='yx', flip_direction='y', - device='cpu' - ) - node = mesh.entity('node') - cell = mesh.entity('cell') + +def create_base_components(config: TestConfig): + """Create basic components needed for topology optimization based on configuration.""" + # Create mesh and determine dimensionality + if config.problem_type == 'cantilever_3d': + if config.nz is None: + raise ValueError("nz must be specified for 3D problems") + extent = [0, config.nx, 0, config.ny, 0, config.nz] + h = [1.0, 1.0, 1.0] + origin = [0.0, 0.0, 0.0] + mesh = UniformMesh3d( + extent=extent, h=h, origin=origin, + ipoints_ordering='zyx', flip_direction='y', + device='cpu' + ) + dimension = 3 + else: + extent = [0, config.nx, 0, config.ny] + h = [1.0, 1.0] + origin = [0.0, 0.0] + mesh = UniformMesh2d( + extent=extent, h=h, origin=origin, + ipoints_ordering='yx', flip_direction='y', + device='cpu' + ) + dimension = 2 - # 创建函数空间 + # Create function spaces p = 1 space_C = LagrangeFESpace(mesh=mesh, p=p, ctype='C') - tensor_space_C = TensorFunctionSpace(space_C, (-1, 2)) + tensor_space_C = TensorFunctionSpace(space_C, (-1, dimension)) space_D = LagrangeFESpace(mesh=mesh, p=p-1, ctype='D') - #--------------------------------------------------------------------------- - # 2. 创建材料属性和PDE问题 - #--------------------------------------------------------------------------- - # 创建材料配置和插值模型 + # Create material properties material_config = ElasticMaterialConfig( - elastic_modulus=1.0, - minimal_modulus=1e-9, - poisson_ratio=0.3, - plane_assumption="plane_stress" - ) - interpolation_model = SIMPInterpolation(penalty_factor=3.0) - - # 创建材料属性计算器 + elastic_modulus=config.elastic_modulus, + minimal_modulus=config.minimal_modulus, + poisson_ratio=config.poisson_ratio, + plane_assumption="3D" if dimension == 3 else "plane_stress" + ) + interpolation_model = SIMPInterpolation(penalty_factor=config.penalty_factor) material_properties = ElasticMaterialProperties( - config=material_config, - interpolation_model=interpolation_model - ) - - # 创建 PDE 问题 - pde = MBBBeam2dData1(xmin=0, xmax=nx*h[0], ymin=0, ymax=ny*h[1]) + config=material_config, + interpolation_model=interpolation_model + ) + + # Create PDE problem based on problem type + if config.problem_type == 'mbb_2d': + pde = MBBBeam2dData1( + xmin=0, xmax=config.nx*h[0], + ymin=0, ymax=config.ny*h[1] + ) + elif config.problem_type == 'cantilever_2d': + pde = Cantilever2dData1( + xmin=0, xmax=config.nx*h[0], + ymin=0, ymax=config.ny*h[1] + ) + else: # cantilever_3d + pde = Cantilever3dData1( + xmin=0, xmax=config.nx*h[0], + ymin=0, ymax=config.ny*h[1], + zmin=0, zmax=config.nz*h[2] + ) - #--------------------------------------------------------------------------- - # 3. 创建求解器 - #--------------------------------------------------------------------------- + # Create solver + # solver = ElasticFEMSolver( + # material_properties=material_properties, + # tensor_space=tensor_space_C, + # pde=pde + # ) solver = ElasticFEMSolver( - material_properties=material_properties, - tensor_space=tensor_space_C, - pde=pde - ) - - # 初始密度场 - volfrac = 0.5 - array = volfrac * bm.ones(mesh.number_of_cells(), dtype=bm.float64) + material_properties=material_properties, + tensor_space=tensor_space_C, + pde=pde, + solver_type='cg', # 添加默认求解器类型 + solver_params={'maxiter': 5000, 'atol': 1e-12, 'rtol': 1e-12} # 添加求解器参数 + ) + + # Initialize density field + array = config.volume_fraction * bm.ones(mesh.number_of_cells(), dtype=bm.float64) rho = space_D.function(array) + + return mesh, space_D, material_properties, solver, rho - # 更新求解器中的密度 - solver.update_density(rho[:]) - - # 检查基础刚度矩阵 - ke0 = solver.get_base_local_stiffness_matrix() - print(f"\n基础材料局部刚度矩阵 - {ke0.shape}:") - print(f"{ke0[0]}") - - #--------------------------------------------------------------------------- - # 4. 创建滤波器和优化组件 - #--------------------------------------------------------------------------- - # 创建灵敏度滤波器 - filter_sens = Filter(FilterConfig(filter_type=1, filter_radius=2.4)) - filter_sens.initialize(mesh) - - # 创建目标函数 +def run_optimization_test(config: TestConfig) -> Dict[str, Any]: + """Run topology optimization test with given configuration.""" + # Create base components + mesh, space_D, material_properties, solver, rho = create_base_components(config) + + # Create filter based on configuration + filter_config = FilterConfig( + filter_type={'sensitivity': 0, 'density': 1, 'heaviside': 2}[config.filter_type], + filter_radius=config.filter_radius + ) + filter_obj = Filter(filter_config) + filter_obj.initialize(mesh) + + # Create optimization components objective = ComplianceObjective( - material_properties=material_properties, - solver=solver, - filter=filter_sens - ) - - # 创建体积约束 + material_properties=material_properties, + solver=solver, + filter=filter_obj + ) constraint = VolumeConstraint( - mesh=mesh, - volume_fraction=volfrac, - filter=filter_sens - ) - - # 创建优化器 - oc_optimizer = OCOptimizer( + mesh=mesh, + volume_fraction=config.volume_fraction, + filter=filter_obj + ) + + # Create optimizer + optimizer = OCOptimizer( objective=objective, constraint=constraint, - filter=filter_sens, + filter=filter_obj, options={ - 'max_iterations': 200, - 'move_limit': 0.2, - 'tolerance': 0.01, - 'initial_lambda': 1e9, - 'bisection_tol': 1e-3 + 'max_iterations': config.max_iterations, + 'move_limit': config.move_limit, + 'tolerance': config.tolerance, + 'initial_lambda': config.initial_lambda, + 'bisection_tol': config.bisection_tol } ) - - #--------------------------------------------------------------------------- - # 5. 测试优化过程 - #--------------------------------------------------------------------------- - print("\n=== 开始优化测试 ===") - try: - # # 计算初始状态 - # u = solver.solve_cg().displacement - - # # 测试目标函数及其导数 - # dc = objective.jac(rho=rho[:], u=u) - # print(f"\n单元灵敏度值 - {dc.shape}:\n {dc}") - - # obj_val = objective.fun(rho=rho[:], u=u) - # print(f"\n目标函数值: {obj_val:.6e}") - - # # 测试约束函数及其导数 - # con_val = constraint.fun(rho=rho[:]) - # print(f"约束函数值: {con_val:.6e}") - - # con_grad = constraint.jac(rho=rho[:]) - # print(f"约束函数梯度 - {con_grad.shape}:\n {con_grad}") - - # 运行优化 - print("\n开始优化迭代...") - rho_opt = oc_optimizer.optimize(rho=rho[:]) - mesh.celldata['density'] = rho_opt[:] - mesh.to_vtk(filename='/home/heliang/FEALPy_Development/fealpy/app/soptx/soptx/tests/density.vts') - - - # 输出优化结果统计 - print("\n优化结果统计:") - print(f"- 最终密度均值: {bm.mean(rho_opt):.3f}") - print(f"- 最终密度最大值: {bm.max(rho_opt):.3f}") - print(f"- 最终密度最小值: {bm.min(rho_opt):.3f}") - - except Exception as e: - print(f"\n优化过程失败: {str(e)}") - raise e + + # Prepare optimization parameters + opt_params = {} + if config.filter_type == 'heaviside' and config.projection_beta is not None: + opt_params['beta'] = config.projection_beta + + # Run optimization + rho_opt, history = optimizer.optimize(rho=rho[:], **opt_params) + + # Save results + save_path = Path(config.save_dir) + save_path.mkdir(parents=True, exist_ok=True) + save_optimization_history(mesh, history, str(save_path)) + + return { + 'optimal_density': rho_opt, + 'history': history, + 'mesh': mesh + } if __name__ == "__main__": - test_oc_optimizer() \ No newline at end of file + # Run single test + config1 = TestConfig( + problem_type='cantilever_2d', + nx=160, ny=100, + volume_fraction=0.4, + filter_radius=6.0, + filter_type='sensitivity', + max_iterations=30, + save_dir='/home/heliang/FEALPy_Development/fealpy/app/soptx/soptx/tests/cantilever_2d' + ) + config2 = TestConfig( + problem_type='mbb_2d', + nx=60, ny=20, + volume_fraction=0.5, + filter_radius=2.4, + filter_type='sensitivity', + max_iterations=30, + save_dir='/home/heliang/FEALPy_Development/fealpy/app/soptx/soptx/tests/mbb_2d' + ) + result = run_optimization_test(config1) + \ No newline at end of file diff --git a/fealpy/mesh/uniform_mesh_2d.py b/fealpy/mesh/uniform_mesh_2d.py index 0b16073f5..b20f01fe0 100644 --- a/fealpy/mesh/uniform_mesh_2d.py +++ b/fealpy/mesh/uniform_mesh_2d.py @@ -878,7 +878,7 @@ def to_vtk(self, filename, celldata=None, nodedata=None): yx_x = xy_x[:, ::-1].flatten() yx_y = xy_y[:, ::-1].flatten() yx_z = xy_z[:, ::-1].flatten() - else: + elif self.flip_direction == None: # 默认:左下到右上 yx_x = xy_x.flatten() yx_y = xy_y.flatten() diff --git a/fealpy/mesh/uniform_mesh_3d.py b/fealpy/mesh/uniform_mesh_3d.py index 6e3ae5062..dec65658f 100644 --- a/fealpy/mesh/uniform_mesh_3d.py +++ b/fealpy/mesh/uniform_mesh_3d.py @@ -77,7 +77,7 @@ def __init__(self, extent: Tuple[int, int, int, int, int, int] = (0, 1, 0, 1, 0, origin: Tuple[float, float, float] = (0.0, 0.0, 0.0), ipoints_ordering='zyx', flip_direction=None, - itype=None, ftype=None): + *, itype=None, ftype=None, device=None): """ Initializes a 3D uniform structured mesh. @@ -103,6 +103,8 @@ def __init__(self, extent: Tuple[int, int, int, int, int, int] = (0, 1, 0, 1, 0, ftype = bm.float64 super().__init__(TD=3, itype=itype, ftype=ftype) + self.device = device + # Mesh properties self.extent = [int(e) for e in extent] self.h = [float(val) for val in h] @@ -1253,7 +1255,8 @@ def jacobi_matrix(self, bcs: TensorLike, index :Index=_S) -> TensorLike: node = self.entity('node') cell = self.entity('cell', index=index) gphi = self.grad_shape_function(bcs, p=1, variables='u') - J = bm.einsum( 'cim, qin -> qcmn', node[cell[:]], gphi) + # J = bm.einsum( 'cim, qin -> qcmn', node[cell[:]], gphi) + J = bm.einsum( 'cim, qin -> cqmn', node[cell[:]], gphi) return J @@ -1312,5 +1315,96 @@ def uniform_refine(self, n: int=1): self.clear() + def to_vtk(self, filename, celldata=None, nodedata=None): + """ + @brief: Converts the 3D mesh data to a VTK structured grid format and writes to a VTS file + """ + import vtk + from vtk.util.numpy_support import numpy_to_vtk + + if celldata is None: + celldata = self.celldata + + if nodedata is None: + nodedata = self.nodedata + + # 网格参数 + nx, ny, nz = self.nx, self.ny, self.nz + h = self.h + origin = self.origin + + # 创建三维坐标点 + x = bm.linspace(origin[0], origin[0] + nx * h[0], nx + 1) + y = bm.linspace(origin[1], origin[1] + ny * h[1], ny + 1) + z = bm.linspace(origin[2], origin[2] + nz * h[2], nz + 1) + + # 创建三维网格点坐标 + xyz_x, xyz_y, xyz_z = bm.meshgrid(x, y, z, indexing='ij') + + if self.flip_direction == 'y': + # 左上到右下:在 y 方向翻转 + xyz_x = xyz_x[:, ::-1, :] + xyz_y = xyz_y[:, ::-1, :] + xyz_z = xyz_z[:, ::-1, :] + + # 将坐标展平为一维数组 + points_x = xyz_x.flatten() + points_y = xyz_y.flatten() + points_z = xyz_z.flatten() + elif self.flip_direction == None: + # 默认:左下到右上 + points_x = xyz_x.flatten() + points_y = xyz_y.flatten() + points_z = xyz_z.flatten() + + # 创建 VTK 网格对象 + rectGrid = vtk.vtkStructuredGrid() + # 注意:VTK 中维度顺序为 (nz+1, ny+1, nx+1) + rectGrid.SetDimensions(nz + 1, ny + 1, nx + 1) + + # 创建点 + points = vtk.vtkPoints() + for i in range(len(points_x)): + points.InsertNextPoint(points_x[i], points_y[i], points_z[i]) + rectGrid.SetPoints(points) + + # 添加节点数据 + if nodedata is not None: + for name, data in nodedata.items(): + # 如果数据是元组,取第一个元素(当前密度值) + if isinstance(data, tuple): + current_data = data[0] # 获取当前密度值 + else: + current_data = data + + # 将数据展平 + data_array = numpy_to_vtk(current_data.flatten(), deep=True) + data_array.SetName(name) + rectGrid.GetPointData().AddArray(data_array) + + # 添加单元格数据 + if celldata is not None: + for name, data in celldata.items(): + # 如果数据是元组,取第一个元素(当前密度值) + if isinstance(data, tuple): + current_data = data[0] # 获取当前密度值 + else: + current_data = data + + # 将数据展平 + data_array = numpy_to_vtk(current_data.flatten(), deep=True) + data_array.SetName(name) + rectGrid.GetCellData().AddArray(data_array) + + # 写入 VTK 文件 + print("Writing to vtk...") + writer = vtk.vtkXMLStructuredGridWriter() + writer.SetInputData(rectGrid) + writer.SetFileName(filename) + writer.Write() + + return filename + + UniformMesh3d.set_ploter('3d') From ce0e51f564a37367ac90e8c3cfce4785db966587 Mon Sep 17 00:00:00 2001 From: july <1985547582@qq.com> Date: Thu, 21 Nov 2024 21:55:07 +0800 Subject: [PATCH 18/63] BDN --- example/fem/poisson_BDM_neumann.py | 130 +++++ ...BrezziDouglasMariniFiniteElementSpace2d.py | 479 ++++++++++++++++++ fealpy/functionspace/__init__.py | 2 + ...iDouglasMariniFiniteElementSpace2d_data.py | 219 ++++++++ ...BrezziDouglasMariniFiniteElementSpace2d.py | 76 +++ 5 files changed, 906 insertions(+) create mode 100644 example/fem/poisson_BDM_neumann.py create mode 100644 fealpy/functionspace/BrezziDouglasMariniFiniteElementSpace2d.py create mode 100644 test/functionspace/BrezziDouglasMariniFiniteElementSpace2d_data.py create mode 100644 test/functionspace/test_BrezziDouglasMariniFiniteElementSpace2d.py diff --git a/example/fem/poisson_BDM_neumann.py b/example/fem/poisson_BDM_neumann.py new file mode 100644 index 000000000..efccd2347 --- /dev/null +++ b/example/fem/poisson_BDM_neumann.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 + +import argparse +import matplotlib.pyplot as plt + +from fealpy.mesh import TriangleMesh, TetrahedronMesh +from fealpy.functionspace import BDMFiniteElementSpace2d +from fealpy.functionspace import LagrangeFESpace +from fealpy import logger +logger.setLevel('WARNING') + +from fealpy.backend import backend_manager as bm +from fealpy.fem import BilinearForm,ScalarMassIntegrator +from fealpy.fem import LinearForm, ScalarSourceIntegrator,BoundaryFaceSourceIntegrator +from fealpy.fem import DivIntegrator +from fealpy.fem import BlockForm,LinearBlockForm + +# from fealpy.decorator import cartesian, barycentric +from fealpy.tools.show import showmultirate, show_error_table + +# solver +from fealpy.solver import spsolve + +from fealpy.pde.poisson_2d import CosCosData as PDE2d +from fealpy.pde.poisson_3d import CosCosCosData as PDE3d +from fealpy.utils import timer + +# 参数解析 +parser = argparse.ArgumentParser(description= + """ + 任意次有限元方法求解possion方程 + """) + +parser.add_argument('--degree', + default=1, type=int, + help='Lagrange 有限元空间的次数, 默认为 1 次.') + +parser.add_argument('--backend', + default='numpy', type=str, + help='默认后端为numpy') + +parser.add_argument('--dim', + default=2, type=int, + help='默认维数为2') + +parser.add_argument('--maxit', + default=4, type=int, + help='默认最大迭代次数为4') + +args = parser.parse_args() +p = args.degree +maxit = args.maxit +dim = args.dim +backend = args.backend +bm.set_backend(backend) + +if dim == 2: + pde = PDE2d() +else: + pde = PDE3d() + + +errorType = ['$|| p - p_h||_0$ with k=2', + '$|| p - p_h||_0$ with k=3', + '$|| p - p_h||_0$ with k=4', + '$|| u - u_h||_0$ with k=2', + '$|| u - u_h||_0$ with k=3', + '$|| u - u_h||_0$ with k=4'] +errorMatrix = bm.zeros((len(errorType), maxit), dtype=bm.float64) +NDof = bm.zeros(maxit, dtype=bm.float64) + +tmr = timer() +next(tmr) + +ps = [2, 3, 4] +for j, p in enumerate(ps): + for i in range(maxit): + print("The {}-th computation:".format(i)) + + if dim == 2: + mesh = TriangleMesh.from_box([0, 1, 0, 1], nx=2**i, ny=2**i) + space1 = LagrangeFESpace(mesh,p=p-1,ctype="D") + space2 = BDMFiniteElementSpace2d(mesh, p=p) + else: + mesh = TetrahedronMesh.from_box([0, 1, 0, 1, 0, 1], nx=2**i, ny=2**i, nz=2**i) + space1 = LagrangeFESpace(mesh,p=p,ctype="D") + #space2 = RTFiniteElementSpace3d(mesh, p=p) + tmr.send(f'第{i}次网格和pde生成时间') + + pdof = space1.dof.number_of_global_dofs() + udof = space2.dof.number_of_global_dofs() + NDof[i] = 1/2**i + + uh = space2.function() + ph = space1.function() + + bform1 = BilinearForm(space2) + bform1.add_integrator(ScalarMassIntegrator(coef=1, q=p+3)) + + bform2 = BilinearForm((space1,space2)) + bform2.add_integrator(DivIntegrator(coef=-1, q=p+3)) + + M = BlockForm([[bform1,bform2], + [bform2.T,None]]) + M = M.assembly() + tmr.send(f'第{i}次矩组装时间') + # 组装右端 + + lform = LinearForm(space1) + lform.add_integrator(ScalarSourceIntegrator(pde.source)) + F = lform.assembly() + G = space2.set_neumann_bc(pde.solution) + b = bm.concatenate([-G,-F],axis=0) + tmr.send(f'第{i}次向量组装时间') + + val = spsolve(M, b,"scipy") + + uh[:] = val[:udof] + ph[:] = val[udof:] + tmr.send(f'第{i}次求解时间') + + #计算误差 + errorMatrix[j, i] = mesh.error(pde.solution, ph) + errorMatrix[j+3, i] = mesh.error(pde.flux, uh.value) + print("error = ", errorMatrix) + +next(tmr) +showmultirate(plt, 2, NDof, errorMatrix, errorType, propsize=20) +show_error_table(NDof, errorType, errorMatrix) +plt.show() \ No newline at end of file diff --git a/fealpy/functionspace/BrezziDouglasMariniFiniteElementSpace2d.py b/fealpy/functionspace/BrezziDouglasMariniFiniteElementSpace2d.py new file mode 100644 index 000000000..fe5415c1c --- /dev/null +++ b/fealpy/functionspace/BrezziDouglasMariniFiniteElementSpace2d.py @@ -0,0 +1,479 @@ + +from typing import Union, TypeVar, Generic, Callable,Optional + +from ..backend import TensorLike +from ..backend import backend_manager as bm +from .space import FunctionSpace +from .lagrange_fe_space import LagrangeFESpace +from .function import Function + +from scipy.sparse import csr_matrix +from ..mesh.mesh_base import Mesh +from ..decorator import barycentric, cartesian + +_MT = TypeVar('_MT', bound=Mesh) +Index = Union[int, slice, TensorLike] +Number = Union[int, float] +_S = slice(None) + + +class BDMDof(): + def __init__(self, mesh, p): + self.p = p + self.mesh = mesh + self.multiindex = mesh.multi_index_matrix(p, 2) + self.ftype = mesh.ftype + self.itype = mesh.itype + + def is_normal_dof(self): + p = self.p + ldof = self.number_of_local_dofs() + isndof = bm.zeros(ldof, dtype=bm.bool) + + multiindex = self.multiindex + dim = bm.sum(multiindex>0, axis=-1)-1 + dim0, = bm.where(dim==0) + dim1, = bm.where(dim==1) + + isndof[dim0] = True + isndof[dim1] = True + isndof[dim0+ldof//2] = True + return isndof + + def edge_to_local_dof(self): + multiindex = self.multiindex + ldof = self.number_of_local_dofs() + + eldof = self.number_of_local_dofs('edge') + e2ld = bm.zeros((3, eldof), dtype=self.itype) + + e2ld[0], = bm.where(multiindex[:, 0]==0) + e2ld[0][0] += ldof//2 + e2ld[0][-1] += ldof//2 + + #e2ld[1] = bm.where(multiindex[:, 1]==0)[0][::-1] + array = bm.where(multiindex[:, 1]==0)[0] + e2ld[1] = bm.flip(array) + e2ld[1][-1] += ldof//2 + + e2ld[2], = bm.where(multiindex[:, 2]==0) + return e2ld + + def number_of_local_dofs(self, doftype='all'): + p = self.p + if doftype == 'all': # number of all dofs on a cell + return (p+1)*(p+2) + elif doftype in {'cell', 2}: # number of dofs inside the cell + return (p-1)*(p-2) + 3*(p-1) + elif doftype in {'face', 'edge', 1}: # number of dofs on each edge + return p+1 + elif doftype in {'node', 0}: # number of dofs on each node + return 0 + + def number_of_global_dofs(self): + NC = self.mesh.number_of_cells() + NE = self.mesh.number_of_edges() + edof = self.number_of_local_dofs(doftype='edge') + cdof = self.number_of_local_dofs(doftype='cell') + return NE*edof + NC*cdof + + def edge_to_dof(self, index=_S): + NE = self.mesh.number_of_edges() + edof = self.number_of_local_dofs(doftype='edge') + return bm.arange(NE*edof).reshape(NE, edof)[index] + + def cell_to_dof(self): + p = self.p + cdof = self.number_of_local_dofs('cell') + edof = self.number_of_local_dofs('edge') + ldof = self.number_of_local_dofs() + gdof = self.number_of_global_dofs() + e2dof = self.edge_to_dof() + e2ldof = self.edge_to_local_dof() + isndof = self.is_normal_dof() + + NC = self.mesh.number_of_cells() + NE = self.mesh.number_of_edges() + c2e = self.mesh.cell_to_edge() + c2esign = self.mesh.cell_to_face_sign() + + c2d = bm.zeros((NC, ldof), dtype=bm.int64) + # c2d[:, e2ldof] : (NC, 3, p+1), e2dof[c2e] : (NC, 3, p+1) + tmp = e2dof[c2e] + # tmp[~c2esign] = tmp[~c2esign, ::-1] + # c2d[:, e2ldof] = tmp + # c2d[:, ~isndof] = bm.arange(NE*edof, gdof).reshape(NC, -1) + tmp[~c2esign] = bm.flip(tmp[~c2esign], [1]) + c2d = bm.set_at(c2d,(slice(None), e2ldof),tmp) + c2d = bm.set_at(c2d,(slice(None), ~isndof),bm.arange(NE*edof, gdof).reshape(NC, -1)) + return c2d + + @property + def cell2dof(self): + return self.cell_to_dof() + + def boundary_dof(self): + eidx = self.mesh.boundary_face_index() + e2d = self.edge_to_dof(index=eidx) + return e2d.reshape(-1) + + def is_boundary_dof(self): + bddof = self.boundary_dof() + + gdof = self.number_of_global_dofs() + flag = bm.zeros(gdof, dtype=bm.bool) + + flag[bddof] = True + return flag + +class BDMFiniteElementSpace2d(FunctionSpace, Generic[_MT]): + def __init__(self, mesh, p, space=None): + self.p = p + self.mesh = mesh + self.dof = BDMDof(mesh, p) + + self.lspace = LagrangeFESpace(mesh, p) + self.cellmeasure = mesh.entity_measure('cell') + self.qf = mesh.quadrature_formula(p+3) + self.ftype = mesh.ftype + self.itype = mesh.itype + + #TODO:JAX + self.device = mesh.device + self.TD = mesh.top_dimension() + self.GD = mesh.geo_dimension() + + + @barycentric + def basis(self, bcs: TensorLike,index=_S)-> TensorLike: + mesh = self.mesh + NC = mesh.number_of_cells() + GD = mesh.geo_dimension() + ldof = self.dof.number_of_local_dofs() + gdof = self.dof.number_of_global_dofs() + + node = mesh.entity("node") + cell = mesh.entity("cell") + + c2v = self.basis_vector()#(NC, ldof, GD) + + shape = bcs.shape[:-1] + val = bm.zeros((NC,)+shape+(ldof, GD), device=self.device, dtype=self.ftype) + + bval = self.lspace.basis(bcs) #(NC, NQ, ldof) + c2v = c2v[:,None,:,:] + c2v = bm.broadcast_to(c2v, val.shape) + + # val[..., :ldof//2, :] = bval[..., None]*c2v[..., :ldof//2, :] + # val[..., ldof//2:, :] = bval[..., None]*c2v[..., ldof//2:, :] + val = bm.set_at(val,(..., slice(None, ldof//2), slice(None)),bval[..., None]*c2v[..., :ldof//2, :]) + val = bm.set_at(val,(..., slice(ldof//2, None), slice(None)),bval[..., None]*c2v[..., ldof//2:, :]) + return val[index] + + + def basis_vector(self)->TensorLike: + mesh = self.mesh + NC = mesh.number_of_cells() + GD = mesh.geo_dimension() + ldof = self.dof.number_of_local_dofs() + e2ldof = self.dof.edge_to_local_dof() + + c2e = mesh.cell_to_edge() + e2n = mesh.edge_unit_normal() + e2t = mesh.edge_unit_tangent() + + c2v = bm.zeros((NC, ldof, GD), device=self.device, dtype=self.ftype) + # c2v[:, :ldof//2, 0] = 1 + # c2v[:, ldof//2:, 1] = 1 + c2v = bm.set_at(c2v,(slice(None), slice(None, ldof//2), 0),1) + c2v = bm.set_at(c2v,(slice(None), slice(ldof//2, None), 1),1) + + #c2v[:, e2ldof[:, 1:-1]] : (NC, 3, p-1, GD), e2t[c2e] : (NC, 3, GD) + # c2v[:, e2ldof[:, 1:-1]] = e2n[c2e][:, :, None, :] + # c2v[:, e2ldof[:, 1:-1]+ldof//2] = e2t[c2e][:, :, None, :] + + c2v = bm.set_at(c2v,(slice(None), e2ldof[:, 1:-1]),e2n[c2e][:, :, None, :]) + c2v = bm.set_at(c2v,(slice(None), e2ldof[:, 1:-1]+ldof//2),e2t[c2e][:, :, None, :]) + + + #c2v[:, e2ldof[0, 0]] : (NC, GD), c2e[e2t[0]] : (NC, GD) + # c2v[:, e2ldof[0, 0]] = e2t[c2e[:, 2]]/(bm.sum(e2t[c2e[:, 2]]*e2n[c2e[:, 0]],axis=-1)[:, None]) + # c2v[:, e2ldof[0, -1]] = e2t[c2e[:, 1]]/(bm.sum(e2t[c2e[:, 1]]*e2n[c2e[:, 0]],axis=-1)[:, None]) + # c2v[:, e2ldof[1, 0]] = e2t[c2e[:, 0]]/(bm.sum(e2t[c2e[:, 0]]*e2n[c2e[:, 1]],axis=-1)[:, None]) + # c2v[:, e2ldof[1, -1]] = e2t[c2e[:, 2]]/(bm.sum(e2t[c2e[:, 2]]*e2n[c2e[:, 1]],axis=-1)[:, None]) + # c2v[:, e2ldof[2, 0]] = e2t[c2e[:, 1]]/(bm.sum(e2t[c2e[:, 1]]*e2n[c2e[:, 2]],axis=-1)[:, None]) + # c2v[:, e2ldof[2, -1]] = e2t[c2e[:, 0]]/(bm.sum(e2t[c2e[:, 0]]*e2n[c2e[:, 2]],axis=-1)[:, None]) + + c2v = bm.set_at(c2v,(slice(None), e2ldof[0, 0]),e2t[c2e[:, 2]]/(bm.sum(e2t[c2e[:, 2]]*e2n[c2e[:, 0]],axis=-1)[:, None])) + c2v = bm.set_at(c2v,(slice(None), e2ldof[0, -1]),e2t[c2e[:, 1]]/(bm.sum(e2t[c2e[:, 1]]*e2n[c2e[:, 0]],axis=-1)[:, None])) + c2v = bm.set_at(c2v,(slice(None), e2ldof[1, 0]),e2t[c2e[:, 0]]/(bm.sum(e2t[c2e[:, 0]]*e2n[c2e[:, 1]],axis=-1)[:, None])) + c2v = bm.set_at(c2v,(slice(None), e2ldof[1, -1]),e2t[c2e[:, 2]]/(bm.sum(e2t[c2e[:, 2]]*e2n[c2e[:, 1]],axis=-1)[:, None])) + c2v = bm.set_at(c2v,(slice(None), e2ldof[2, 0]),e2t[c2e[:, 1]]/(bm.sum(e2t[c2e[:, 1]]*e2n[c2e[:, 2]],axis=-1)[:, None])) + c2v = bm.set_at(c2v,(slice(None), e2ldof[2, -1]),e2t[c2e[:, 0]]/(bm.sum(e2t[c2e[:, 0]]*e2n[c2e[:, 2]],axis=-1)[:, None])) + + return c2v + + @barycentric + def edge_basis(self, bcs: TensorLike, index : Index = _S)->TensorLike: + mesh = self.mesh + GD = mesh.geo_dimension() + edof = self.dof.number_of_local_dofs('edge') + sphi = self.lspace.basis(bcs, index=index) #(NQ, NE, edof) + + e2n = mesh.edge_unit_normal() + val = sphi[..., None]*e2n[index, None, None, :] + + return val + + @barycentric + def div_basis(self, bcs: TensorLike,index=_S)->TensorLike: + mesh = self.mesh + NC = mesh.number_of_cells() + GD = mesh.geo_dimension() + ldof = self.dof.number_of_local_dofs() + gdof = self.dof.number_of_global_dofs() + + node = mesh.entity("node") + cell = mesh.entity("cell") + + c2v = self.basis_vector()#(NC, ldof, GD) + + shape = bcs.shape[:-1] + val = bm.zeros((NC,)+shape+(ldof,),device=self.device, dtype=self.ftype) + + sgval = self.lspace.grad_basis(bcs) #(NQ, NC, ldof, GD) + c2v = c2v[:,None,:,:] + c2v = bm.broadcast_to(c2v, val.shape+(GD,)) + # val[..., :ldof//2] = bm.einsum('ijkl, ijkl->ijk', sgval, c2v[..., :ldof//2, :]) + # val[..., ldof//2:] = bm.einsum('ijkl, ijkl->ijk', sgval, c2v[..., ldof//2:, :]) + val = bm.set_at(val,(..., slice(None, ldof//2)),bm.einsum('ijkl, ijkl->ijk', sgval, c2v[..., :ldof//2, :])) + val = bm.set_at(val,(..., slice(ldof//2, None)),bm.einsum('ijkl, ijkl->ijk', sgval, c2v[..., ldof//2:, :])) + return val[index] + + def edge_basis_vector(self): + return self.edge_dof_vector() + + def edge_dof_vector(self, index : Index =_S)->TensorLike: + """ + @brief 获取每条边上每个自由度处的dof方向 + @return (NE, p+1, 2) + """ + #TODO : 测试 + + NE = self.mesh.number_of_edges() + GD = self.mesh.geo_dimension() + p = self.p + + e = bm.arange(NE)[index] + N = len(e) + e2dv = bm.zeros([N, p+1, GD], dtype=self.ftype) + #e2dv[:] = self.mesh.edge_unit_normal(index=index)[:, None] + e2dv = bm.set_at(e2dv,(slice(None)),self.mesh.edge_unit_normal(index=index)[:, None]) + return e2dv + + def cell_to_dof(self): + return self.dof.cell2dof + + def number_of_global_dofs(self): + return self.dof.number_of_global_dofs() + + def number_of_local_dofs(self, doftype='all'): + return self.dof.number_of_local_dofs(doftype) + + def is_boundary_dof(self, threshold=None,method=None): + return self.dof.is_boundary_dof() + + @barycentric + def value(self, uh, bcs, index=_S): + '''@ + @brief 计算一个有限元函数在每个单元的 bc 处的值 + @param bc : (..., GD+1) + @return val : (..., NC, GD) + ''' + phi = self.basis(bcs) + c2d = self.dof.cell_to_dof() + # uh[c2d].shape = (NC, ldof); phi.shape = (..., NC, ldof, GD) + val = bm.einsum("cl, cqlk->cqk", uh[c2d], phi) + return val + + @barycentric + def div_value(self, uh, bcs, index=_S): + pass + + @barycentric + def grad_value(self, uh, bc, index=_S): + pass + + @barycentric + def edge_value(self, uh, bc, index=_S): + pass + + @barycentric + def face_value(self, uh, bc, index=_S): + pass + + def mass_matrix(self): + mesh = self.mesh + NC = mesh.number_of_cells() + ldof = self.dof.number_of_local_dofs() + gdof = self.dof.number_of_global_dofs() + cm = self.cellmeasure + c2d = self.dof.cell_to_dof() #(NC, ldof) + + bcs, ws = self.qf.get_quadrature_points_and_weights() + phi = self.basis(bcs) #(NC, NQ, ldof, GD) + mass = bm.einsum("cqlg, cqdg, c, q->cld", phi, phi, cm, ws) + + I = bm.broadcast_to(c2d[:, :, None], shape=mass.shape) + J = bm.broadcast_to(c2d[:, None, :], shape=mass.shape) + M = csr_matrix((mass.flat, (I.flat, J.flat)), shape=(gdof, gdof)) + return M + + def div_matrix(self, space): + mesh = self.mesh + NC = mesh.number_of_cells() + ldof = self.dof.number_of_local_dofs() + gdof0 = self.dof.number_of_global_dofs() + gdof1 = space.dof.number_of_global_dofs() + cm = self.cellmeasure + + c2d = self.dof.cell_to_dof() #(NC, ldof) + c2d_space = space.dof.cell_to_dof() + + bcs, ws = self.qf.get_quadrature_points_and_weights() + + #if space.basis.coordtype == 'barycentric': + fval = space.basis(bcs) #(NQ, NC, ldof1) + #else: + # points = self.mesh.bc_to_point(bcs) + # fval = space.basis(points) + + phi = self.div_basis(bcs) #(NQ, NC, ldof) + A = bm.einsum("cql, cqd, c, q->cld", phi, fval, cm, ws) + + I = bm.broadcast_to(c2d[:, :, None], shape=A.shape) + J = bm.broadcast_to(c2d_space[:, None, :], shape=A.shape) + B = csr_matrix((A.flat, (I.flat, J.flat)), shape=(gdof0, gdof1)) + return B + + def source_vector(self, f): + mesh = self.mesh + cm = self.cellmeasure + ldof = self.dof.number_of_local_dofs() + gdof = self.dof.number_of_global_dofs() + bcs, ws = self.qf.get_quadrature_points_and_weights() + c2d = self.dof.cell_to_dof() #(NC, ldof) + + p = mesh.bc_to_point(bcs) #(NC, NQ, GD) + fval = f(p) #(NC, NQ, GD) + + phi = self.basis(bcs) #(NC, NQ, ldof, GD) + val = bm.einsum("cqg, cqlg, q, c->cl", fval, phi, ws, cm)# (NC, ldof) + vec = bm.zeros(gdof,device=self.device, dtype=self.ftype) + bm.add.at(vec, c2d, val) + return vec + +# def projection(self, f, method="L2"): +# M = self.mass_matrix() +# b = self.source_vector(f) +# x = spsolve(M, b) +# return self.function(array=x) + +# def function(self, dim=None, array=None, dtype=np.float_): +# if array is None: +# gdof = self.dof.number_of_global_dofs() +# array = np.zeros(gdof, dtype=np.float_) +# return Function(self, dim=dim, array=array, coordtype='barycentric', dtype=dtype) + +# def interplation(self, f): +# pass + +# def L2_error(self, u, uh): +# '''@ +# @brief 计算 ||u - uh||_{L_2} +# ''' +# mesh = self.mesh +# cm = self.cellmeasure +# bcs, ws = self.integrator.get_quadrature_points_and_weights() +# p = mesh.bc_to_point(bcs) #(NQ, NC, GD) +# uval = u(p) #(NQ, NC, GD) +# uhval = uh(bcs) #(NQ, NC, GD) +# errval = np.sum((uval-uhval)*(uval-uhval), axis=-1)#(NQ, NC) +# val = np.einsum("qc, q, c->", errval, ws, cm) +# return np.sqrt(val) + +# def set_neumann_bc(self, g): +# bcs, ws = self.integralalg.faceintegrator.get_quadrature_points_and_weights() + +# edof = self.dof.number_of_local_dofs('edge') +# eidx = self.mesh.ds.boundary_edge_index() +# phi = self.edge_basis(bcs, index=eidx) #(NQ, NE0, edof, GD) +# e2n = self.mesh.edge_unit_normal(index=eidx) +# phi = np.einsum("qelg, eg->qel", phi, e2n) #(NQ, NE0, edof) + +# point = self.mesh.edge_bc_to_point(bcs, index=eidx) +# gval = g(point) #(NQ, NE0) + +# em = self.mesh.entity_measure("edge")[eidx] +# integ = np.einsum("qel, qe, e, q->el", phi, gval, em, ws) + +# e2d = np.ones((len(eidx), edof), dtype=np.int_) +# e2d[:, 0] = edof*eidx +# e2d = np.cumsum(e2d, axis=-1) + +# gdof = self.dof.number_of_global_dofs() +# val = np.zeros(gdof, dtype=np.float_) +# np.add.at(val, e2d, integ) +# return val + + def set_neumann_bc(self, g): + p = self.p + mesh = self.mesh + + qf = self.mesh.quadrature_formula(p+3, 'face') + bcs, ws = qf.get_quadrature_points_and_weights() + + edof = self.dof.number_of_local_dofs('edge') + eidx = self.mesh.boundary_face_index() + gdof = self.dof.number_of_global_dofs() + edge2dof = self.dof.edge_to_dof()[eidx] + + phi = self.edge_basis(bcs)[eidx] #(NE, NQ, edof, GD) + e2n = self.mesh.edge_unit_normal(index=eidx) + phi = bm.einsum("eqlg, eg->eql", phi, e2n) #(NE, NQ, edof) + + points = mesh.bc_to_point(bcs)[eidx] + gval = g(points) + + em = self.mesh.entity_measure("edge")[eidx] + vec = bm.zeros(gdof, device=self.device, dtype=self.ftype) + vec[edge2dof] = bm.einsum("eql, eq, e, q->el", phi, gval, em, ws) + + return vec + + def set_dirichlet_bc(self, gd, uh, threshold=None, q=None,method=None): + p = self.p + mesh = self.mesh + ldof = p+1 + gdof = self.number_of_global_dofs() + + if type(threshold) is bm.array: + index = threshold + else: + index = self.mesh.boundary_face_index() + + edge2dof = self.dof.edge_to_dof()[index] + e2v = self.edge_dof_vector(index=index) #(NE, p+1, 3) + + bcs = self.mesh.multi_index_matrix(p, 1, dtype=self.ftype)/p + point = mesh.bc_to_point(bcs, index=index) + gval = gd(point,e2v) #(NE, p+1, 3) + #uh[edge2dof] = bm.sum(gval*e2v, axis=-1) + uh[edge2dof] = gval + + isDDof = bm.zeros(gdof, dtype=bm.bool) + isDDof[edge2dof] = True + return uh,isDDof + + boundary_interpolate = set_dirichlet_bc + diff --git a/fealpy/functionspace/__init__.py b/fealpy/functionspace/__init__.py index 6f644c911..b73ffef80 100644 --- a/fealpy/functionspace/__init__.py +++ b/fealpy/functionspace/__init__.py @@ -22,3 +22,5 @@ from .parametric_lagrange_fe_space import ParametricLagrangeFESpace from .huzhang_fe_space_2d import HuZhangFESpace2D + +from .BrezziDouglasMariniFiniteElementSpace2d import BDMFiniteElementSpace2d \ No newline at end of file diff --git a/test/functionspace/BrezziDouglasMariniFiniteElementSpace2d_data.py b/test/functionspace/BrezziDouglasMariniFiniteElementSpace2d_data.py new file mode 100644 index 000000000..96b996f4a --- /dev/null +++ b/test/functionspace/BrezziDouglasMariniFiniteElementSpace2d_data.py @@ -0,0 +1,219 @@ +import numpy as np + +# 定义多个典型的 BDMDof 对象 +# mesh = TriangleMesh.from_box(nx = 1,ny =1) +# p = 3 + +init_data = [ + { + "edof":4, + "cdof":20, + "gdof":36, + "cell2dof":np.array([[16, 17, 6, 18, 20, 5, 19, 9, 10, 4, 7, 21, 22, 23, 24, 25, + 8, 26, 27, 11], + [ 0, 1, 14, 2, 28, 13, 3, 10, 9, 12, 15, 29, 30, 31, 32, 33, + 11, 34, 35, 8]],dtype=np.int32), + "basis_vector":np.array([[[ 1. , 0. ], + [ 1. , -0. ], + [ 0. , -1. ], + [ 1. , -0. ], + [ 1. , 0. ], + [ 0. , -1. ], + [ 1. , 1. ], + [-0.70710678, 0.70710678], + [-0.70710678, 0.70710678], + [-1. , -1. ], + [-0. , -1. ], + [ 0. , 1. ], + [ 1. , 0. ], + [ 0. , 1. ], + [ 0. , 1. ], + [ 1. , 0. ], + [ 0. , 1.41421356], + [-0.70710678, -0.70710678], + [-0.70710678, -0.70710678], + [-1.41421356, -0. ]], + + [[-1. , 0. ], + [-1. , -0. ], + [ 0. , 1. ], + [-1. , -0. ], + [ 1. , 0. ], + [ 0. , 1. ], + [-1. , -1. ], + [-0.70710678, 0.70710678], + [-0.70710678, 0.70710678], + [ 1. , 1. ], + [-0. , 1. ], + [ 0. , -1. ], + [-1. , 0. ], + [ 0. , -1. ], + [ 0. , 1. ], + [-1. , 0. ], + [-0. , 1.41421356], + [-0.70710678, -0.70710678], + [-0.70710678, -0.70710678], + [-1.41421356, 0. ]]],dtype=np.float64), + + + "basis":np.array([[[[ 0.0595 , 0. ], + [-0.063 , 0. ], + [-0. , 0.2205 ], + [-0.036 , 0. ], + [ 0.378 , 0. ], + [ 0. , -0.3465 ], + [ 0.056 , 0.056 ], + [ 0.17819091, -0.17819091], + [-0.490025 , 0.490025 ], + [-0.0385 , -0.0385 ], + [-0. , -0.0595 ], + [-0. , -0.063 ], + [-0.2205 , -0. ], + [-0. , -0.036 ], + [ 0. , 0.378 ], + [ 0.3465 , 0. ], + [ 0. , 0.07919596], + [ 0.17819091, 0.17819091], + [-0.490025 , -0.490025 ], + [-0.05444722, -0. ]], + + [[-0.0625 , -0. ], + [ 0.225 , -0. ], + [ 0. , -0.3375 ], + [-0.18 , 0. ], + [ 0.81 , 0. ], + [-0. , 0.0675 ], + [ 0.056 , 0.056 ], + [ 0.07636753, -0.07636753], + [ 0.01909188, -0.01909188], + [-0.0165 , -0.0165 ], + [ 0. , 0.0625 ], + [ 0. , 0.225 ], + [ 0.3375 , 0. ], + [-0. , -0.18 ], + [ 0. , 0.81 ], + [-0.0675 , -0. ], + [ 0. , 0.07919596], + [ 0.07636753, 0.07636753], + [ 0.01909188, 0.01909188], + [-0.02333452, -0. ]], + + [[ 0.0595 , 0. ], + [-0.126 , 0. ], + [-0. , 0.1575 ], + [ 0.036 , -0. ], + [ 0.54 , 0. ], + [ 0. , -0.1125 ], + [-0.032 , -0.032 ], + [-0.12727922, 0.12727922], + [-0.31819805, 0.31819805], + [ 0.0625 , 0.0625 ], + [-0. , -0.0595 ], + [-0. , -0.126 ], + [-0.1575 , -0. ], + [ 0. , 0.036 ], + [ 0. , 0.54 ], + [ 0.1125 , 0. ], + [-0. , -0.04525483], + [-0.12727922, -0.12727922], + [-0.31819805, -0.31819805], + [ 0.08838835, 0. ]]], + + + [[[-0.0595 , 0. ], + [ 0.063 , 0. ], + [-0. , -0.2205 ], + [ 0.036 , 0. ], + [ 0.378 , 0. ], + [ 0. , 0.3465 ], + [-0.056 , -0.056 ], + [ 0.17819091, -0.17819091], + [-0.490025 , 0.490025 ], + [ 0.0385 , 0.0385 ], + [-0. , 0.0595 ], + [-0. , 0.063 ], + [ 0.2205 , -0. ], + [-0. , 0.036 ], + [ 0. , 0.378 ], + [-0.3465 , 0. ], + [-0. , 0.07919596], + [ 0.17819091, 0.17819091], + [-0.490025 , -0.490025 ], + [-0.05444722, 0. ]], + + [[ 0.0625 , -0. ], + [-0.225 , -0. ], + [ 0. , 0.3375 ], + [ 0.18 , 0. ], + [ 0.81 , 0. ], + [-0. , -0.0675 ], + [-0.056 , -0.056 ], + [ 0.07636753, -0.07636753], + [ 0.01909188, -0.01909188], + [ 0.0165 , 0.0165 ], + [ 0. , -0.0625 ], + [ 0. , -0.225 ], + [-0.3375 , 0. ], + [-0. , 0.18 ], + [ 0. , 0.81 ], + [ 0.0675 , -0. ], + [-0. , 0.07919596], + [ 0.07636753, 0.07636753], + [ 0.01909188, 0.01909188], + [-0.02333452, 0. ]], + + [[-0.0595 , 0. ], + [ 0.126 , 0. ], + [-0. , -0.1575 ], + [-0.036 , -0. ], + [ 0.54 , 0. ], + [ 0. , 0.1125 ], + [ 0.032 , 0.032 ], + [-0.12727922, 0.12727922], + [-0.31819805, 0.31819805], + [-0.0625 , -0.0625 ], + [-0. , 0.0595 ], + [-0. , 0.126 ], + [ 0.1575 , -0. ], + [ 0. , -0.036 ], + [ 0. , 0.54 ], + [-0.1125 , 0. ], + [ 0. , -0.04525483], + [-0.12727922, -0.12727922], + [-0.31819805, -0.31819805], + [ 0.08838835, -0. ]]]],dtype=np.float64), + + "div_basis":np.array([[[ 0.235 , -0.36 , -1.26 , -0.36 , + 3.24 , 3.465 , -0.26 , 0.19091883, + 4.48659253, 1.315 , 0.235 , 0.045 , + -0.945 , 0.45 , -1.89 , 2.025 , + -0.36769553, -0.70003571, -0.41365747, 1.85969083], + [-0.125 , 1.8 , 2.7 , -0.36 , + -1.08 , -0.135 , -0.26 , -0.06363961, + 0.41365747, -0.485 , -0.125 , -0.675 , + 1.575 , 0.81 , 2.43 , -1.935 , + -0.36769553, -0.44547727, 0.6045763 , -0.68589358], + [ 0.235 , -0.72 , -0.9 , 0.36 , + 4.32 , 1.125 , -0.44 , 2.4819448 , + 3.34107954, -0.125 , 0.235 , 0.405 , + -0.585 , 0.27 , -4.05 , 0.225 , + -0.62225397, -1.97282792, 1.75008928, -0.1767767 ]], + + [[ 0.235 , -0.36 , -1.26 , -0.36 , + -3.24 , 3.465 , -0.26 , -0.19091883, + -4.48659253, 1.315 , 0.235 , 0.045 , + -0.945 , 0.45 , 1.89 , 2.025 , + 0.36769553, 0.70003571, 0.41365747, -1.85969083], + [-0.125 , 1.8 , 2.7 , -0.36 , + 1.08 , -0.135 , -0.26 , 0.06363961, + -0.41365747, -0.485 , -0.125 , -0.675 , + 1.575 , 0.81 , -2.43 , -1.935 , + 0.36769553, 0.44547727, -0.6045763 , 0.68589358], + [ 0.235 , -0.72 , -0.9 , 0.36 , + -4.32 , 1.125 , -0.44 , -2.4819448 , + -3.34107954, -0.125 , 0.235 , 0.405 , + -0.585 , 0.27 , 4.05 , 0.225 , + 0.62225397, 1.97282792, -1.75008928, 0.1767767 ]]]), + + } +] \ No newline at end of file diff --git a/test/functionspace/test_BrezziDouglasMariniFiniteElementSpace2d.py b/test/functionspace/test_BrezziDouglasMariniFiniteElementSpace2d.py new file mode 100644 index 000000000..52946952e --- /dev/null +++ b/test/functionspace/test_BrezziDouglasMariniFiniteElementSpace2d.py @@ -0,0 +1,76 @@ + +import numpy as np +import pytest + +from fealpy.functionspace.BrezziDouglasMariniFiniteElementSpace2d import BDMDof +from fealpy.functionspace.BrezziDouglasMariniFiniteElementSpace2d import BDMFiniteElementSpace2d +from fealpy.backend import backend_manager as bm +from fealpy.mesh.triangle_mesh import TriangleMesh + +from BrezziDouglasMariniFiniteElementSpace2d_data import * + +class TestBDMDof: + @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) + @pytest.mark.parametrize("meshdata", init_data) + def test_cell_to_dof(self,meshdata,backend): + bm.set_backend(backend) + + mesh = TriangleMesh.from_box(nx = 1,ny =1) + p = 3 + a = BDMDof(mesh,p) + assert a.number_of_local_dofs("edge") == meshdata["edof"] + assert a.number_of_local_dofs() == meshdata["cdof"] + assert a.number_of_global_dofs() == meshdata["gdof"] + + cell2dof = a.cell2dof + np.testing.assert_array_equal(bm.to_numpy(cell2dof), meshdata["cell2dof"]) + +class TestBDMFiniteElementSpace2d: + @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) + @pytest.mark.parametrize("meshdata", init_data) + def test_basis_vector(self,meshdata,backend): + bm.set_backend(backend) + + mesh = TriangleMesh.from_box(nx = 1,ny =1) + p = 3 + a = BDMFiniteElementSpace2d(mesh,p) + + basis_vector = a.basis_vector() + np.testing.assert_allclose(bm.to_numpy(basis_vector), meshdata["basis_vector"],1e-8) + + @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) + @pytest.mark.parametrize("meshdata", init_data) + def test_basis(self,meshdata,backend): + bm.set_backend(backend) + + mesh = TriangleMesh.from_box(nx = 1,ny =1) + p = 3 + a = BDMFiniteElementSpace2d(mesh,p) + bcs = bm.array([[0.1,0.2,0.7], + [0.5,0.2,0.3], + [0.1,0.4,0.5]]) + basis = a.basis(bcs) + np.testing.assert_allclose(bm.to_numpy(basis), meshdata["basis"],1e-6) + + @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) + @pytest.mark.parametrize("meshdata", init_data) + def test_div_basis(self,meshdata,backend): + bm.set_backend(backend) + + mesh = TriangleMesh.from_box(nx = 1,ny =1) + p = 3 + a = BDMFiniteElementSpace2d(mesh,p) + bcs = bm.array([[0.1,0.2,0.7], + [0.5,0.2,0.3], + [0.1,0.4,0.5]],dtype=bm.float64) + div_basis = a.div_basis(bcs) + np.testing.assert_allclose(bm.to_numpy(div_basis), meshdata["div_basis"],1e-7) + + +if __name__ == "__main__": + # test = TestBDMDof() + # test.test_cell_to_dof(init_data[0],'pytorch') + test = TestBDMFiniteElementSpace2d() + #test.test_basis_vector(init_data[0],'numpy') + #test.test_basis(init_data[0],'numpy') + test.test_div_basis(init_data[0],'pytorch') \ No newline at end of file From 0e7ba5f9f6461b398935fbbff89c0cbf6219a911 Mon Sep 17 00:00:00 2001 From: concha Date: Fri, 22 Nov 2024 11:07:22 +0800 Subject: [PATCH 19/63] =?UTF-8?q?=E9=BD=BF=E8=BD=AE=E5=85=AD=E9=9D=A2?= =?UTF-8?q?=E4=BD=93=E9=A1=B9=E7=9B=AE=EF=BC=9A=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/gearx/test/test_gear_system.py | 160 ++++++++++++++++++++++++++--- 1 file changed, 143 insertions(+), 17 deletions(-) diff --git a/app/gearx/test/test_gear_system.py b/app/gearx/test/test_gear_system.py index 30bb6b23a..69d0aa2b0 100644 --- a/app/gearx/test/test_gear_system.py +++ b/app/gearx/test/test_gear_system.py @@ -77,7 +77,8 @@ def test_internal_gear(self): z_cutter = data['z_cutter'] xn_cutter = data['xn_cutter'] - internal_gear = InternalGear(m_n, z, alpha_n, beta, x_n, hac, cc, rcc, jn, n1, n2, n3, na, nf, nw, outer_diam, z_cutter, + internal_gear = InternalGear(m_n, z, alpha_n, beta, x_n, hac, cc, rcc, jn, n1, n2, n3, na, nf, nw, outer_diam, + z_cutter, xn_cutter, tooth_width) t = np.array([0.1, 0.2, 25]) @@ -85,7 +86,8 @@ def test_internal_gear(self): p1_dis = internal_gear.get_tip_intersection_points(t) alphawt = InternalGear.ainv( - 2 * (internal_gear.x_n - internal_gear.xn_cutter) * tan(internal_gear.alpha_n) / (internal_gear.z - internal_gear.z_cutter) + ( + 2 * (internal_gear.x_n - internal_gear.xn_cutter) * tan(internal_gear.alpha_n) / ( + internal_gear.z - internal_gear.z_cutter) + ( tan(internal_gear.alpha_t) - internal_gear.alpha_t)) E = 0.5 * (internal_gear.d - internal_gear.d_cutter) + internal_gear.m_t * ( 0.5 * (internal_gear.z - internal_gear.z_cutter) * (cos(internal_gear.alpha_t) / cos(alphawt) - 1)) @@ -185,7 +187,7 @@ def test_get_one_tooth(self): # 寻找内圈上节点 node_r = np.sqrt(node[:, 0] ** 2 + node[:, 1] ** 2) is_inner_node = np.abs(node_r - external_gear.inner_diam / 2) < 1e-11 - inner_node_idx = np.where(np.abs(node_r - external_gear.inner_diam / 2)<1e-11)[0] + inner_node_idx = np.where(np.abs(node_r - external_gear.inner_diam / 2) < 1e-11)[0] with open('../data/external_gear_test_data.pkl', 'wb') as f: pickle.dump({'external_gear': external_gear, 'hex_mesh': target_hex_mesh, 'helix_node': helix_node, @@ -206,23 +208,147 @@ def test_export_to_inp(self): node = hex_mesh.node cell = hex_mesh.cell fixed_nodes = inner_node_idx - load_nodes = cell[target_cell_idx[0]] - loads = np.array([[-10, -10, 0], - [-10, -10, 0], - [-10, -10, 0], - [-10, -10, 0], - [-10, -10, 0], - [-10, -10, 0], - [-10, -10, 0], - [-10, -10, 0]]) - young_modulus = 206e9 + load_nodes = np.array([454, 120, 121, 455, 2632, 2298, 2299, 2633, 2631, + 2297, 2298, 2632, 4809, 4475, 4476, 4810, 4808, 4474, + 4475, 4809, 6986, 6652, 6653, 6987, 6985, 6651, 6652, + 6986, 9163, 8829, 8830, 9164, 9162, 8828, 8829, 9163, + 11340, 11006, 11007, 11341, 11339, 11005, 11006, 11340, 13517, + 13183, 13184, 13518, 13516, 13182, 13183, 13517, 15694, 15360, + 15361, 15695, 17871, 17537, 17538, 17872, 20049, 19715, 19716, + 20050, 20048, 19714, 19715, 20049, 22226, 21892, 21893, 22227, + 22225, 21891, 21892, 22226, 24403, 24069, 24070, 24404, 24402, + 24068, 24069, 24403, 26580, 26246, 26247, 26581, 26579, 26245, + 26246, 26580, 28757, 28423, 28424, 28758, 28756, 28422, 28423, + 28757, 30934, 30600, 30601, 30935, 30599, 30501, 30600, 30934, + 32777, 32679, 32778, 33112, 32777, 32679, 32778, 33112, 34955, + 34857, 34956, 35290]) + loads = np.array([[0., 0., 0.], + [0., 0., 0.], + [0., 0., 0.], + [0., 0., 0.], + [299.37136845, 299.37136845, 299.37136845], + [0., 0., 0.], + [0., 0., 0.], + [260.01223405, 260.01223405, 260.01223405], + [26.33068321, 26.33068321, 26.33068321], + [0., 0., 0.], + [0., 0., 0.], + [299.37136845, 299.37136845, 299.37136845], + [342.35133024, 342.35133024, 342.35133024], + [0., 0., 0.], + [0., 0., 0.], + [356.301615, 356.301615, 356.301615], + [41.34705476, 41.34705476, 41.34705476], + [0., 0., 0.], + [0., 0., 0.], + [342.35133024, 342.35133024, 342.35133024], + [403.59356272, 403.59356272, 403.59356272], + [0., 0., 0.], + [0., 0., 0.], + [460.91807739, 460.91807739, 460.91807739], + [34.05978846, 34.05978846, 34.05978846], + [0., 0., 0.], + [0., 0., 0.], + [403.59356272, 403.59356272, 403.59356272], + [513.60593762, 513.60593762, 513.60593762], + [0., 0., 0.], + [0., 0., 0.], + [400.30123444, 400.30123444, 400.30123444], + [28.9499708, 28.9499708, 28.9499708], + [0., 0., 0.], + [0., 0., 0.], + [513.60593762, 513.60593762, 513.60593762], + [667.75137915, 667.75137915, 667.75137915], + [0., 0., 0.], + [0., 0., 0.], + [351.14466476, 351.14466476, 351.14466476], + [32.53252752, 32.53252752, 32.53252752], + [0., 0., 0.], + [0., 0., 0.], + [667.75137915, 667.75137915, 667.75137915], + [828.37446848, 828.37446848, 828.37446848], + [0., 0., 0.], + [0., 0., 0.], + [258.41556042, 258.41556042, 258.41556042], + [58.92425681, 58.92425681, 58.92425681], + [0., 0., 0.], + [0., 0., 0.], + [828.37446848, 828.37446848, 828.37446848], + [9.82070947, 9.82070947, 9.82070947], + [0., 0., 0.], + [0., 0., 0.], + [135.89357625, 135.89357625, 135.89357625], + [0., 0., 0.], + [0., 0., 0.], + [0., 0., 0.], + [0., 0., 0.], + [256.67649633, 256.67649633, 256.67649633], + [0., 0., 0.], + [0., 0., 0.], + [966.27329305, 966.27329305, 966.27329305], + [29.90735348, 29.90735348, 29.90735348], + [0., 0., 0.], + [0., 0., 0.], + [256.67649633, 256.67649633, 256.67649633], + [425.76910538, 425.76910538, 425.76910538], + [0., 0., 0.], + [0., 0., 0.], + [797.69873627, 797.69873627, 797.69873627], + [93.67501549, 93.67501549, 93.67501549], + [0., 0., 0.], + [0., 0., 0.], + [425.76910538, 425.76910538, 425.76910538], + [557.24179034, 557.24179034, 557.24179034], + [0., 0., 0.], + [0., 0., 0.], + [615.81246126, 615.81246126, 615.81246126], + [204.08860554, 204.08860554, 204.08860554], + [0., 0., 0.], + [0., 0., 0.], + [557.24179034, 557.24179034, 557.24179034], + [622.73987876, 622.73987876, 622.73987876], + [0., 0., 0.], + [0., 0., 0.], + [430.73900214, 430.73900214, 430.73900214], + [375.09254767, 375.09254767, 375.09254767], + [0., 0., 0.], + [0., 0., 0.], + [622.73987876, 622.73987876, 622.73987876], + [591.74426803, 591.74426803, 591.74426803], + [0., 0., 0.], + [0., 0., 0.], + [262.96630353, 262.96630353, 262.96630353], + [611.00371415, 611.00371415, 611.00371415], + [0., 0., 0.], + [0., 0., 0.], + [591.74426803, 591.74426803, 591.74426803], + [439.89833767, 439.89833767, 439.89833767], + [0., 0., 0.], + [0., 0., 0.], + [124.16994291, 124.16994291, 124.16994291], + [918.78886228, 918.78886228, 918.78886228], + [0., 0., 0.], + [0., 0., 0.], + [439.89833767, 439.89833767, 439.89833767], + [153.13147705, 153.13147705, 153.13147705], + [0., 0., 0.], + [0., 0., 0.], + [1342.58280867, 1342.58280867, 1342.58280867], + [153.13147705, 153.13147705, 153.13147705], + [0., 0., 0.], + [0., 0., 0.], + [1342.58280867, 1342.58280867, 1342.58280867], + [0., 0., 0.], + [0., 0., 0.], + [0., 0., 0.], + [0., 0., 0.]]) + young_modulus = 206e3 poisson_ratio = 0.3 density = 7850 - export_to_inp('../data/external_gear_test.inp', node, cell, fixed_nodes, load_nodes, loads, young_modulus, poisson_ratio, density) - - + export_to_inp('../data/external_gear_test.inp', node, cell, fixed_nodes, load_nodes, loads, young_modulus, + poisson_ratio, density) if __name__ == "__main__": - pytest.main(["./test_gear_system.py", "-k", "test_external_gear"]) \ No newline at end of file + pytest.main(["./test_gear_system.py", "-k", "test_external_gear"]) From a86a3366183180eae6f7010286827ca14c48915a Mon Sep 17 00:00:00 2001 From: tiantian0347 Date: Fri, 22 Nov 2024 16:26:33 +0800 Subject: [PATCH 20/63] update --- .../cases/phase_field/model0_example.py | 6 +- .../fracturex/cases/phase_field/model3d.py | 60 +++++++--- .../square_domian_with_fracture.py | 6 +- .../crack_surface_density_function.py | 23 ++-- .../{main_solver.py => main_solve.py} | 104 ++++++++++-------- .../phasefield/phase_fracture_material.py | 71 +----------- .../tests/test_fracture_constitutive_model.py | 2 +- .../fracturex/tests/test_main_solver.py | 2 +- 8 files changed, 128 insertions(+), 146 deletions(-) rename app/fracturex/fracturex/phasefield/{main_solver.py => main_solve.py} (92%) diff --git a/app/fracturex/fracturex/cases/phase_field/model0_example.py b/app/fracturex/fracturex/cases/phase_field/model0_example.py index 09315834b..709a96269 100644 --- a/app/fracturex/fracturex/cases/phase_field/model0_example.py +++ b/app/fracturex/fracturex/cases/phase_field/model0_example.py @@ -6,7 +6,7 @@ from fealpy.mesh import TriangleMesh from fealpy.old.geometry.domain_2d import SquareWithCircleHoleDomain -from app.fracturex.fracturex.phasefield.main_solver import MainSolve +from app.fracturex.fracturex.phasefield.main_solve import MainSolve from fealpy.utils import timer @@ -133,7 +133,7 @@ def is_dirchlet_boundary(self, p): mesh = TriangleMesh.from_domain_distmesh(domain, maxit=100) -ms = MainSolve(mesh=mesh, material_params=model.params, p=p) +ms = MainSolve(mesh=mesh, material_params=model.params) tmr.send('init') # 拉伸模型边界条件 @@ -150,7 +150,7 @@ def is_dirchlet_boundary(self, p): ms.output_timer() ms.save_vtkfile(fname=vtkname) -ms.solve(maxit=maxit) +ms.solve(p=p, maxit=maxit) tmr.send('stop') tmr.send(None) diff --git a/app/fracturex/fracturex/cases/phase_field/model3d.py b/app/fracturex/fracturex/cases/phase_field/model3d.py index 0e96f1581..0f71fb8be 100644 --- a/app/fracturex/fracturex/cases/phase_field/model3d.py +++ b/app/fracturex/fracturex/cases/phase_field/model3d.py @@ -1,7 +1,9 @@ from fealpy.backend import backend_manager as bm +import torch +import numpy as np from fealpy.mesh import TetrahedronMesh, HexahedronMesh -from app.fracturex.fracturex.phasefield.main_solver import MainSolver +from app.fracturex.fracturex.phasefield.main_solve import MainSolve from fealpy.utils import timer import time @@ -64,9 +66,10 @@ def is_dirchlet_boundary(self, p): default='HybridModel', type=str, help='有限元方法, 默认为 HybridModel.') + parser.add_argument('--mesh_type', - default='tet', type=str, - help='网格类型, 默认为 tet.') + default='tri', type=str, + help='网格类型, 默认为 tri.') parser.add_argument('--enable_adaptive', default=False, type=bool, @@ -81,8 +84,8 @@ def is_dirchlet_boundary(self, p): help='网格加密方法, 默认为 bisect.') parser.add_argument('--n', - default=2, type=int, - help='初始网格加密次数, 默认为 2.') + default=6, type=int, + help='初始网格加密次数, 默认为 6.') parser.add_argument('--vtkname', default='test', type=str, @@ -92,6 +95,18 @@ def is_dirchlet_boundary(self, p): default=True, type=bool, help='是否保存 vtk 文件, 默认为 False.') +parser.add_argument('--force_type', + default='y', type=str, + help='Force type, default is y.') + +parser.add_argument('--gpu', + default=False, type=bool, + help='是否使用 GPU, 默认为 False.') + +parser.add_argument('--cupy', + default=False, type=bool, + help='是否使用cupy求解.') + args = parser.parse_args() p= args.degree maxit = args.maxit @@ -101,15 +116,18 @@ def is_dirchlet_boundary(self, p): marking_strategy = args.marking_strategy refine_method = args.refine_method n = args.n -vtkname = args.vtkname save_vtkfile = args.save_vtkfile vtkname = args.vtkname +'_' + args.mesh_type + '_' - +force_type = args.force_type +gpu = args.gpu +cupy = args.cupy tmr = timer() next(tmr) start = time.time() bm.set_backend(backend) +if gpu: + bm.set_default_device('cuda') model = square_with_circular_notch_3d() if args.mesh_type == 'hex': @@ -121,33 +139,43 @@ def is_dirchlet_boundary(self, p): mesh.uniform_refine(n=n) + fname = args.mesh_type + '_3d_square_with_a_notch_init.vtu' mesh.to_vtk(fname=fname) -ms = MainSolver(mesh=mesh, material_params=model.params, p=p, model_type=model_type) +ms = MainSolve(mesh=mesh, material_params=model.params, model_type=model_type) tmr.send('init') -if enable_adaptive: - print('Enable adaptive refinement.') - ms.set_adaptive_refinement(marking_strategy=marking_strategy, refine_method=refine_method) - -# 拉伸模型边界条件 ms.add_boundary_condition('force', 'Dirichlet', model.is_force_boundary, model.is_z_force(), 'z') # 固定位移边界条件 ms.add_boundary_condition('displacement', 'Dirichlet', model.is_dirchlet_boundary, 0) -ms.solve(maxit=maxit, vtkname=vtkname) + +if bm.backend_name == 'pytorch': + ms.auto_assembly_matrix() +if cupy: + ms.set_cupy_solver() + +ms.output_timer() +ms.save_vtkfile(fname=vtkname) +ms.solve(p=p, maxit=maxit) tmr.send('stop') +tmr.send(None) end = time.time() force = ms.Rforce disp = ms.force_value -tname = args.mesh_type + '_p' + str(p) + '_' + 'model3d_disp.txt' + +ftname = 'force_'+args.mesh_type + '_p' + str(p) + '_' + 'model3d_disp.pt' + +torch.save(force, ftname) +#np.savetxt('force'+tname, bm.to_numpy(force)) +tname = 'params_'+args.mesh_type + '_p' + str(p) + '_' + 'model3d_disp.txt' with open(tname, 'w') as file: - file.write(f'force: {force},\n time: {end-start},\n degree:{p},\n, backend:{backend},\n, model_type:{model_type},\n, enable_adaptive:{enable_adaptive},\n, marking_strategy:{marking_strategy},\n, refine_method:{refine_method},\n, n:{n},\n, maxit:{maxit},\n, vtkname:{vtkname}\n') + file.write(f'\n time: {end-start},\n degree:{p},\n, backend:{backend},\n, model_type:{model_type},\n, enable_adaptive:{enable_adaptive},\n, marking_strategy:{marking_strategy},\n, refine_method:{refine_method},\n, n:{n},\n, maxit:{maxit},\n, vtkname:{vtkname}\n') fig, axs = plt.subplots() plt.plot(disp, force, label='Force') plt.xlabel('Displacement Increment') diff --git a/app/fracturex/fracturex/cases/phase_field/square_domian_with_fracture.py b/app/fracturex/fracturex/cases/phase_field/square_domian_with_fracture.py index b1d45307c..7673a9a71 100644 --- a/app/fracturex/fracturex/cases/phase_field/square_domian_with_fracture.py +++ b/app/fracturex/fracturex/cases/phase_field/square_domian_with_fracture.py @@ -6,7 +6,7 @@ from fealpy.mesh import TriangleMesh, QuadrangleMesh -from app.fracturex.fracturex.phasefield.main_solver import MainSolve +from app.fracturex.fracturex.phasefield.main_solve import MainSolve from fealpy.utils import timer import time import matplotlib.pyplot as plt @@ -168,7 +168,7 @@ def adaptive_mesh(self, mesh, d0=0.49, d1=1.01, h=0.005): fname = args.mesh_type + '_square_with_a_notch_init.vtu' mesh.to_vtk(fname=fname) -ms = MainSolve(mesh=mesh, material_params=model.params, p=p, model_type=model_type) +ms = MainSolve(mesh=mesh, material_params=model.params, model_type=model_type) tmr.send('init') ''' @@ -198,7 +198,7 @@ def adaptive_mesh(self, mesh, d0=0.49, d1=1.01, h=0.005): ms.output_timer() ms.save_vtkfile(fname=vtkname) -ms.solve(maxit=maxit) +ms.solve(p=p, maxit=maxit) tmr.send('stop') tmr.send(None) diff --git a/app/fracturex/fracturex/phasefield/crack_surface_density_function.py b/app/fracturex/fracturex/phasefield/crack_surface_density_function.py index 0b1a872d6..4f41c1bcb 100644 --- a/app/fracturex/fracturex/phasefield/crack_surface_density_function.py +++ b/app/fracturex/fracturex/phasefield/crack_surface_density_function.py @@ -73,21 +73,28 @@ def _AT1_grad_grad_density(self, d): """ return 0, 8/3 + # User-defined model implementations def _user_defined_density(self, d): """ - The user defined crack surface density function g(d). + The user-defined crack surface density function h(d). """ - raise NotImplementedError() - + if 'density_func' in self.params: + return self.params['density_func'](d) + raise NotImplementedError("User-defined density function is not provided.") + def _user_defined_grad_density(self, d): """ - The derivative of the user defined crack surface density function g'(d). + The first derivative of the user-defined crack surface density function h'(d). """ - raise NotImplementedError() - + if 'grad_density_func' in self.params: + return self.params['grad_density_func'](d) + raise NotImplementedError("User-defined first derivative function is not provided.") + def _user_defined_grad_grad_density(self, d): """ - The second derivative of the user defined crack surface density function g''(d). + The second derivative of the user-defined crack surface density function h''(d). """ - raise NotImplementedError() + if 'grad_grad_density_func' in self.params: + return self.params['grad_grad_density_func'](d) + raise NotImplementedError("User-defined second derivative function is not provided.") \ No newline at end of file diff --git a/app/fracturex/fracturex/phasefield/main_solver.py b/app/fracturex/fracturex/phasefield/main_solve.py similarity index 92% rename from app/fracturex/fracturex/phasefield/main_solver.py rename to app/fracturex/fracturex/phasefield/main_solve.py index 4794aeb69..4eebe46a9 100644 --- a/app/fracturex/fracturex/phasefield/main_solver.py +++ b/app/fracturex/fracturex/phasefield/main_solve.py @@ -29,7 +29,7 @@ class MainSolve: def __init__(self, mesh, material_params: Dict, - model_type: str = 'HybridModel', method: str = 'lfem', p: int = 1, q: int = None): + model_type: str = 'HybridModel'): """ Initialize the MainSolver class with more customization options. @@ -49,26 +49,10 @@ def __init__(self, mesh, material_params: Dict, The method for solving the problem, by default 'lfem'. """ self.mesh = mesh - self.p = p - self.q = self.p + 3 if q is None else q - # Material and energy degradation function - self.set_energy_degradation(degradation_type='quadratic') - self.set_crack_surface_density(density_type='AT2') - self.model_type = model_type - self.pfcm = PhaseFractureMaterialFactory.create(model_type, material_params, self.EDFunc) - - self._method = method - # Initialize spaces - if self._method == 'lfem': - self.set_lfe_space() - else: - raise ValueError(f"Unknown method: {self.method}") - - self.pfcm.update_disp(self.uh) - self.pfcm.update_phase(self.d) + self.material_params = material_params # Material parameters self.Gc = material_params['Gc'] @@ -76,32 +60,54 @@ def __init__(self, mesh, material_params: Dict, self.bc_dict = {} + self.CSDFunc = None + self.EDFunc = None + self.enable_refinement = False self._save_vtk = False self._atype = None self._timer = False - - if bm.device_type(self.uh) == 'cpu': - print('Using scipy solver.') - self._solver = 'scipy' - elif bm.device_type(self.uh) == 'cuda': - print('Using cupy solver.') - self._solver = 'cupy' - else: - raise ValueError(f"Unknown device type: {bm.device_type(self.uh)}") + + self._solver = None # Initialize the timer self.tmr = timer() next(self.tmr) - def initialization_settings(self): + def initialization_settings(self, p: int = 1, q: int = None, ): """ Initialize the settings for the problem. """ - pass + # Material and energy degradation function + if self.EDFunc is None: + self.set_energy_degradation(degradation_type='quadratic') + if self.CSDFunc is None: + self.set_crack_surface_density(density_type='AT2') + + self.pfcm = PhaseFractureMaterialFactory.create(self.model_type, self.material_params, self.EDFunc) - def solve(self, maxit: int = 50): + # Initialize spaces + if self._method == 'lfem': + self.set_lfe_space(p=p, q=q) + else: + raise ValueError(f"Unknown method: {self.method}") + + self.pfcm.update_disp(self.uh) + self.pfcm.update_phase(self.d) + + # solver + if self._solver is None: + if bm.device_type(self.uh) == 'cpu': + print('Using scipy solver.') + self._solver = 'scipy' + elif bm.device_type(self.uh) == 'cuda': + print('Using cupy solver.') + self._solver = 'cupy' + else: + raise ValueError(f"Unknown device type: {bm.device_type(self.uh)}") + + def solve(self, method: str = 'lfem', p: int = 1, q: int = None, maxit: int = 50): """ Solve the phase-field fracture problem. @@ -114,6 +120,8 @@ def solve(self, maxit: int = 50): vtkname : str, optional VTK output file name, by default None. """ + self._method = method + self.initialization_settings(p=p, q=q) self._initialize_force_boundary() self._Rforce = bm.zeros_like(self._force_value) @@ -314,7 +322,7 @@ def solve_displacement_auto(self) -> float: @barycentric def postive_coef(bc, **kwargs): - return self.pfcm.positive_coef(bc) + return self.EDFunc.degradation_function(self.d(bc)) postive_coef.uh = uh postive_coef.kernel_func = self.pfcm.positive_stress_func @@ -327,7 +335,7 @@ def postive_coef(bc, **kwargs): else: @barycentric def negative_coef(bc, **kwargs): - return self.pfcm.negative_coef(bc) + return 1 negative_coef.uh = uh negative_coef.kernel_func = self.pfcm.negative_stress_func @@ -371,11 +379,24 @@ def solve_phase_field_auto(self) -> float: @barycentric def diffusion_coef(bc, **kwargs): return Gc * l0 * 2 / c_d + + def diffusion_kernel_func(u): + return u + + def diffusion_grad_kernel_func(u): + return 1 @barycentric def mass_coef1(bc, **kwargs): return Gc / (l0 * c_d) + @barycentric + def mass_kernel_func1(u): + return self.CSDFunc.grad_density_function(u)[0] + + def mass_grad_kernel_func1(u): + return self.CSDFunc.grad_grad_density_function(u)[0] + @barycentric def mass_coef2(bc, **kwargs): return self.pfcm.maximum_historical_field(bc) @@ -388,25 +409,18 @@ def mass_kernel_func2(u): def mass_grad_kernel_func2(u): return self.EDFunc.grad_grad_degradation_function(u) - @barycentric - def kernel_func(u): - return self.CSDFunc.grad_density_function(u)[0] - - def grad_kernel_func(u): - return self.CSDFunc.grad_grad_density_function(u)[0] - @barycentric def source_coef(bc, index): gg_gd = self.EDFunc.grad_degradation_function_constant_coef() return -1 * gg_gd * self.pfcm.maximum_historical_field(bc) - diffusion_coef.kernel_func = kernel_func - mass_coef1.kernel_func = kernel_func + diffusion_coef.kernel_func = diffusion_kernel_func + mass_coef1.kernel_func = mass_kernel_func1 mass_coef2.kernel_func = mass_kernel_func2 if bm.backend_name == 'numpy': - diffusion_coef.grad_kernel_func = grad_kernel_func - mass_coef1.grad_kernel_func = grad_kernel_func + diffusion_coef.grad_kernel_func = diffusion_grad_kernel_func + mass_coef1.grad_kernel_func = mass_grad_kernel_func1 mass_coef2.grad_kernel_func = mass_grad_kernel_func2 mass_coef1.uh = d @@ -440,10 +454,12 @@ def source_coef(bc, index): return bm.linalg.norm(R) - def set_lfe_space(self): + def set_lfe_space(self, p: int = 1, q: int = None): """ Set the finite element spaces for displacement and phase fields. """ + self.p = p + self.q = self.p + 3 if q is None else q self.space = LagrangeFESpace(self.mesh, self.p) self.tspace = TensorFunctionSpace(self.space, (self.mesh.geo_dimension(), -1)) self.d = self.space.function() diff --git a/app/fracturex/fracturex/phasefield/phase_fracture_material.py b/app/fracturex/fracturex/phasefield/phase_fracture_material.py index a0d4ee856..fa3ab61f0 100644 --- a/app/fracturex/fracturex/phasefield/phase_fracture_material.py +++ b/app/fracturex/fracturex/phasefield/phase_fracture_material.py @@ -132,14 +132,6 @@ def elastic_matrix(self, bc) -> TensorLike: D = D0 * gd[..., None, None] return D - def positive_coef(self, bc) -> TensorLike: - """ - @brief Compute the positive energy coefficient. - """ - d = self.d - gd = self._gd.degradation_function(d(bc)) - return gd - def positive_stress_func(self, guh) -> TensorLike: """ @brief Compute the stress tensor from the grad displacement tensor. @@ -179,12 +171,6 @@ def negative_stress_func(self, guh) -> TensorLike: flat_stress = flatten_symmetric_matrices(stress) return flat_stress - def negative_coef(self, bc) -> TensorLike: - """ - @brief Compute the negative energy coefficient. - """ - return 1 - class AnisotropicModel(BasedPhaseFractureMaterial): def stress_value(self, bc) -> TensorLike: # 计算各向异性模型下的应力 @@ -206,14 +192,6 @@ def positive_stress_func(self, guh) -> TensorLike: The flattened stress tensor. """ pass - - def positive_coef(self, bc) -> TensorLike: - """ - @brief Compute the positive energy coefficient. - """ - d = self.d - gd = self._gd.degradation_function(d(bc)) - return gd def negative_stress_func(self, guh) -> TensorLike: """ @@ -228,12 +206,6 @@ def negative_stress_func(self, guh) -> TensorLike: """ pass - def negative_coef(self, bc) -> TensorLike: - """ - @brief Compute the negative energy coefficient. - """ - return 1 - class DeviatoricModel(BasedPhaseFractureMaterial): def stress_value(self, bc) -> TensorLike: # 计算偏应力模型下的应力 @@ -256,14 +228,6 @@ def positive_stress_func(self, guh) -> TensorLike: """ pass - def positive_coef(self, bc) -> TensorLike: - """ - @brief Compute the positive energy coefficient. - """ - d = self.d - gd = self._gd.degradation_function(d(bc)) - return gd - def negative_stress_func(self, guh) -> TensorLike: """ @brief Compute the stress tensor from the grad displacement tensor. @@ -277,11 +241,6 @@ def negative_stress_func(self, guh) -> TensorLike: """ pass - def negative_coef(self, bc) -> TensorLike: - """ - @brief Compute the negative energy coefficient. - """ - return 1 class SpectralModel(BasedPhaseFractureMaterial): def stress_value(self, bc) -> TensorLike: @@ -391,14 +350,6 @@ def positive_stress_func(self, guh) -> TensorLike: The flattened stress tensor. """ pass - - def positive_coef(self, bc) -> TensorLike: - """ - @brief Compute the positive energy coefficient. - """ - d = self.d - gd = self._gd.degradation_function(d(bc)) - return gd def negative_stress_func(self, guh) -> TensorLike: """ @@ -411,13 +362,7 @@ def negative_stress_func(self, guh) -> TensorLike: TensorLike The flattened stress tensor. """ - pass - - def negative_coef(self, bc) -> TensorLike: - """ - @brief Compute the negative energy coefficient. - """ - return 1 + pass class HybridModel(BasedPhaseFractureMaterial): def __init__(self, material, energy_degradation_fun): @@ -458,20 +403,6 @@ def positive_stress_func(self, guh) -> TensorLike: The flattened stress tensor. """ return self._isotropic_model.positive_stress_func(guh) - - def positive_coef(self, bc) -> TensorLike: - """ - @brief Compute the positive energy coefficient. - """ - d = self.d - gd = self._gd.degradation_function(d(bc)) - return gd - - def negative_coef(self, bc) -> TensorLike: - """ - @brief Compute the negative energy coefficient. - """ - return 1 def negative_stress_func(self, guh) -> TensorLike: """ diff --git a/app/fracturex/fracturex/tests/test_fracture_constitutive_model.py b/app/fracturex/fracturex/tests/test_fracture_constitutive_model.py index 97dd4a347..c868332f5 100644 --- a/app/fracturex/fracturex/tests/test_fracture_constitutive_model.py +++ b/app/fracturex/fracturex/tests/test_fracture_constitutive_model.py @@ -8,7 +8,7 @@ from app.fracturex.fracturex.phasefield.energy_degradation_function import EnergyDegradationFunction as EDFunc from app.fracturex.fracturex.phasefield.phase_fracture_material import PhaseFractureMaterialFactory -from app.fracturex.fracturex.phasefield.main_solver import MainSolver +from app.fracturex.fracturex.phasefield.main_solve import MainSolve class TestfracutreConstitutiveModel: diff --git a/app/fracturex/fracturex/tests/test_main_solver.py b/app/fracturex/fracturex/tests/test_main_solver.py index 61679e5fa..f5b609e83 100644 --- a/app/fracturex/fracturex/tests/test_main_solver.py +++ b/app/fracturex/fracturex/tests/test_main_solver.py @@ -6,7 +6,7 @@ from fealpy.backend import backend_manager as bm from fealpy.mesh import TriangleMesh -from app.fracturex.fracturex.phasefield.main_solver import MainSolver +from app.fracturex.fracturex.phasefield.main_solve import MainSolve class TestMainSolver: From a9265fcf35b94e1adcd9c64a9fbdfb19c8155b37 Mon Sep 17 00:00:00 2001 From: wpx <1143615697@qq.com> Date: Fri, 22 Nov 2024 17:04:14 +0800 Subject: [PATCH 21/63] update --- app/tssim/ocp_opt/jovan/ocp_opt.py | 169 ---------- app/tssim/ocp_opt/jovan/ocp_opt_pde.py | 166 ---------- app/tssim/ocp_opt/ocp_opt.py | 236 ++++++++------ app/tssim/ocp_opt/ocp_opt_pde.py | 130 +++----- app/tssim/ocp_opt/solver.py | 308 ------------------ .../ocp_opt/{jovan => }/solver_update.py | 8 +- 6 files changed, 186 insertions(+), 831 deletions(-) delete mode 100644 app/tssim/ocp_opt/jovan/ocp_opt.py delete mode 100644 app/tssim/ocp_opt/jovan/ocp_opt_pde.py delete mode 100644 app/tssim/ocp_opt/solver.py rename app/tssim/ocp_opt/{jovan => }/solver_update.py (96%) diff --git a/app/tssim/ocp_opt/jovan/ocp_opt.py b/app/tssim/ocp_opt/jovan/ocp_opt.py deleted file mode 100644 index 20f9c1560..000000000 --- a/app/tssim/ocp_opt/jovan/ocp_opt.py +++ /dev/null @@ -1,169 +0,0 @@ -from fealpy.mesh import TriangleMesh -from fealpy.backend import backend_manager as bm -from fealpy.functionspace import LagrangeFESpace -from fealpy.old.timeintegratoralg import UniformTimeLine -from fealpy.functionspace import TensorFunctionSpace -from ocp_opt_pde import example_1 -from solver_update import ocp_opt_solver -from fealpy.fem import DirichletBC - -from functools import partial -from fealpy import logger -from fealpy.solver import spsolve -logger.setLevel('ERROR') #积分子问题 - -bm.set_backend("numpy") -pde = example_1() -n = 20 -q = 3 -T = 1 -nt = 100 -maxit = 10 - -mesh = TriangleMesh.from_box(pde.domain(), nx=n, ny=n) -timeline = UniformTimeLine(0, T, nt) -dt = timeline.dt - -yspace= LagrangeFESpace(mesh, p=1) -space = LagrangeFESpace(mesh, p=1) -pspace = TensorFunctionSpace(space, (2,-1)) -solver = ocp_opt_solver(mesh, yspace, pspace, pde, timeline, q=q) - -ygdof = yspace.number_of_global_dofs() -pgdof = pspace.number_of_global_dofs() - -ally = [None]*(nt+1) -allp = [None]*(nt+1) -allu = [None]*(nt+1) -allz = [None]*(nt+1) - -y0 = yspace.function(yspace.interpolate(partial(pde.y_solution, time=0))) -y0t = yspace.interpolate(partial(pde.y_t_solution, time=0)) -p0 = pspace.function(pspace.interpolate(partial(pde.p_solution, time=0))) -zn = yspace.function(yspace.interpolate(partial(pde.z_solution, time=T))) - -q2 = pspace.function() - -ally[0] = y0 -allp[0] = p0 -allz[-1] = zn - -A0 = solver.Forward_BForm_A0().assembly() -b0_LForm = solver.Forward_LForm_b0() - -FA = solver.Forward_BForm_A().assembly() -Forward_b_LForm = solver.Forward_LForm_b() - -An = solver.Forward_BForm_A0().assembly() -bn_LForm = solver.Backward_LForm_bn() - -BA = solver.Forward_BForm_A().assembly() -Backward_b_LForm = solver.Backward_LForm_b() - -for k in range(maxit): - y1 = yspace.function() - p1 = pspace.function() - - ## 正向求解第0步 - solver.Forward_0_update(allu[1]) - Fb0 = b0_LForm.assembly() - BC = DirichletBC(space=(yspace,pspace), threshold=(None, None), - gd=(partial(pde.y_solution,time=dt), - partial(pde.p_solution,time=dt)),method='interp') - A0, b0 = BC.apply(A0, Fb0) - x0 = spsolve(A0, b0, solver='mumps') - - y1[:] = x0[:ygdof] - p1[:] = x0[-pgdof:] - ally[1] = y1 - allp[1] = p1 - - timeline.advance() - - # 正向求解 - for i in bm.arange(2, nt+1): - t1 = timeline.current_time_level() - t1index = timeline.current_time_level_index() - - y2 = yspace.function() - p2 = pspace.function() - - solver.Forward_update(ally[t1index-1], ally[t1index], allu[t1index+1], t1+dt) - Fb = Forward_b_LForm.assembly() - - BC = DirichletBC(space=(yspace,pspace), threshold=(None, None), - gd=(partial(pde.y_solution,time=t1+dt), - partial(pde.p_solution,time=t1+dt)),method='interp') - Forward_A, Forward_b = BC.apply(FA, Fb) - Forward_x = spsolve(Forward_A, Forward_b, solver='mumps') - - y2[:] = Forward_x[:ygdof] - p2[:] = Forward_x[-pgdof:] - ally[t1index+1] = y2 - allp[t1index+1] = p2 - timeline.advance() - - ## 反向求解第0步 - t1 = timeline.current_time_level() - t1index = timeline.current_time_level_index() - - zn1 = yspace.function() - solver.Backward_n_update(ally[t1index-1], allp[t1index-1]) - bn = bn_LForm.assembly() - - BC = DirichletBC(space=(yspace,pspace), threshold=(None, None), - gd=(partial(pde.z_solution,time = T-dt), - partial(pde.q_solution,time = T-dt)),method='interp') - An, bn = BC.apply(An, bn) - xn = spsolve(An, bn, solver='mumps') - zn1[:] = xn[:ygdof] - q2[:] = xn[-pgdof:] - allz[t1index-1] = zn1 - - timeline.backward() - - ## 反向求解 - for i in bm.arange(nt-1, 0, -1): - t1 = timeline.current_time_level() - t1index = timeline.current_time_level_index() - zn2 = yspace.function() - - ##求第i-1步的z,q - solver.Backward_update(allz[t1index+1], allz[t1index], ally[t1index-1], allp[t1index-1], t1-dt) - Bb = Backward_b_LForm.assembly() - BC = DirichletBC(space=(yspace,pspace), threshold=(None, None), - gd=(partial(pde.z_solution,time = t1-dt), - partial(pde.q_solution,time = t1-dt)),method='interp') - Backward_A, Backward_b = BC.apply(BA, Bb) - Backward_x = spsolve(Backward_A, Backward_b, solver='mumps') - - zn2[:] = Backward_x[:ygdof] - q2 = Backward_x[-pgdof:] - allz[t1index-1] = zn2 - timeline.backward() - - z_bar = solver.solve_z_bar(allz) - un_pre = allu[0] - if un_pre == None: - un_pre = 0 - for i in range(nt+1): - ufunction = yspace.function() - ufunction[:] = bm.max((0,z_bar)) - allz[i] - allu[i] = ufunction - print(f"第{k}次的前后u差别",bm.sum(allu[0] - un_pre)) - -ysolution = yspace.function(yspace.interpolate(partial(pde.y_solution, time=T))) -psolution = pspace.function(pspace.interpolate(partial(pde.p_solution, time=T))) -usolution = yspace.function(yspace.interpolate(partial(pde.u_solution, time=0))) -zsolution = yspace.function(yspace.interpolate(partial(pde.z_solution, time=0))) -qsolution = pspace.function(pspace.interpolate(partial(pde.q_solution, time=0))) -errory = mesh.error(ally[-1], ysolution) -errorp = mesh.error(allp[-1], psolution) -erroru = mesh.error(allu[0], usolution) -errorz = mesh.error(allz[0], zsolution) -errorq = mesh.error(q2, qsolution) -print("y误差",errory) -print("p误差",errorp) -print("u误差",erroru) -print("z误差",errorz) -print("q误差",errorq) diff --git a/app/tssim/ocp_opt/jovan/ocp_opt_pde.py b/app/tssim/ocp_opt/jovan/ocp_opt_pde.py deleted file mode 100644 index c52347168..000000000 --- a/app/tssim/ocp_opt/jovan/ocp_opt_pde.py +++ /dev/null @@ -1,166 +0,0 @@ -#!/usr/bin/python3 -from fealpy.backend import backend_manager as bm -from fealpy.decorator import cartesian -import sympy as sp - -class example_1: - def __init__(self, c=1): - self.c = c - self.manager, = bm._backends - - t, x1, x2 = sp.symbols('t, x1, x2', real=True) - self.x1 = x1 - self.x2 = x2 - self.t = t - - self.y = (1-x1)*(1-x2)*x1*x2*sp.exp(t) - self.z = (1-x1)*(1-x2)*x1*x2*(t-1)**3 - self.u = -(1-x1)*(1-x2)*x1*x2*(t-1)**3 - - self.p0 = -(1+x1**2)*(1-x2)*x2*(1-2*x1)*sp.exp(t) - self.p1= -(1+x2**2)*(1-x1)*x1*(1-2*x2)*sp.exp(t) - self.p = sp.Matrix([self.p0, self.p1]) - - self.q0 = -sp.pi*sp.cos(sp.pi*x1)*sp.sin(sp.pi*x2)*(t-1)**2 - self.q1 = -sp.pi*sp.sin(sp.pi*x1)*sp.cos(sp.pi*x2)*(t-1)**2 - self.q = sp.Matrix([self.q0, self.q1]) - - self.A00 = 1+x1**2 - self.A11 = 1+x2**2 - self.A = sp.Matrix([[self.A00, 0], [0, self.A11]]) - - def domain(self): - return [0, 1, 0, 1] - - @cartesian - def y_solution(self, space, time): - x1 = self.x1 - x2 = self.x2 - t = self.t - result = sp.lambdify([x1,x2,t], self.y, self.manager) - return result(space[...,0], space[...,1], time) - - @cartesian - def y_t_solution(self, space, time): - x1 = self.x1 - x2 = self.x2 - t = self.t - result = sp.lambdify([x1,x2,t], sp.diff(self.y, t), self.manager) - return result(space[...,0], space[...,1], time) - - @cartesian - def z_solution(self, space, time): - x1 = self.x1 - x2 = self.x2 - t = self.t - result = sp.lambdify([x1,x2,t], self.z, self.manager) - return result(space[...,0], space[...,1], time) - - @cartesian - def z_t_solution(self, space, time): - x1 = self.x1 - x2 = self.x2 - t = self.t - result = sp.lambdify([x1,x2,t], sp.diff(self.z, t), self.manager) - return result(space[...,0], space[...,1], time) - - @cartesian - def u_solution(self, space, time): - x1 = self.x1 - x2 = self.x2 - t = self.t - result = sp.lambdify([x1,x2,t], self.u, self.manager) - return result(space[...,0], space[...,1], time) - - @cartesian - def p_solution(self, space, time): - ''' - val1 = -(1+x**2)*(1-y)*y*(1-2*x)*bm.exp(time) - val2 = -(1+y**2)*(1-x)*x*(1-2*y)*bm.exp(time) - result = bm.stack([val1, val2], axis=-1) - ''' - x1 = self.x1 - x2 = self.x2 - t = self.t - - x = space[..., 0] - y = space[..., 1] - result = bm.zeros_like(space) - p0 = sp.lambdify([x1,x2,t], self.p0, self.manager) - p1 = sp.lambdify([x1,x2,t], self.p1, self.manager) - result[...,0] = p0(x, y, time) - result[...,1] = p1(x, y, time) - return result - - @cartesian - def q_solution(self, space, time): - x1 = self.x1 - x2 = self.x2 - t = self.t - val = sp.Matrix([self.q0, self.q1]) - result = sp.lambdify([x1,x2,t], val, self.manager) - output = result(space[..., 0], space[..., 1], time) - reshape_output = output.transpose(2, 0, 1).squeeze() - return reshape_output - - - @cartesian - def A_matirx(self, space): - x = space[..., 0] - y = space[..., 1] - result = bm.zeros(space.shape[:-1]+(2,2)) - result[..., 0, 0] = 1+x**2 - result[..., 1, 1] = 1+y**2 - return result - - @cartesian - def A_inverse(self, space): - x = space[..., 0] - y = space[..., 1] - result = bm.zeros(space.shape[:-1]+(2,2)) - result[..., 0, 0] = 1/(1+x**2) - result[..., 1, 1] = 1/(1+y**2) - return result - - @cartesian - def f_fun(self, space, time, index=None): - y = self.y - p = self.p - u = self.u - t = self.t - x1 = self.x1 - x2 = self.x2 - self.f = sp.diff(y, t, 2) + sp.diff(p[0], x1) + sp.diff(p[1], x2) + y - u - result = sp.lambdify([x1,x2,t], self.f, self.manager) - return result(space[...,0], space[...,1], time) - - @cartesian - def p_d_fun(self, space, time): - p = self.p - q = self.q - z = self.z - A = self.A - t = self.t - x1 = self.x1 - x2 = self.x2 - grad_z = sp.Matrix([sp.diff(z, x1), sp.diff(z, x2)]) - A_inv = A.inv() - p_d = p + A_inv*q + grad_z - self.p_d = p_d - result = sp.lambdify([x1,x2,t], self.p_d, self.manager) - output = result(space[..., 0], space[..., 1], time) # 原始形状为 (2, 1, 200, 10) - output = output.transpose(2, 3, 0, 1).squeeze() # 变为 (200, 10, 2) - return output - - @cartesian - def y_d_fun(self, space, time): - q = self.q - y = self.y - z = self.z - t = self.t - x1 = self.x1 - x2 = self.x2 - #self.y_d = y - sp.diff(z, t, 2) + sp.diff(q[0], x1) + sp.diff(q[1], x2) - self.y_d = y - sp.diff(z, t, 2) - sp.diff(q[0], x1) - sp.diff(q[1], x2) - z - result = sp.lambdify([x1,x2,t], self.y_d, self.manager) - return result(space[...,0], space[...,1], time) diff --git a/app/tssim/ocp_opt/ocp_opt.py b/app/tssim/ocp_opt/ocp_opt.py index e6654f126..a3a6243e9 100644 --- a/app/tssim/ocp_opt/ocp_opt.py +++ b/app/tssim/ocp_opt/ocp_opt.py @@ -1,149 +1,169 @@ -#!/usr/bin/python3 -'''! - @Author: wpx - @File Name: ocp-opt.py - @Mail: wpx15673207315@gmail.com - @Created Time: Thu 05 Sep 2024 04:50:58 PM CST - @bref - @ref -''' from fealpy.mesh import TriangleMesh from fealpy.backend import backend_manager as bm from fealpy.functionspace import LagrangeFESpace -from fealpy.timeintegratoralg import UniformTimeLine - +from fealpy.old.timeintegratoralg import UniformTimeLine +from fealpy.functionspace import TensorFunctionSpace from ocp_opt_pde import example_1 -from solver import ocp_opt_solver +from solver_update import ocp_opt_solver +from fealpy.fem import DirichletBC -from scipy.sparse import coo_array, bmat from functools import partial from fealpy import logger +from fealpy.solver import spsolve logger.setLevel('ERROR') #积分子问题 bm.set_backend("numpy") pde = example_1() -n = 10 -q = 4 +n = 160 +q = 3 T = 1 -nt = 30 -maxit = 3 +nt = 500 +maxit = 12 mesh = TriangleMesh.from_box(pde.domain(), nx=n, ny=n) timeline = UniformTimeLine(0, T, nt) dt = timeline.dt yspace= LagrangeFESpace(mesh, p=1) -pspace= LagrangeFESpace(mesh, p=1) -solver = ocp_opt_solver(mesh, yspace, pspace, pde, timeline) +space = LagrangeFESpace(mesh, p=1) +pspace = TensorFunctionSpace(space, (2,-1)) +solver = ocp_opt_solver(mesh, yspace, pspace, pde, timeline, q=q) -ygodf = yspace.number_of_global_dofs() +ygdof = yspace.number_of_global_dofs() pgdof = pspace.number_of_global_dofs() -yisbdof = yspace.is_boundary_dof() -pisbdof = pspace.is_boundary_dof() -isbdof = bm.concatenate([yisbdof, pisbdof, pisbdof], axis=0) ally = [None]*(nt+1) -allpx1 = [None]*(nt+1) -allpx2 = [None]*(nt+1) +allp = [None]*(nt+1) allu = [None]*(nt+1) +allz = [None]*(nt+1) y0 = yspace.function(yspace.interpolate(partial(pde.y_solution, time=0))) y0t = yspace.interpolate(partial(pde.y_t_solution, time=0)) -p0x1 = pspace.function(pspace.interpolate(partial(pde.px1_solution, time=0))) -p0x2 = pspace.function(pspace.interpolate(partial(pde.px2_solution, time=0))) +p0 = pspace.function(pspace.interpolate(partial(pde.p_solution, time=0))) +zn = yspace.function(yspace.interpolate(partial(pde.z_solution, time=T))) + +q2 = pspace.function() + ally[0] = y0 -allpx1[0] = p0x1 -allpx2[0] = p0x2 +allp[0] = p0 +allz[-1] = zn +allu[0] = yspace.function() -for k in range(maxit): - A0 = solver.A0n() - b0 = solver.forward_b0(allu[1]) - A0, b0 = solver.forward_boundary(A0, b0, isbdof, dt) - x0 = solver.mumps_solve(A0, b0) +A0 = solver.Forward_BForm_A0().assembly() +b0_LForm = solver.Forward_LForm_b0() + +FA = solver.Forward_BForm_A().assembly() +Forward_b_LForm = solver.Forward_LForm_b() +An = solver.Forward_BForm_A0().assembly() +bn_LForm = solver.Backward_LForm_bn() + +BA = solver.Forward_BForm_A().assembly() +Backward_b_LForm = solver.Backward_LForm_b() + +for k in range(maxit): y1 = yspace.function() - p1x1 = pspace.function() - p1x2 = pspace.function() - y1[:] = x0[:ygodf] - p1x1[:] = x0[ygodf:-pgdof] - p1x2[:] = x0[-pgdof:] + p1 = pspace.function() + + ## 正向求解第0步 + solver.Forward_0_update(allu[1]) + Fb0 = b0_LForm.assembly() + BC = DirichletBC(space=(yspace,pspace), threshold=(None, None), + gd=(partial(pde.y_solution,time=dt), + partial(pde.p_solution,time=dt)),method='interp') + A0, b0 = BC.apply(A0, Fb0) + x0 = spsolve(A0, b0, solver='mumps') + + y1[:] = x0[:ygdof] + p1[:] = x0[-pgdof:] ally[1] = y1 - allpx1[1] = p1x1 - allpx2[1] = p1x2 + allp[1] = p1 + timeline.advance() - AA = solver.A() - #正向求解 - for i in range(nt-1): - t1 = timeline.next_time_level() - tnextindex = timeline.current_time_level_index()+1 - + # 正向求解 + for i in bm.arange(1, nt): + t1 = timeline.current_time_level() + t1index = timeline.current_time_level_index() + y2 = yspace.function() - px1 = pspace.function() - px2 = pspace.function() - b = solver.forward_b(y0, y1, allu[tnextindex], t1) - A,b = solver.forward_boundary(AA, b, isbdof, t1) + p2 = pspace.function() - x = solver.mumps_solve(A, b) - y1[:] = y0 - y2[:] = x[:ygodf] - px1[:] = x[ygodf:-pgdof] - px2[:] = x[-pgdof:] - ally[tnextindex] = y2 - allpx1[tnextindex] = px1 - allpx2[tnextindex] = px2 + solver.Forward_update(ally[t1index-1], ally[t1index], allu[t1index+1], t1+dt) + Fb = Forward_b_LForm.assembly() + + BC = DirichletBC(space=(yspace,pspace), threshold=(None, None), + gd=(partial(pde.y_solution,time=t1+dt), + partial(pde.p_solution,time=t1+dt)),method='interp') + Forward_A, Forward_b = BC.apply(FA, Fb) + Forward_x = spsolve(Forward_A, Forward_b, solver='mumps') + + y2[:] = Forward_x[:ygdof] + p2[:] = Forward_x[-pgdof:] + ally[t1index+1] = y2 + allp[t1index+1] = p2 timeline.advance() + + ## 反向求解第0步 + t1 = timeline.current_time_level() + t1index = timeline.current_time_level_index() - - zn0 = yspace.function(yspace.interpolate(partial(pde.z_solution, time=T))) - zn0t = yspace.interpolate(partial(pde.z_t_solution, time=0)) zn1 = yspace.function() - zn2 = yspace.function() - qx1 = pspace.function() - qx2 = pspace.function() - un0 = yspace.function() - un1 = yspace.function() - - un0[:] = solver.solve_u(zn0) #积分子 - allu[tnextindex] = un0 - - An = solver.A0n() - bn = solver.backward_b0(ally[-1], allpx1[-1], allpx2[-1]) - An, bn = solver.backward_boundary(An, bn, isbdof, T-dt) - xn = solver.mumps_solve(An, bn) - - zn1[:] = xn[:ygodf] - qx1[:] = xn[ygodf:-pgdof] - qx2[:] = xn[-pgdof:] - timeline.backward() - - tnextindex = timeline.current_time_level_index() - un1[:] = solver.solve_u(zn1) - allu[tnextindex] = un1 + solver.Backward_n_update(ally[t1index-1], allp[t1index-1]) + bn = bn_LForm.assembly() + + BC = DirichletBC(space=(yspace,pspace), threshold=(None, None), + gd=(partial(pde.z_solution,time = T-dt), + partial(pde.q_solution,time = T-dt)),method='interp') + An, bn = BC.apply(An, bn) + xn = spsolve(An, bn, solver='mumps') + zn1[:] = xn[:ygdof] + q2[:] = xn[-pgdof:] + allz[t1index-1] = zn1 - # 反向求解 - for i in range(nt-1): - t1 = timeline.prev_time_level() - tnextindex = timeline.current_time_level_index()-1 - u = yspace.function() - - b = solver.backward_b(zn0, zn1, ally[tnextindex], allpx1[tnextindex], allpx2[tnextindex], t1) - A,b = solver.forward_boundary(AA, b, isbdof, t1) - - x = solver.mumps_solve(A, b) + timeline.backward() + + ## 反向求解 + for i in bm.arange(nt-1, 0, -1): + t1 = timeline.current_time_level() + t1index = timeline.current_time_level_index() + zn2 = yspace.function() + + ##求第i-1步的z,q + solver.Backward_update(allz[t1index+1], allz[t1index], ally[t1index-1], allp[t1index-1], t1-dt) + Bb = Backward_b_LForm.assembly() + BC = DirichletBC(space=(yspace,pspace), threshold=(None, None), + gd=(partial(pde.z_solution,time = t1-dt), + partial(pde.q_solution,time = t1-dt)),method='interp') + Backward_A, Backward_b = BC.apply(BA, Bb) + Backward_x = spsolve(Backward_A, Backward_b, solver='mumps') - zn1[:] = zn0 - zn2[:] = x[:ygodf] - qx1[:] = x[ygodf:-pgdof] - qx2[:] = x[-pgdof:] - u[:] = solver.solve_u(zn2) - allu[tnextindex] = u + zn2[:] = Backward_x[:ygdof] + q2[:] = Backward_x[-pgdof:] + allz[t1index-1] = zn2 timeline.backward() - - -ysolution = yspace.function(yspace.interpolate(partial(pde.y_solution, time=T))) -usolution = yspace.function(yspace.interpolate(partial(pde.u_solution, time=0))) -errory = mesh.error(ally[-1], ysolution) -erroru = mesh.error(allu[0], usolution) -print(errory) -print(erroru) + + z_bar = solver.solve_z_bar(allz) + un_pre = allu[0] + for i in range(nt+1): + ufunction = yspace.function() + ufunction[:] = bm.max((0,z_bar)) - allz[i] + allu[i] = ufunction + print(f"第{k}次的前后u差别",mesh.error(un_pre,allu[0])) + +print(q2) +ysolution = yspace.function(yspace.interpolate(partial(pde.y_solution, time=T-dt))) +psolution = pspace.function(pspace.interpolate(partial(pde.p_solution, time=T-dt))) +usolution = yspace.function(yspace.interpolate(partial(pde.u_solution, time=dt))) +zsolution = yspace.function(yspace.interpolate(partial(pde.z_solution, time=dt))) +qsolution = pspace.function(pspace.interpolate(partial(pde.q_solution, time=0))) +errory = mesh.error(ally[-2], ysolution) +errorp = mesh.error(allp[-2], psolution) +erroru = mesh.error(allu[1], usolution) +errorz = mesh.error(allz[1], zsolution) +errorq = mesh.error(q2, qsolution) +print("y误差",errory) +print("p误差",errorp) +print("u误差",erroru) +print("z误差",errorz) +print("q误差",errorq) diff --git a/app/tssim/ocp_opt/ocp_opt_pde.py b/app/tssim/ocp_opt/ocp_opt_pde.py index bbe2ab1bb..23e19b5d1 100644 --- a/app/tssim/ocp_opt/ocp_opt_pde.py +++ b/app/tssim/ocp_opt/ocp_opt_pde.py @@ -1,33 +1,30 @@ #!/usr/bin/python3 -'''! - @Author: wpx - @File Name: ocp_opt_pde.py - @Mail: wpx15673207315@gmail.com - @Created Time: Thu 05 Sep 2024 04:57:54 PM CST - @bref - @ref -''' from fealpy.backend import backend_manager as bm from fealpy.decorator import cartesian import sympy as sp class example_1: - def __init__(self): + def __init__(self, c=1): + self.c = c + self.manager, = bm._backends + t, x1, x2 = sp.symbols('t, x1, x2', real=True) self.x1 = x1 self.x2 = x2 self.t = t - self.c = 1 - self.y = (1-x1)*(1-x2)*x1*x2*x1*x2*sp.exp(t) - self.z = (1-x1)*(1-x2)*x1*x2*x1*x2*(t-1)**3 - self.u = -(1-x1)*(1-x2)*x1*x2*x1*x2*(t-1)**3 - p0 = -(1+x1**2)*(1-x2)*x2*(1-2*x1)*sp.exp(t) - p1 = -(1+x2**2)*(1-x1)*x1*(1-2*x2)*sp.exp(t) - self.p = sp.Matrix([p0, p1]) + self.y = (1-x1)*(1-x2)*x1*x2*sp.exp(t) + self.z = (1-x1)*(1-x2)*x1*x2*(t-1)**3 + self.u = -(1-x1)*(1-x2)*x1*x2*(t-1)**3 + + self.p0 = -(1+x1**2)*(1-x2)*x2*(1-2*x1)*sp.exp(t) + self.p1= -(1+x2**2)*(1-x1)*x1*(1-2*x2)*sp.exp(t) + self.p = sp.Matrix([self.p0, self.p1]) + self.q0 = -sp.pi*sp.cos(sp.pi*x1)*sp.sin(sp.pi*x2)*(t-1)**2 self.q1 = -sp.pi*sp.sin(sp.pi*x1)*sp.cos(sp.pi*x2)*(t-1)**2 self.q = sp.Matrix([self.q0, self.q1]) + self.A00 = 1+x1**2 self.A11 = 1+x2**2 self.A = sp.Matrix([[self.A00, 0], [0, self.A11]]) @@ -37,80 +34,74 @@ def domain(self): @cartesian def y_solution(self, space, time): - manager, = bm._backends x1 = self.x1 x2 = self.x2 t = self.t - result = sp.lambdify([x1,x2,t], self.y, manager) + result = sp.lambdify([x1,x2,t], self.y, self.manager) return result(space[...,0], space[...,1], time) @cartesian def y_t_solution(self, space, time): - manager, = bm._backends x1 = self.x1 x2 = self.x2 t = self.t - result = sp.lambdify([x1,x2,t], sp.diff(self.y, t), manager) + result = sp.lambdify([x1,x2,t], sp.diff(self.y, t), self.manager) return result(space[...,0], space[...,1], time) @cartesian def z_solution(self, space, time): - manager, = bm._backends x1 = self.x1 x2 = self.x2 t = self.t - result = sp.lambdify([x1,x2,t], self.z, manager) + result = sp.lambdify([x1,x2,t], self.z, self.manager) return result(space[...,0], space[...,1], time) @cartesian def z_t_solution(self, space, time): - manager, = bm._backends x1 = self.x1 x2 = self.x2 t = self.t - result = sp.lambdify([x1,x2,t], sp.diff(self.z, t), manager) + result = sp.lambdify([x1,x2,t], sp.diff(self.z, t), self.manager) return result(space[...,0], space[...,1], time) @cartesian def u_solution(self, space, time): - manager, = bm._backends x1 = self.x1 x2 = self.x2 t = self.t - result = sp.lambdify([x1,x2,t], self.u, manager) + result = sp.lambdify([x1,x2,t], self.u, self.manager) return result(space[...,0], space[...,1], time) @cartesian - def px1_solution(self, space, time): - x = space[..., 0] - y = space[..., 1] - result = -(1+x**2)*(1-y)*y*(1-2*x)*bm.exp(time) - return result - - @cartesian - def px2_solution(self, space, time): - x = space[..., 0] - y = space[..., 1] - result= -(1+y**2)*(1-x)*x*(1-2*y)*bm.exp(time) - return result - - @cartesian - def qx1_solution(self, space, time): - manager, = bm._backends + def p_solution(self, space, time): + ''' + val1 = -(1+x**2)*(1-y)*y*(1-2*x)*bm.exp(time) + val2 = -(1+y**2)*(1-x)*x*(1-2*y)*bm.exp(time) + result = bm.stack([val1, val2], axis=-1) + ''' x1 = self.x1 x2 = self.x2 t = self.t - result = sp.lambdify([x1,x2,t], self.q0, manager) - return result(space[...,0], space[...,1], time) + + x = space[..., 0] + y = space[..., 1] + result = bm.zeros_like(space) + p0 = sp.lambdify([x1,x2,t], self.p0, self.manager) + p1 = sp.lambdify([x1,x2,t], self.p1, self.manager) + result[...,0] = p0(x, y, time) + result[...,1] = p1(x, y, time) + return result @cartesian - def qx2_solution(self, space, time): - manager, = bm._backends + def q_solution(self, space, time): x1 = self.x1 x2 = self.x2 t = self.t - result = sp.lambdify([x1,x2,t], self.q1, manager) - return result(space[...,0], space[...,1], time) + result = sp.lambdify([x1,x2,t], self.q, self.manager) + output = result(space[..., 0], space[..., 1], time) + reshape_output = output.transpose(2, 0, 1).squeeze() + return reshape_output + @cartesian def A_matirx(self, space): @@ -129,23 +120,21 @@ def A_inverse(self, space): result[..., 0, 0] = 1/(1+x**2) result[..., 1, 1] = 1/(1+y**2) return result - + @cartesian def f_fun(self, space, time, index=None): - manager, = bm._backends y = self.y p = self.p u = self.u t = self.t x1 = self.x1 x2 = self.x2 - self.f = sp.diff(y, t, 2) + sp.diff(p[0], x1) + sp.diff(p[1], x2) + y - u - result = sp.lambdify([x1,x2,t], self.f, manager) + self.f = sp.diff(y, t, 2) + sp.diff(p[0], x1) + sp.diff(p[1], x2) + self.c*y - u + result = sp.lambdify([x1,x2,t], self.f, self.manager) return result(space[...,0], space[...,1], time) @cartesian - def p_dx1_fun(self, space, time): - manager, = bm._backends + def p_d_fun(self, space, time): p = self.p q = self.q z = self.z @@ -154,36 +143,23 @@ def p_dx1_fun(self, space, time): x1 = self.x1 x2 = self.x2 grad_z = sp.Matrix([sp.diff(z, x1), sp.diff(z, x2)]) - p_d = p + A**(-1)*q + grad_z - self.p_dx1 = p_d[0] - result = sp.lambdify([x1,x2,t], self.p_dx1, manager) - return result(space[...,0], space[...,1], time) - - @cartesian - def p_dx2_fun(self, space, time): - manager, = bm._backends - p = self.p - q = self.q - z = self.z - A = self.A - t = self.t - x1 = self.x1 - x2 = self.x2 - grad_z = sp.Matrix([sp.diff(z, x1), sp.diff(z, x2)]) - p_d = p + A**(-1)*q + grad_z - self.p_dx2 = p_d[1] - result = sp.lambdify([x1,x2,t], self.p_dx2, manager) - return result(space[...,0], space[...,1], time) + A_inv = A.inv() + p_d = p + A_inv*q + grad_z + self.p_d = p_d + result = sp.lambdify([x1,x2,t], self.p_d, self.manager) + output = result(space[..., 0], space[..., 1], time) # 原始形状为 (2, 1, 200, 10) + output = output.transpose(2, 3, 0, 1).squeeze() # 变为 (200, 10, 2) + return output @cartesian def y_d_fun(self, space, time): - manager, = bm._backends q = self.q y = self.y z = self.z t = self.t x1 = self.x1 x2 = self.x2 - self.y_d = y - sp.diff(z, t, 2) + sp.diff(q[0], x1) + sp.diff(q[1], x2) - result = sp.lambdify([x1,x2,t], self.y_d, manager) + #self.y_d = y - sp.diff(z, t, 2) + sp.diff(q[0], x1) + sp.diff(q[1], x2) + self.y_d = y - sp.diff(z, t, 2) - sp.diff(q[0], x1) - sp.diff(q[1], x2) - self.c*z + result = sp.lambdify([x1,x2,t], self.y_d, self.manager) return result(space[...,0], space[...,1], time) diff --git a/app/tssim/ocp_opt/solver.py b/app/tssim/ocp_opt/solver.py deleted file mode 100644 index 997cbef0a..000000000 --- a/app/tssim/ocp_opt/solver.py +++ /dev/null @@ -1,308 +0,0 @@ -#!/usr/bin/python3 -'''! - @Author: wpx - @File Name: solver.py - @Mail: wpx15673207315@gmail.com - @Created Time: Tue 10 Sep 2024 03:32:33 PM CST - @bref - @ref -''' - -from fealpy.fem import BilinearForm, ScalarMassIntegrator -from fealpy.fem import PressWorkIntegrator, PressWorkIntegrator1 -from fealpy.fem import LinearForm, ScalarSourceIntegrator -from fealpy.decorator import barycentric, cartesian -from fealpy.backend import backend_manager as bm -from fealpy.sparse import COOTensor -from functools import partial - -from ocp_opt_pde import example_1 - -class ocp_opt_solver(): - def __init__(self, mesh, yspace, pspace, pde, timeline,q=4): - self.mesh = mesh - self.yspace = yspace - self.pspace = pspace - self.pde = pde - self.q = q - self.dt = timeline.dt - self.timeline = timeline - - @cartesian - def coef_M00(p, index = None): - return pde.A_inverse(p)[...,0,0] - bform = BilinearForm(pspace) - bform.add_integrator(ScalarMassIntegrator(coef=coef_M00, q=q)) - self.M00 = bform.assembly() - - @cartesian - def coef_M01(p, index = None): - return pde.A_inverse(p)[...,0,1] - bform = BilinearForm(pspace) - bform.add_integrator(ScalarMassIntegrator(coef=coef_M01, q=q)) - self.M01 = bform.assembly() - - @cartesian - def coef_M10(p, index = None): - return pde.A_inverse(p)[...,1,0] - bform = BilinearForm(pspace) - bform.add_integrator(ScalarMassIntegrator(coef=coef_M10, q=q)) - self.M10 = bform.assembly() - - @cartesian - def coef_M11(p, index = None): - return pde.A_inverse(p)[...,1,1] - bform = BilinearForm(pspace) - bform.add_integrator(ScalarMassIntegrator(coef=coef_M11, q=q)) - self.M11 = bform.assembly() - - bform = BilinearForm(pspace) - bform.add_integrator(ScalarMassIntegrator(q=q)) - self.M = bform.assembly() - - bform = BilinearForm((pspace, pspace)) - bform.add_integrator(PressWorkIntegrator(q=q)) - self.S1 = bform.assembly() - - bform = BilinearForm((pspace, pspace)) - bform.add_integrator(PressWorkIntegrator1(q=q)) - self.S2 = bform.assembly() - - def A0n(self): - dt = self.dt - A0 = COOTensor.concat(((2/dt**2 + self.pde.c)*self.M, -self.S1, -self.S2), axis=0) - A1 = COOTensor.concat((self.S1.T, self.M00, self.M01), axis=0) - A2 = COOTensor.concat((self.S2.T, self.M10, self.M11), axis=0) - A = COOTensor.concat((A0,A1,A2), axis=1) - return A - - def forward_b0(self, u1): - if u1 == None: - u1 = self.yspace.function() - - @cartesian - def fun(p, index=None): - result = self.pde.y_solution(p, 0) - result += self.dt * self.pde.y_t_solution(p, 0) - result *= 2/self.dt**2 - return result - @cartesian - def coef(p, index=None): - result = self.pde.f_fun(p, time=self.dt, index=index) - return result - lform = LinearForm(self.yspace) - lform.add_integrator(ScalarSourceIntegrator(coef,q=self.q)) - lform.add_integrator(ScalarSourceIntegrator(u1, q=self.q)) - lform.add_integrator(ScalarSourceIntegrator(fun, q=self.q)) - F0 = lform.assembly() - F1 = bm.zeros(self.pspace.number_of_global_dofs()) - b = bm.concatenate([F0,F1,F1]) - return b - - def backward_b0(self, yn1, pnx1, pnx2): - T =self.timeline.T1 - - @barycentric - def fun(bcs, index=None): - result = yn1(bcs) - return result - - @cartesian - def coef(p, index=None): - result = self.pde.z_solution(p, T) - result -= self.pde.z_t_solution(p, T) - result *= 2/self.dt**2 - result = -self.pde.y_d_fun(p, time=T-self.dt) - return result - - lform = LinearForm(self.yspace) - lform.add_integrator(ScalarSourceIntegrator(coef,q=self.q)) - lform.add_integrator(ScalarSourceIntegrator(fun, q=self.q)) - F0 = lform.assembly() - - @barycentric - def funpx1(bcs, index=None): - result = -pnx1(bcs)[..., 0] - return result - - @cartesian - def coefpx1(p, index=None): - result = self.pde.p_dx1_fun(p, time=T-self.dt) - return result - lform = LinearForm(self.pspace) - lform.add_integrator(ScalarSourceIntegrator(coefpx1,q=self.q)) - lform.add_integrator(ScalarSourceIntegrator(funpx1, q=self.q)) - F1= lform.assembly() - - @barycentric - def funpx2(bcs, index=None): - result = -pnx2(bcs)[..., 1] - return result - - @cartesian - def coefpx2(p, index=None): - result = self.pde.p_dx2_fun(p, time=T-self.dt) - return result - lform = LinearForm(self.pspace) - lform.add_integrator(ScalarSourceIntegrator(coefpx2,q=self.q)) - lform.add_integrator(ScalarSourceIntegrator(funpx2, q=self.q)) - F2 = lform.assembly() - - b = bm.concatenate([F0,F1,F2]) - return b - - def forward_boundary(self, A, b, isbdof, t): - x0 = bm.zeros(self.yspace.number_of_global_dofs()) - yh, yisbdof = self.yspace.boundary_interpolate( - partial(self.pde.y_solution, time=t), x0) - x1 = bm.zeros(self.pspace.number_of_global_dofs()) - px1, pisbdof = self.pspace.boundary_interpolate( - partial(self.pde.px1_solution, time=t), x1) - x2 = bm.zeros(self.pspace.number_of_global_dofs()) - px2, pisbdof = self.pspace.boundary_interpolate( - partial(self.pde.px2_solution, time=t), x2) - xx = bm.concatenate([x0,x1,x2]) - - b -= A@xx - b[isbdof] = xx[isbdof] - - kwargs = A.values_context() - indices = A.indices() - new_values = bm.copy(A.values()) - IDX = isbdof[indices[0, :]] | isbdof[indices[1, :]] - new_values[IDX] = 0 - A = COOTensor(indices, new_values, A.sparse_shape) - - index = bm.nonzero(isbdof)[0] - shape = new_values.shape[:-1] + (len(index), ) - one_values = bm.ones(shape, **kwargs) - one_indices = bm.stack([index, index], axis=0) - A1 = COOTensor(one_indices, one_values, A.sparse_shape) - A = A.add(A1).coalesce() - - return A,b - - def backward_boundary(self, A, b, isbdof, t): - x0 = bm.zeros(self.yspace.number_of_global_dofs()) - zh, zisbdof = self.yspace.boundary_interpolate( - partial(self.pde.z_solution, time=t), x0) - x1 = bm.zeros(self.pspace.number_of_global_dofs()) - qx1, qisbdof = self.pspace.boundary_interpolate( - partial(self.pde.qx1_solution, time=t), x1) - x2 = bm.zeros(self.pspace.number_of_global_dofs()) - qx2, qisbdof = self.pspace.boundary_interpolate( - partial(self.pde.qx2_solution, time=t), x2) - xx = bm.concatenate([x0,x1,x2]) - - b -= A@xx - b[isbdof] = xx[isbdof] - - kwargs = A.values_context() - indices = A.indices() - new_values = bm.copy(A.values()) - IDX = isbdof[indices[0, :]] | isbdof[indices[1, :]] - new_values[IDX] = 0 - A = COOTensor(indices, new_values, A.sparse_shape) - - index = bm.nonzero(isbdof)[0] - shape = new_values.shape[:-1] + (len(index), ) - one_values = bm.ones(shape, **kwargs) - one_indices = bm.stack([index, index], axis=0) - A1 = COOTensor(one_indices, one_values, A.sparse_shape) - A = A.add(A1).coalesce() - return A,b - - def A(self): - dt = self.dt - A0 = COOTensor.concat(((1/dt**2 + self.pde.c)*self.M, -self.S1, -self.S2), axis=0) - A1 = COOTensor.concat((self.S1.T, self.M00, self.M01), axis=0) - A2 = COOTensor.concat((self.S2.T, self.M10, self.M11), axis=0) - A = COOTensor.concat((A0,A1,A2), axis=1) - return A - - def forward_b(self, y0, y1, u, t): - if u==None: - u = self.yspace.function() - - @barycentric - def fun(bcs, index=None): - result = 2*y1(bcs) - y0(bcs) - result *= 1/self.dt**2 - return result - @cartesian - def coef(p, index=None): - result = self.pde.f_fun(p, time=t, index=index) - return result - - lform = LinearForm(self.yspace) - lform.add_integrator(ScalarSourceIntegrator(coef,q=self.q)) - lform.add_integrator(ScalarSourceIntegrator(u, q=self.q)) - lform.add_integrator(ScalarSourceIntegrator(fun, q=self.q)) - F0 = lform.assembly() - F1 = bm.zeros(self.pspace.number_of_global_dofs()) - b = bm.concatenate([F0,F1,F1]) - return b - - def backward_b(self, zn0, zn1, yn1, pnx1, pnx2, t): - - @barycentric - def fun(bcs, index=None): - result = 2*zn1(bcs) - zn0(bcs) - result *= 1/self.dt**2 - result = yn1(bcs) - return result - - @cartesian - def coef(p, index=None): - result = -self.pde.y_d_fun(p, time=t) - return result - - lform = LinearForm(self.yspace) - lform.add_integrator(ScalarSourceIntegrator(coef,q=self.q)) - lform.add_integrator(ScalarSourceIntegrator(fun, q=self.q)) - F0 = lform.assembly() - - @barycentric - def funpx1(bcs, index=None): - result = -pnx1(bcs)[..., 0] - return result - - @cartesian - def coefpx1(p, index=None): - result = self.pde.p_dx1_fun(p, time=t) - return result - lform = LinearForm(self.pspace) - lform.add_integrator(ScalarSourceIntegrator(coefpx1,q=self.q)) - lform.add_integrator(ScalarSourceIntegrator(funpx1, q=self.q)) - F1= lform.assembly() - - @barycentric - def funpx2(bcs, index=None): - result = -pnx2(bcs)[..., 1] - return result - - @cartesian - def coefpx2(p, index=None): - result = self.pde.p_dx2_fun(p, time=t) - return result - lform = LinearForm(self.pspace) - lform.add_integrator(ScalarSourceIntegrator(coefpx2,q=self.q)) - lform.add_integrator(ScalarSourceIntegrator(funpx2, q=self.q)) - F2 = lform.assembly() - - b = bm.concatenate([F0,F1,F2]) - return b - - def solve_u(self, z): - result = bm.max(self.mesh.integral(z), 0) - z #积分子 - return result - - def mumps_solve(self, A, b): - import scipy.sparse as sp - values = A.values() - indices = A.indices() - A = sp.coo_matrix((values, (indices[0], indices[1])), shape=A.shape) - A = A.tocsr() - x = sp.linalg.spsolve(A,b) - return x - diff --git a/app/tssim/ocp_opt/jovan/solver_update.py b/app/tssim/ocp_opt/solver_update.py similarity index 96% rename from app/tssim/ocp_opt/jovan/solver_update.py rename to app/tssim/ocp_opt/solver_update.py index d196370b9..12eb2647a 100644 --- a/app/tssim/ocp_opt/jovan/solver_update.py +++ b/app/tssim/ocp_opt/solver_update.py @@ -153,7 +153,7 @@ def Backward_LForm_bn(self): @cartesian def z_coef_c(p, index=None): - result = 2*self.pde.z_solution(p, T) - 2*self.pde.z_t_solution(p, T)*self.dt + result = 2*self.pde.z_solution(p, T) - 2*dt*self.pde.z_t_solution(p, T) result -= dt**2 * self.pde.y_d_fun(p, time=T-dt) return result @@ -216,6 +216,7 @@ def Backward_LForm_b(self): def Backward_update(self, zn0, zn1, yn2, p2, t2): dt = self.dt + @cartesian def coef_c_z(p, index=None): result = -dt**2 * self.pde.y_d_fun(p, time=t2) @@ -248,6 +249,7 @@ def coef_b_q(bcs, index=None): def solve_z_bar(self, allz): dt = self.dt integral_z = bm.array([self.mesh.integral(i, q=self.q) for i in allz],dtype=bm.float64) - z_bar = (dt/2)*(integral_z[:-1] + integral_z[1:]) - return bm.sum(z_bar) + z_bar = bm.sum((dt/2)*(integral_z[:-1] + integral_z[1:])) + z_bar /= (self.timeline.T1 - self.timeline.T0)*self.mesh.integral(1, q=self.q) + return z_bar From 7fdf796cfd0a6b55b8640060a430d9c2820389ee Mon Sep 17 00:00:00 2001 From: Flantori Date: Fri, 22 Nov 2024 19:53:11 +0800 Subject: [PATCH 22/63] feat(mesh): New utils `inverse_relation` to return the inverse relationship of a homogeneous entity in COO sparse format. --- fealpy/mesh/utils.py | 22 ++++++++++++++ test/mesh/test_utils.py | 64 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 test/mesh/test_utils.py diff --git a/fealpy/mesh/utils.py b/fealpy/mesh/utils.py index 930cb21ec..5227af72e 100644 --- a/fealpy/mesh/utils.py +++ b/fealpy/mesh/utils.py @@ -47,6 +47,28 @@ def edim2entity(storage: Dict, factory: Dict, edim: int, index=None): return et[index] +def inverse_relation(entity: TensorLike, size: int, index=None): + """Return the inverse relationship of a homogeneous entity in COO sparse format, + including the row indices, column indices, and shape.""" + assert entity.ndim == 2 + + if index is None: + row = entity.reshape(-1) + col = bm.repeat(bm.arange(entity.shape[0]), entity.shape[1]) + else: + if isinstance(index, TensorLike) and index.dtype == bm.bool: + flag = index + else: + flag = bm.zeros(size, dtype=bm.bool, device=entity.device) + flag = bm.set_at(flag, index, True) + relation_flag = flag[entity] + row = entity.reshape(-1)[relation_flag.reshape(-1)] + num_selected_each_entity = bm.sum(relation_flag, axis=-1, dtype=bm.int32) + col = bm.repeat(bm.arange(entity.shape[0]), num_selected_each_entity) + + return row, col, (size, entity.shape[0]) + + def flocc(array: TensorLike, /): """Find the first and last occurrence of each unique row in a 2D array. diff --git a/test/mesh/test_utils.py b/test/mesh/test_utils.py new file mode 100644 index 000000000..26e85d50a --- /dev/null +++ b/test/mesh/test_utils.py @@ -0,0 +1,64 @@ + +import pytest + +import numpy as np +from fealpy.backend import backend_manager as bm +from fealpy.mesh.utils import inverse_relation + +inverse_relation_with_index_data = [ + { + "entity": np.array( + [[0, 3, 4], [0, 2, 1], [1, 4, 5], [1, 5, 2], + [3, 6, 7], [3, 7, 4], [4, 7, 8], [4, 8, 5]], + dtype=np.int32), + "size": 9, + "index": np.array([0, 1, 2, 3, 4, 5]), + "row": np.array([0, 3, 4, 0, 2, 1, 1, 4, 5, 1, 5, 2, 3, 3, 4, 4, 4, 5], dtype=np.int32), + "col": np.array([0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 5, 5, 6, 7, 7], dtype=np.int32), + "spshape": (9, 8) + } +] + + +@pytest.mark.parametrize('data', inverse_relation_with_index_data) +@pytest.mark.parametrize('backend', ['numpy', 'pytorch', 'jax']) +def test_inverse_relation_with_index(data, backend): + bm.set_backend(backend) + + entity = bm.from_numpy(data['entity']) + size = data['size'] + index = bm.from_numpy(data['index']) + row, col, spshape = inverse_relation(entity, size, index) + + assert bm.all(bm.equal(row, bm.from_numpy(data['row']))) + assert bm.all(bm.equal(col, bm.from_numpy(data['col']))) + assert spshape == data['spshape'] + + +inverse_relation_with_flag_data = [ + { + "entity": np.array( + [[0, 3, 4], [0, 2, 1], [1, 4, 5], [1, 5, 2], + [3, 6, 7], [3, 7, 4], [4, 7, 8], [4, 8, 5]], + dtype=np.int32), + "size": 9, + "flag": np.array([True, True, False, True, True, False, True, True, False], dtype=np.bool), + "row": np.array([0, 3, 4, 0, 1, 1, 4, 1, 3, 6, 7, 3, 7, 4, 4, 7, 4], dtype=np.int32), + "col": np.array([0, 0, 0, 1, 1, 2, 2, 3, 4, 4, 4, 5, 5, 5, 6, 6, 7], dtype=np.int32), + "spshape": (9, 8) + } +] + +@pytest.mark.parametrize('data', inverse_relation_with_flag_data) +@pytest.mark.parametrize('backend', ['numpy', 'pytorch', 'jax']) +def test_inverse_relation_with_flag(data, backend): + bm.set_backend(backend) + + entity = bm.from_numpy(data['entity']) + size = data['size'] + index = bm.from_numpy(data['flag']) + row, col, spshape = inverse_relation(entity, size, index) + + assert bm.all(bm.equal(row, bm.from_numpy(data['row']))) + assert bm.all(bm.equal(col, bm.from_numpy(data['col']))) + assert spshape == data['spshape'] From 1f746a53030fae03e9864f525e14b1f2919db39a Mon Sep 17 00:00:00 2001 From: chaos <2250213115@qq.com> Date: Sat, 23 Nov 2024 15:29:35 +0800 Subject: [PATCH 23/63] update --- .../lafemims/data_generator/__init__.py | 2 +- .../near_field_data_generator.py | 195 +++++++++++------- .../test/test_near_field_data_generator.py | 46 ----- app/lafem-ims/setup.py | 19 ++ .../test/test_near_field_data_generator.py | 74 +++++++ .../test/test_parallel_data_generation.py | 0 fealpy/mesh/uniform_mesh_2d.py | 26 +++ .../ml/generator/near_field_data_generator.py | 6 +- 8 files changed, 248 insertions(+), 120 deletions(-) delete mode 100644 app/lafem-ims/lafemims/test/test_near_field_data_generator.py create mode 100644 app/lafem-ims/setup.py create mode 100644 app/lafem-ims/test/test_near_field_data_generator.py create mode 100644 app/lafem-ims/test/test_parallel_data_generation.py diff --git a/app/lafem-ims/lafemims/data_generator/__init__.py b/app/lafem-ims/lafemims/data_generator/__init__.py index e4b04c5db..7ab1d52d1 100644 --- a/app/lafem-ims/lafemims/data_generator/__init__.py +++ b/app/lafem-ims/lafemims/data_generator/__init__.py @@ -1 +1 @@ -from near_field_data_generator import NearFieldDataFEMGenerator2d \ No newline at end of file +from .near_field_data_generator import NearFieldDataFEMGenerator2d \ No newline at end of file diff --git a/app/lafem-ims/lafemims/data_generator/near_field_data_generator.py b/app/lafem-ims/lafemims/data_generator/near_field_data_generator.py index 6db0ea652..5f8221f79 100644 --- a/app/lafem-ims/lafemims/data_generator/near_field_data_generator.py +++ b/app/lafem-ims/lafemims/data_generator/near_field_data_generator.py @@ -1,31 +1,38 @@ -import os +import os +import matplotlib.pyplot as plt from numpy.typing import NDArray from typing import Sequence, Callable -import matplotlib.pyplot as plt from fealpy.backend import backend_manager as bm from fealpy.mesh import TriangleMesh, QuadrangleMesh, UniformMesh2d -from fealpy.functionspace import LagrangeFESpace -from fealpy.fem import ScalarDiffusionIntegrator, ScalarMassIntegrator, ScalarSourceIntegrator, ScalarConvectionIntegrator, DirichletBC -from fealpy.fem import BilinearForm, LinearForm from fealpy.pde.pml_2d import PMLPDEModel2d +from fealpy.functionspace import LagrangeFESpace +from fealpy.fem import ( + ScalarDiffusionIntegrator, + ScalarMassIntegrator, + ScalarSourceIntegrator, + ScalarConvectionIntegrator, + BilinearForm, + LinearForm, + DirichletBC +) from fealpy.solver import spsolve class NearFieldDataFEMGenerator2d: def __init__(self, - domain:Sequence[float], - mesh:str, - nx:int, - ny:int, - p:int, - q:int, - u_inc:str, - levelset:Callable[[NDArray], NDArray], - d:Sequence[float], - k:Sequence[float], - reciever_points:NDArray): + domain: Sequence[float], + mesh: str, + nx: int, + ny: int, + p: int, + q: int, + u_inc: str, + levelset: Callable[[NDArray], NDArray], + d: Sequence[float], + k: Sequence[float], + reciever_points: NDArray): self.domain = domain self.nx = nx @@ -35,53 +42,65 @@ def __init__(self, self.u_inc = u_inc self.levelset = levelset + # 验证并创建网格 if mesh not in ['InterfaceMesh', 'QuadrangleMesh', 'UniformMesh']: raise ValueError("Invalid value for 'mesh'. Choose from 'InterfaceMesh', 'QuadrangleMesh' or 'UniformMesh'.") + + if mesh == 'InterfaceMesh': + self.mesh = TriangleMesh.interfacemesh_generator(box=self.domain, nx=self.nx, ny=self.ny, phi=self.levelset) + self.meshtype = 'InterfaceMesh' + elif mesh == 'QuadrangleMesh': + self.mesh = QuadrangleMesh.from_box(box=self.domain, nx=self.nx, ny=self.ny) + self.meshtype = 'QuadrangleMesh' else: - if mesh == 'InterfaceMesh': - self.mesh = TriangleMesh.interfacemesh_generator(box=self.domain, nx=self.nx, ny=self.ny, phi=self.levelset) - self.meshtype = 'InterfaceMesh' - elif mesh == 'QuadrangleMesh': - self.mesh = QuadrangleMesh.from_box(box=self.domain, nx=self.nx, ny=self.ny) - self.meshtype = 'QuadrangleMesh' - else: - EXTC_1 = self.nx - EXTC_2 = self.ny - HC_1 = 1/EXTC_1 * (self.domain[1] - self.domain[0]) - HC_2 = 1/EXTC_2 * (self.domain[3] - self.domain[2]) - self.mesh = UniformMesh2d((0, EXTC_1, 0, EXTC_2), (HC_1, HC_2), origin=(self.domain[0], self.domain[2])) - self.meshtype = 'UniformMesh' - - self.mesh.ftype = bm.complex128 - self.d = d + EXTC_1 = self.nx + EXTC_2 = self.ny + HC_1 = 1 / EXTC_1 * (self.domain[1] - self.domain[0]) + HC_2 = 1 / EXTC_2 * (self.domain[3] - self.domain[2]) + self.mesh = UniformMesh2d((0, EXTC_1, 0, EXTC_2), (HC_1, HC_2), origin=(self.domain[0], self.domain[2])) + self.meshtype = 'UniformMesh' + + self.d = d self.k = k self.reciever_points = reciever_points qf = self.mesh.quadrature_formula(self.q, 'cell') - self.bc, _= qf.get_quadrature_points_and_weights() - - def get_nearfield_data(self, k:float, d:Sequence[float]): - - k_index = (self.k).index(k) - d_index = (self.d).index(d) - pde = PMLPDEModel2d(levelset=self.levelset, - domain=self.domain, - u_inc=self.u_inc, - A=1, - k=self.k[k_index], - d=self.d[d_index], - refractive_index=[1, 1+1/self.k[k_index]**2], - absortion_constant=1.79, - lx=1.0, - ly=1.0 - ) + self.bc, _ = qf.get_quadrature_points_and_weights() + + def get_nearfield_data(self, k: float, d: Sequence[float]): + """ + 获取近场数据。 + + 参数: + - k: 波数 + - d: 波矢量方向 + + 返回: + - uh: 近场数据 + """ + k_index = self.k.index(k) + d_index = self.d.index(d) + pde = PMLPDEModel2d( + levelset=self.levelset, + domain=self.domain, + u_inc=self.u_inc, + A=1, + k=self.k[k_index], + d=self.d[d_index], + refractive_index=[1, 1 + 1 / self.k[k_index]**2], + absortion_constant=1.79, + lx=1.0, + ly=1.0 + ) space = LagrangeFESpace(self.mesh, p=self.p) + # 定义积分器 D = ScalarDiffusionIntegrator(pde.diffusion_coefficient, q=self.q) C = ScalarConvectionIntegrator(pde.convection_coefficient, q=self.q) M = ScalarMassIntegrator(pde.reaction_coefficient, q=self.q) f = ScalarSourceIntegrator(pde.source, q=self.q) + # 组装双线性形式和线性形式 b = BilinearForm(space) b.add_integrator([D, C, M]) @@ -90,14 +109,27 @@ def get_nearfield_data(self, k:float, d:Sequence[float]): A = b.assembly() F = l.assembly() + bc = DirichletBC(space, pde.dirichlet) uh = space.function(dtype=bm.complex128) A, F = bc.apply(A, F) uh[:] = spsolve(A, F, solver='scipy') return uh - - def points_location_and_bc(self, p, domain:Sequence[float], nx:int, ny:int): + def points_location_and_bc(self, p: NDArray, domain: Sequence[float], nx: int, ny: int): + """ + 计算接收点的位置和重心坐标。 + + 参数: + - p: 接收点坐标 + - domain: 计算域 + - nx: x方向的网格数 + - ny: y方向的网格数 + + 返回: + - location: 接收点所在单元的索引 + - bc: 重心坐标 + """ x = p[..., 0] y = p[..., 1] cell_length_x = (domain[1] - domain[0]) / nx @@ -113,17 +145,26 @@ def points_location_and_bc(self, p, domain:Sequence[float], nx:int, ny:int): bc = (bc_x, bc_y) return location, bc - def data_for_dsm(self, k:float, d:Sequence[float]): + def data_for_dsm(self, k: float, d: Sequence[float]): + """ + 获取用于DSM的数据。 + 参数: + - k: 波数 + - d: 波矢量方向 + + 返回: + - data: DSM数据 + """ reciever_points = self.reciever_points data_length = reciever_points.shape[0] - data = bm.zeros((data_length, ), dtype=bm.complex128) + data = bm.zeros((data_length,), dtype=bm.complex128) uh = self.get_nearfield_data(k=k, d=d) - - if self.meshtype =='InterfaceMesh': + + if self.meshtype == 'InterfaceMesh': b = self.mesh.point_to_bc(reciever_points) location = self.mesh.location(reciever_points) - for i in range (data_length): + for i in range(data_length): data[i] = uh(b[i])[location[i]] elif self.meshtype == 'QuadrangleMesh': for i in range(data_length): @@ -140,14 +181,20 @@ def data_for_dsm(self, k:float, d:Sequence[float]): u = uh(b).reshape(-1) data[i] = u[location] return data - - def save(self, save_path:str, scatterer_index:int): + def save(self, save_path: str, scatterer_index: int): + """ + 保存数据。 + + 参数: + - save_path: 保存路径 + - scatterer_index: 散射体索引 + """ k_values = self.k d_values = self.d data_dict = {} - for i in range (len(k_values)): - for j in range (len(d_values)): + for i in range(len(k_values)): + for j in range(len(d_values)): k_name = f'k={k_values[i]}' d_name = d_values[j] name = f"{k_name}, d={d_name}" @@ -155,8 +202,14 @@ def save(self, save_path:str, scatterer_index:int): filename = os.path.join(save_path, f"data_for_dsm_{scatterer_index}.bmz") bm.savez(filename, **data_dict) - def visualization_of_nearfield_data(self, k:float, d:Sequence[float]): + def visualization_of_nearfield_data(self, k: float, d: Sequence[float]): + """ + 可视化近场数据。 + 参数: + - k: 波数 + - d: 波矢量方向 + """ uh = self.get_nearfield_data(k=k, d=d) value = uh(self.bc) if self.meshtype == 'UniformMesh': @@ -164,15 +217,15 @@ def visualization_of_nearfield_data(self, k:float, d:Sequence[float]): self.mesh.add_plot(plt, cellcolor=value[..., 0].real, linewidths=0) self.mesh.add_plot(plt, cellcolor=value[..., 0].imag, linewidths=0) - #TODO - fig = plt.figure() - axes = fig.add_subplot(1, 3, 1) - self.mesh.add_plot(axes) - if self.meshtype == 'UniformMesh': - uh = uh.view(bm.ndarray) - axes = fig.add_subplot(1, 3, 2, projection='3d') - self.mesh.show_function(axes, bm.real(uh)) - axes = fig.add_subplot(1, 3, 3, projection='3d') - self.mesh.show_function(axes, bm.imag(uh)) + # TODO: 添加更多可视化选项 + # fig = plt.figure() + # axes = fig.add_subplot(1, 3, 1) + # self.mesh.add_plot(axes) + # if self.meshtype == 'UniformMesh': + # uh = uh.view(bm.ndarray) + # axes = fig.add_subplot(1, 3, 2, projection='3d') + # self.mesh.show_function(axes, bm.real(uh)) + # axes = fig.add_subplot(1, 3, 3, projection='3d') + # self.mesh.show_function(axes, bm.imag(uh)) plt.show() \ No newline at end of file diff --git a/app/lafem-ims/lafemims/test/test_near_field_data_generator.py b/app/lafem-ims/lafemims/test/test_near_field_data_generator.py deleted file mode 100644 index 623a4280b..000000000 --- a/app/lafem-ims/lafemims/test/test_near_field_data_generator.py +++ /dev/null @@ -1,46 +0,0 @@ - -from math import sqrt, pi -import numpy as np -from numpy.typing import NDArray - -from fealpy.ml.generator import NearFieldDataFEMGenerator2d -from fealpy.ml.sampler import CircleCollocator - - -domain = [-6, 6, -6, 6] -u_inc = 'cos(d_0*k*x + d_1*k*y) + sin(d_0*k*x + d_1*k*y) * 1j' -d = [[-sqrt(0.5), sqrt(0.5)]] -k = [2 * pi] - -def levelset(p: NDArray, centers: NDArray, radius: NDArray): - """ - Calculate level set function value. - """ - struct = p.shape[:-1] - p = p.reshape(-1, p.shape[-1]) - dis = np.linalg.norm(p[:, None, :] - centers[None, :, :], axis=-1) # (N, NCir) - ret = np.min(dis - radius[None, :], axis=-1) # (N, ) - return ret.reshape(struct) - -reciever_points = CircleCollocator(0, 0, 5).run(50) -reciever_points = reciever_points.detach().numpy() -cirs = np.array([[0.4, -0.6, 0.2], - [0.6, -0.5, 0.1], - [0.3, 0.2, 0.3]], dtype=np.float64) - -centers = cirs[:, 0:2] -radius = cirs[:, 2] -ls_fn = lambda p: levelset(p, centers, radius) - -generator = NearFieldDataFEMGenerator2d(domain=domain, - mesh='UniformMesh', - nx=100, - ny=100, - p=1, - q=3, - u_inc=u_inc, - levelset=ls_fn, - d=d, - k=k, - reciever_points=reciever_points) -generator.visualization_of_nearfield_data(k=2*pi, d=[-sqrt(0.5), sqrt(0.5)]) diff --git a/app/lafem-ims/setup.py b/app/lafem-ims/setup.py new file mode 100644 index 000000000..56e7b0822 --- /dev/null +++ b/app/lafem-ims/setup.py @@ -0,0 +1,19 @@ + +import os +import pathlib +from setuptools import setup, find_packages + +from lafemims import __version__ + + +setup( + name="lafemims", + version=__version__, + description="LaFEM-IMS: Learn-automated FEM Inverse Medium Scattering", + url="", + author="Chaos", + author_email="", + license="GNU", + packages=find_packages(), + python_requires=">=3.8", +) diff --git a/app/lafem-ims/test/test_near_field_data_generator.py b/app/lafem-ims/test/test_near_field_data_generator.py new file mode 100644 index 000000000..b0b95ffab --- /dev/null +++ b/app/lafem-ims/test/test_near_field_data_generator.py @@ -0,0 +1,74 @@ + +from math import sqrt, pi +import numpy as np +from numpy.typing import NDArray + +from lafemims.data_generator import NearFieldDataFEMGenerator2d +from fealpy.ml.sampler import CircleCollocator + +# 定义计算域 +domain = [-6, 6, -6, 6] + +# 定义入射波函数 +u_inc = 'cos(d_0*k*x + d_1*k*y) + sin(d_0*k*x + d_1*k*y) * 1j' + +# 定义波矢量方向和波数 +d = [[-sqrt(0.5), sqrt(0.5)]] +k = [2 * pi] + +def levelset(p: NDArray, centers: NDArray, radius: NDArray) -> NDArray: + """ + 计算水平集函数值。 + + 参数: + - p: 坐标点数组,形状为 (N, 2) + - centers: 圆心坐标数组,形状为 (NCir, 2) + - radius: 半径数组,形状为 (NCir,) + + 返回: + - 水平集函数值,形状为 (N,) + """ + struct = p.shape[:-1] # 获取输入点的结构形状 + p = p.reshape(-1, p.shape[-1]) # 将输入点展平为 (N, 2) + + # 计算每个点到所有圆心的距离 + dis = np.linalg.norm(p[:, None, :] - centers[None, :, :], axis=-1) # 形状为 (N, NCir) + + # 计算每个点到最近圆的距离 + ret = np.min(dis - radius[None, :], axis=-1) # 形状为 (N,) + + return ret.reshape(struct) # 恢复输入点的原始结构形状 + +# 生成接收点 +reciever_points = CircleCollocator(0, 0, 5).run(50) + +# 定义圆的中心和半径 +cirs = np.array([ + [0.4, -0.6, 0.2], + [0.6, -0.5, 0.1], + [0.3, 0.2, 0.3] +], dtype=np.float64) + +centers = cirs[:, 0:2] # 圆心坐标 +radius = cirs[:, 2] # 圆的半径 + +# 定义水平集函数 +ls_fn = lambda p: levelset(p, centers, radius) + +# 创建近场数据生成器 +generator = NearFieldDataFEMGenerator2d( + domain=domain, + mesh='UniformMesh', + nx=100, + ny=100, + p=1, + q=3, + u_inc=u_inc, + levelset=ls_fn, + d=d, + k=k, + reciever_points=reciever_points +) + +# 可视化近场数据 +generator.visualization_of_nearfield_data(k=k[0], d=d[0]) diff --git a/app/lafem-ims/test/test_parallel_data_generation.py b/app/lafem-ims/test/test_parallel_data_generation.py new file mode 100644 index 000000000..e69de29bb diff --git a/fealpy/mesh/uniform_mesh_2d.py b/fealpy/mesh/uniform_mesh_2d.py index b20f01fe0..efa17045b 100644 --- a/fealpy/mesh/uniform_mesh_2d.py +++ b/fealpy/mesh/uniform_mesh_2d.py @@ -507,6 +507,32 @@ def edge_unit_normal(self, index: Index=_S, out=None) -> TensorLike: """ return self.edge_normal(index=index, unit=True, out=out) + def cell_location(self, points) -> TensorLike: + """ + @brief 给定一组点,确定所有点所在的单元 + + """ + hx = self.h[0] + hy = self.h[1] + v = bm.real(points - bm.array(self.origin, dtype=points.dtype)) + n0 = v[..., 0] // hx + n1 = v[..., 1] // hy + + return n0.astype('int64'), n1.astype('int64') + + def point_to_bc(self, points): + + x = points[..., 0] + y = points[..., 1] + + bc_x_ = bm.real((x - self.origin[0]) / self.h[0]) % 1 + bc_y_ = bm.real((y - self.origin[1]) / self.h[1]) % 1 + bc_x = bm.array([[bc_x_, 1 - bc_x_]], dtype=bm.float64) + bc_y = bm.array([[bc_y_, 1 - bc_y_]], dtype=bm.float64) + val = (bc_x, bc_y) + + return val + #################################### 插值点 ############################################# def interpolation_points(self, p: int, index: Index=_S) -> TensorLike: diff --git a/fealpy/ml/generator/near_field_data_generator.py b/fealpy/ml/generator/near_field_data_generator.py index f35338328..0072887bc 100644 --- a/fealpy/ml/generator/near_field_data_generator.py +++ b/fealpy/ml/generator/near_field_data_generator.py @@ -12,6 +12,8 @@ from fealpy.fem import BilinearForm, LinearForm from fealpy.pde.pml_2d import PMLPDEModel2d +bm.set_backend('numpy') + class NearFieldDataFEMGenerator2d: def __init__(self, @@ -52,7 +54,6 @@ def __init__(self, self.mesh = UniformMesh2d((0, EXTC_1, 0, EXTC_2), (HC_1, HC_2), origin=(self.domain[0], self.domain[2])) self.meshtype = 'UniformMesh' - # self.mesh.ftype = bm.complex128 self.d = d self.k = k self.reciever_points = reciever_points @@ -139,6 +140,7 @@ def data_for_dsm(self, k:float, d:Sequence[float]): b = self.mesh.point_to_bc(reciever_points[i]) u = uh(b).reshape(-1) data[i] = u[location] + print(data) return data def save(self, save_path:str, scatterer_index:int): @@ -152,7 +154,7 @@ def save(self, save_path:str, scatterer_index:int): d_name = d_values[j] name = f"{k_name}, d={d_name}" data_dict[name] = self.data_for_dsm(k=k_values[i], d=d_values[j]) - filename = os.path.join(save_path, f"data_for_dsm_{scatterer_index}.bmz") + filename = os.path.join(save_path, f"data_for_dsm_{scatterer_index}.npz") bm.savez(filename, **data_dict) def visualization_of_nearfield_data(self, k:float, d:Sequence[float]): From 371c8cd0352f26aee1319d5e34965d6109640436 Mon Sep 17 00:00:00 2001 From: chaos <2250213115@qq.com> Date: Sat, 23 Nov 2024 15:31:39 +0800 Subject: [PATCH 24/63] - --- fealpy/ml/generator/near_field_data_generator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fealpy/ml/generator/near_field_data_generator.py b/fealpy/ml/generator/near_field_data_generator.py index 0072887bc..44af20148 100644 --- a/fealpy/ml/generator/near_field_data_generator.py +++ b/fealpy/ml/generator/near_field_data_generator.py @@ -2,6 +2,7 @@ from fealpy.backend import backend_manager as bm from numpy.typing import NDArray +import numpy as np from fealpy.solver import spsolve from typing import Sequence, Callable import matplotlib.pyplot as plt @@ -140,7 +141,6 @@ def data_for_dsm(self, k:float, d:Sequence[float]): b = self.mesh.point_to_bc(reciever_points[i]) u = uh(b).reshape(-1) data[i] = u[location] - print(data) return data def save(self, save_path:str, scatterer_index:int): @@ -155,7 +155,7 @@ def save(self, save_path:str, scatterer_index:int): name = f"{k_name}, d={d_name}" data_dict[name] = self.data_for_dsm(k=k_values[i], d=d_values[j]) filename = os.path.join(save_path, f"data_for_dsm_{scatterer_index}.npz") - bm.savez(filename, **data_dict) + np.savez(filename, **data_dict) def visualization_of_nearfield_data(self, k:float, d:Sequence[float]): From 3b5adbae26c7172750ad7416d00594951e589c6c Mon Sep 17 00:00:00 2001 From: BenHBLiu Date: Sat, 23 Nov 2024 16:30:24 +0800 Subject: [PATCH 25/63] add DCS test --- test/opt/test_iopt_alg.py | 43 +++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/test/opt/test_iopt_alg.py b/test/opt/test_iopt_alg.py index 7bf0f14d5..12b461e34 100644 --- a/test/opt/test_iopt_alg.py +++ b/test/opt/test_iopt_alg.py @@ -14,6 +14,7 @@ from fealpy.opt import ButterflyOptAlg from fealpy.opt import ExponentialTrigonometricOptAlg from fealpy.opt import DifferentialEvolution +from fealpy.opt import DifferentialtedCreativeSearch from fealpy.opt import initialize from fealpy.opt.optimizer_base import Optimizer, opt_alg_options @@ -29,7 +30,7 @@ def test_crayfish_opt_alg(self, backend, data, NP): bm.set_backend(backend) lb, ub = data['domain'] x0 = initialize(NP, data['ndim'], ub, lb) - option = opt_alg_options(x0, data['objective'], data['domain'] , NP) + option = opt_alg_options(x0, data['objective'], data['domain'], NP) optimizer = CrayfishOptAlg(option) optimizer.run() @@ -40,7 +41,7 @@ def test_honeybadger_opt_alg(self, backend, data, NP): bm.set_backend(backend) lb, ub = data['domain'] x0 = initialize(NP, data['ndim'], ub, lb) - option = opt_alg_options(x0, data['objective'], data['domain'] , NP) + option = opt_alg_options(x0, data['objective'], data['domain'], NP) optimizer = HoneybadgerAlg(option) gbest, gbest_f = optimizer.run() @@ -51,7 +52,7 @@ def test_quantumparticleswarm_opt_alg(self, backend, data, NP): bm.set_backend(backend) lb, ub = data['domain'] x0 = initialize(NP, data['ndim'], ub, lb) - option = opt_alg_options(x0, data['objective'], data['domain'] , NP) + option = opt_alg_options(x0, data['objective'], data['domain'], NP) optimizer = QuantumParticleSwarmOpt(option) optimizer = LevyQuantumParticleSwarmOpt(option) gbest, gbest_f = optimizer.run() @@ -63,7 +64,7 @@ def test_snowmelt_opt_alg(self, backend, data, NP): bm.set_backend(backend) lb, ub = data['domain'] x0 = initialize(NP, data['ndim'], ub, lb) - option = opt_alg_options(x0, data['objective'], data['domain'] , NP) + option = opt_alg_options(x0, data['objective'], data['domain'], NP) optimizer = SnowAblationOpt(option) gbest, gbest_f = optimizer.run() @@ -74,7 +75,7 @@ def test_hippopotamus_optimizer(self, backend, data, NP): bm.set_backend(backend) lb, ub = data['domain'] x0 = initialize(NP, data['ndim'], ub, lb) - option = opt_alg_options(x0, data['objective'], data['domain'] , NP) + option = opt_alg_options(x0, data['objective'], data['domain'], NP) optimizer = HippopotamusOptAlg(option) optimizer.run() @@ -85,7 +86,7 @@ def test_grey_wolf_optimizer(self, backend, data, NP): bm.set_backend(backend) lb, ub = data['domain'] x0 = initialize(NP, data['ndim'], ub, lb) - option = opt_alg_options(x0, data['objective'], data['domain'] , NP) + option = opt_alg_options(x0, data['objective'], data['domain'], NP) optimizer = GreyWolfOptimizer(option) gbest, gbest_f = optimizer.run() @@ -96,7 +97,7 @@ def test_crested_porcupine_opt(self, backend, data, NP): bm.set_backend(backend) lb, ub = data['domain'] x0 = initialize(NP, data['ndim'], ub, lb) - option = opt_alg_options(x0, data['objective'], data['domain'] , NP) + option = opt_alg_options(x0, data['objective'], data['domain'], NP) optimizer = CrestedPorcupineOpt(option) gbest, gbest_f = optimizer.run() @@ -107,7 +108,7 @@ def test_black_winged_kite_alg(self, backend, data, NP): bm.set_backend(backend) lb, ub = data['domain'] x0 = initialize(NP, data['ndim'], ub, lb) - option = opt_alg_options(x0, data['objective'], data['domain'] , NP) + option = opt_alg_options(x0, data['objective'], data['domain'], NP) optimizer = BlackwingedKiteAlg(option) gbest, gbest_f = optimizer.run() @@ -118,7 +119,7 @@ def test_cuckoo_search_opt(self, backend, data, NP): bm.set_backend(backend) lb, ub = data['domain'] x0 = initialize(NP, data['ndim'], ub, lb) - option = opt_alg_options(x0, data['objective'], data['domain'] , NP) + option = opt_alg_options(x0, data['objective'], data['domain'], NP) optimizer = CuckooSearchOpt(option) gbest, gbest_f = optimizer.run() @@ -129,7 +130,7 @@ def test_particle_swarm_opt(self, backend, data, NP): bm.set_backend(backend) lb, ub = data['domain'] x0 = initialize(NP, data['ndim'], ub, lb) - option = opt_alg_options(x0, data['objective'], data['domain'] , NP) + option = opt_alg_options(x0, data['objective'], data['domain'], NP) optimizer = ParticleSwarmOpt(option) gbest, gbest_f = optimizer.run() @@ -140,7 +141,7 @@ def test_butterfly_opt_alg(self, backend, data, NP): bm.set_backend(backend) lb, ub = data['domain'] x0 = initialize(NP, data['ndim'], ub, lb) - option = opt_alg_options(x0, data['objective'], data['domain'] , NP) + option = opt_alg_options(x0, data['objective'], data['domain'], NP) optimizer = ButterflyOptAlg(option) gbest, gbest_f = optimizer.run() @@ -151,7 +152,7 @@ def test_exponential_trigonometric_opt_alg(self, backend, data, NP): bm.set_backend(backend) lb, ub = data['domain'] x0 = initialize(NP, data['ndim'], ub, lb) - option = opt_alg_options(x0, data['objective'], data['domain'] , NP) + option = opt_alg_options(x0, data['objective'], data['domain'], NP) optimizer = ExponentialTrigonometricOptAlg(option) gbest, gbest_f = optimizer.run() @@ -162,10 +163,21 @@ def test_differential_evolution(self, backend, data, NP): bm.set_backend(backend) lb, ub = data['domain'] x0 = initialize(NP, data['ndim'], ub, lb) - option = opt_alg_options(x0, data['objective'], data['domain'] , NP) + option = opt_alg_options(x0, data['objective'], data['domain'], NP) optimizer = DifferentialEvolution(option) gbest, gbest_f = optimizer.run() + @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) + @pytest.mark.parametrize("data", iopt_data) + @pytest.mark.parametrize("NP", [100]) + def test_differentialted_creative_search(self, backend, data, NP): + bm.set_backend(backend) + lb, ub = data['domain'] + x0 = initialize(NP, data['ndim'], ub, lb) + option = opt_alg_options(x0, data['objective'], data['domain'], NP) + optimizer = DifferentialtedCreativeSearch(option) + gbest, gbest_f = optimizer.run() + if __name__ == "__main__": # pytest.main(["./test_iopt_alg.py", "-k", "test_honeybadger_opt_alg"]) # pytest.main(["./test_iopt_alg.py", "-k", "test_crayfish_opt_alg"]) @@ -178,5 +190,6 @@ def test_differential_evolution(self, backend, data, NP): # pytest.main(["./test_iopt_alg.py", "-k", "test_cuckoo_search_opt"]) # pytest.main(["./test_iopt_alg.py", "-k", "test_particle_swarm_opt"]) # pytest.main(["./test_iopt_alg.py", "-k", "test_butterfly_opt_alg"]) - pytest.main(["./test_iopt_alg.py", "-k", "test_differential_evolution"]) - # pytest.main(["./test_iopt_alg.py", "-k", "test_exponential_trigonometric_opt_alg"]) \ No newline at end of file + # pytest.main(["./test_iopt_alg.py", "-k", "test_differential_evolution"]) + # pytest.main(["./test_iopt_alg.py", "-k", "test_exponential_trigonometric_opt_alg"]) + pytest.main(["./test_iopt_alg.py", "-k", "test_differentialted_creative_search"]) \ No newline at end of file From 5d6411368656a6b8e5d452f17547dd2daf76db3a Mon Sep 17 00:00:00 2001 From: BenHBLiu Date: Sat, 23 Nov 2024 16:31:13 +0800 Subject: [PATCH 26/63] add DCS --- fealpy/opt/differentialted_creative_search.py | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 fealpy/opt/differentialted_creative_search.py diff --git a/fealpy/opt/differentialted_creative_search.py b/fealpy/opt/differentialted_creative_search.py new file mode 100644 index 000000000..6df481abe --- /dev/null +++ b/fealpy/opt/differentialted_creative_search.py @@ -0,0 +1,75 @@ +from ..backend import backend_manager as bm +from ..typing import TensorLike, Index, _S +from .. import logger + +from .optimizer_base import Optimizer + +""" +Differentialted Creative Search + +Reference: +~~~~~~~~~~ +Poomin Duankhan, Khamron Sunat, Sirapat Chiewchanwattana, Patchara Nasa-ngium. +The Differentiated Creative Search (DCS): Leveraging differentiated knowledge-acquisition and creative realism to address complex optimization problems. +Expert Systems with Applications, 2024, 252: 123734 +""" + +class DifferentialtedCreativeSearch(Optimizer): + def __init__(self, option) -> None: + super().__init__(option) + + + def run(self): + options = self.options + x = options["x0"] + N = options["NP"] + fit = self.fun(x)[:, None] + MaxIT = options["MaxIters"] + dim = options["ndim"] + lb, ub = options["domain"] + gbest_index = bm.argmin(fit) + gbest = x[gbest_index] + gbest_f = fit[gbest_index] + + # Parameters + golden_ratio = 2 / (1 + bm.sqrt(bm.array(5))) + ngS = int(bm.max(bm.array([6, bm.round(N * (golden_ratio / 3))]))) + pc = 0.5 + phi_qKR = 0.25 + 0.55 * ((0 + (bm.arange(0, N) / N)) ** 0.5)[:, None] + x_new = bm.zeros((N, dim)) + for it in range(MaxIT): + bestInd = 0 + lamda_t = 0.1 + (0.518 * ((1 - (it / MaxIT) ** 0.5))) + index = bm.argsort(fit, axis=0) + fit = bm.sort(fit, axis=0) + x = x[index[:, 0]] + + eta_qKR = (bm.round(bm.random.rand(N, 1) * phi_qKR) + 1 * (bm.random.rand(N, 1) <= phi_qKR)) / 2 + jrand = bm.floor(dim * bm.random.rand(N, 1)) + + r1 = bm.random.randint(0, N, (ngS,)) + aa = (bm.random.rand(ngS, dim) < eta_qKR[0 : ngS]) + (jrand[0 : ngS] == bm.arange(0, dim)) + R = bm.sin(0.5 * bm.pi * golden_ratio) * bm.tan(0.5 * bm.pi * (1 - golden_ratio * bm.random.rand(ngS, dim))) + Y = 0.05 * bm.sign(bm.random.rand(ngS, dim) - 0.5) * bm.log(bm.random.rand(ngS, dim) / bm.random.rand(ngS, dim)) * (R ** (1 / golden_ratio)) + x_new[0 : ngS] = (~aa * x[0 : ngS] + + aa * (x[r1[0 : ngS]] + Y)) + + r1 = bm.random.randint(0, N, (N - ngS - 1,)) + r2 = bm.random.randint(0, N - ngS, (N - ngS - 1,)) + ngS + aa = (bm.random.rand(N - ngS - 1, dim) < eta_qKR[ngS : N - 1]) + (jrand[ngS : N - 1] == bm.arange(0, dim)) + x_new[ngS : N - 1] = (~aa * x[ngS : N - 1] + + aa * (x[bestInd] + ((x[r2] - x[ngS : N - 1]) * lamda_t) + ((x[r1] - x[ngS : N - 1])) * bm.random.rand(N - ngS - 1, 1))) + + + + if bm.random.rand(1, 1) < pc: + x_new[N - 1] = lb + (ub - lb) * bm.random.rand(1, dim) + + x_new = x_new + (lb - x_new) * (x_new < lb) + (ub - x_new) * (x_new > ub) + fit_new = self.fun(x_new)[:, None] + mask = fit_new < fit + x, fit = bm.where(mask, x_new, x), bm.where(mask, fit_new, fit) + gbest_idx = bm.argmin(fit) + (gbest, gbest_f) = (x[gbest_idx], fit[gbest_idx]) if fit[gbest_idx] < gbest_f else (gbest, gbest_f) + + return gbest, gbest_f \ No newline at end of file From 7d6a10f019ec2cefa9977f22c8ff61d4dab1c551 Mon Sep 17 00:00:00 2001 From: BenHBLiu Date: Sat, 23 Nov 2024 16:31:42 +0800 Subject: [PATCH 27/63] update --- fealpy/opt/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fealpy/opt/__init__.py b/fealpy/opt/__init__.py index ec990f454..a06ebe799 100644 --- a/fealpy/opt/__init__.py +++ b/fealpy/opt/__init__.py @@ -19,4 +19,5 @@ from .cuckoo_search_opt import CuckooSearchOpt from .Butterfly_opt_alg import ButterflyOptAlg from .exponential_trigonometric_opt_alg import ExponentialTrigonometricOptAlg -from .differential_evolution import DifferentialEvolution \ No newline at end of file +from .differential_evolution import DifferentialEvolution +from .differentialted_creative_search import DifferentialtedCreativeSearch \ No newline at end of file From c0a9156555327b27bbbdba5424541d710552c6f1 Mon Sep 17 00:00:00 2001 From: concha Date: Sat, 23 Nov 2024 16:50:13 +0800 Subject: [PATCH 28/63] =?UTF-8?q?=E5=9B=9B=E8=BE=B9=E5=BD=A2=E7=BD=91?= =?UTF-8?q?=E6=A0=BC=EF=BC=9A=E5=8D=95=E4=BD=8D=E7=90=83=E9=9D=A2=E4=B8=8A?= =?UTF-8?q?=E5=9B=9B=E8=BE=B9=E5=BD=A2=E7=BD=91=E6=A0=BC=E7=B1=BB=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fealpy/mesh/quadrangle_mesh.py | 133 +++++++++++++++++++++------------ 1 file changed, 86 insertions(+), 47 deletions(-) diff --git a/fealpy/mesh/quadrangle_mesh.py b/fealpy/mesh/quadrangle_mesh.py index 952dc162a..7e9479875 100644 --- a/fealpy/mesh/quadrangle_mesh.py +++ b/fealpy/mesh/quadrangle_mesh.py @@ -8,7 +8,6 @@ from .mesh_base import TensorMesh from .plot import Plotable - from scipy.sparse import coo_matrix, csc_matrix, csr_matrix from scipy.sparse import spdiags, eye, tril, triu, bmat @@ -128,7 +127,7 @@ def jacobi_matrix(self, bc, index: Index = _S) -> TensorLike: J = bm.einsum('cim, ...in->...cmn', node[cell[:, [0, 3, 1, 2]]], gphi) return J - def first_fundamental_form(self, J:TensorLike) -> TensorLike: + def first_fundamental_form(self, J: TensorLike) -> TensorLike: """ @brief 由 Jacobi 矩阵计算第一基本形式。 """ @@ -163,7 +162,7 @@ def edge_frame(self, index: Index = _S): n = t @ w return n, t - def interpolation_points(self, p:int, index: Index = _S): + def interpolation_points(self, p: int, index: Index = _S): """ @brief Get all p-th order interpolation points on the quadrilateral mesh """ @@ -190,7 +189,7 @@ def interpolation_points(self, p:int, index: Index = _S): def number_of_corner_nodes(self): return self.number_of_nodes() - def cell_to_ipoint(self, p:int, index: Index = _S): + def cell_to_ipoint(self, p: int, index: Index = _S): """ @brief 获取单元上的双 p 次插值点 """ @@ -237,7 +236,7 @@ def cell_to_ipoint(self, p:int, index: Index = _S): flag = iflag & (edge2cell[:, 3] == 3) c2p = bm.set_at(c2p, (edge2cell[flag, 1], 0, slice(None)), e2p[flag]) - c2p = bm.set_at(c2p, (slice(None), slice(1, -1), slice(1, -1)), NN + NE * (p - 1) + + c2p = bm.set_at(c2p, (slice(None), slice(1, -1), slice(1, -1)), NN + NE * (p - 1) + bm.arange(NC * (p - 1) * (p - 1)).reshape(NC, p - 1, p - 1)) # if bm.backend_name in ["numpy", "pytorch"]: @@ -327,9 +326,9 @@ def angle(self) -> TensorLike: angle = bm.concatenate(angles, axis=-1) return angle - def cell_quality(self) -> TensorLike: + def cell_quality(self) -> TensorLike: jacobi = self.jacobi_at_corner() - return jacobi.sum(axis=1)/4 + return jacobi.sum(axis=1) / 4 def reorder_cell(self, idx): raise NotImplementedError @@ -358,26 +357,26 @@ def uniform_refine(self, n=1, surface=None, interface=None, returnim=False) -> ' edge2node = self.edge_to_node() cell2node = self.cell_to_node() if returnim is True: - nonzeros = NN+2*NE+4*NC - num_new_node = NN+NE+NC + nonzeros = NN + 2 * NE + 4 * NC + num_new_node = NN + NE + NC - data = bm.zeros(nonzeros,dtype=bm.float64) - indices = bm.zeros(nonzeros,dtype=bm.int32) - indptr = bm.zeros(num_new_node+1,dtype=bm.int32) + data = bm.zeros(nonzeros, dtype=bm.float64) + indices = bm.zeros(nonzeros, dtype=bm.int32) + indptr = bm.zeros(num_new_node + 1, dtype=bm.int32) - #赋值 + # 赋值 data[:NN] = 1 - data[NN:NN+2*NE] = 1/2 - data[NN+2*NE:] = 1/4 + data[NN:NN + 2 * NE] = 1 / 2 + data[NN + 2 * NE:] = 1 / 4 - indices[:NN] = bm.arange(NN) - indices[NN:NN+2*NE] = edge2node.flatten() - indices[NN+2*NE:] = cell2node.flatten() + indices[:NN] = bm.arange(NN) + indices[NN:NN + 2 * NE] = edge2node.flatten() + indices[NN + 2 * NE:] = cell2node.flatten() - indptr[:NN+1] = bm.arange(NN+1) - indptr[NN+1:NN+NE+1]=bm.arange(NN+2,NN+2*NE+1,step=2) - indptr[NN+NE+1:] = bm.arange(NN+2*NE+4,NN+2*NE+4*NC+1,step=4) - A = csr_matrix((data,indices,indptr),dtype=bm.float64) + indptr[:NN + 1] = bm.arange(NN + 1) + indptr[NN + 1:NN + NE + 1] = bm.arange(NN + 2, NN + 2 * NE + 1, step=2) + indptr[NN + NE + 1:] = bm.arange(NN + 2 * NE + 4, NN + 2 * NE + 4 * NC + 1, step=4) + A = csr_matrix((data, indices, indptr), dtype=bm.float64) IM.append(A) # Find the cutted edge @@ -394,20 +393,19 @@ def uniform_refine(self, n=1, surface=None, interface=None, returnim=False) -> ' cell = bm.zeros((4 * NC, 4), dtype=bm.int64, device=bm.get_device(cell2edge)) - cell = bm.set_at(cell, (slice(0, None, 4), slice(None)), - bm.concatenate([cp[0], ep[0], cc, ep[3]], axis=1)) - cell = bm.set_at(cell, (slice(1, None, 4), slice(None)), - bm.concatenate([ep[0], cp[1], ep[1], cc], axis=1)) + cell = bm.set_at(cell, (slice(0, None, 4), slice(None)), + bm.concatenate([cp[0], ep[0], cc, ep[3]], axis=1)) + cell = bm.set_at(cell, (slice(1, None, 4), slice(None)), + bm.concatenate([ep[0], cp[1], ep[1], cc], axis=1)) cell = bm.set_at(cell, (slice(2, None, 4), slice(None)), - bm.concatenate([cc, ep[1], cp[2], ep[2]], axis=1)) - cell = bm.set_at(cell, (slice(3, None, 4), slice(None)), - bm.concatenate([ep[3], cc, ep[2], cp[3]], axis=1)) + bm.concatenate([cc, ep[1], cp[2], ep[2]], axis=1)) + cell = bm.set_at(cell, (slice(3, None, 4), slice(None)), + bm.concatenate([ep[3], cc, ep[2], cp[3]], axis=1)) # cell[0::4, :] = bm.concatenate([cp[0], ep[0], cc, ep[3]], axis=1) # cell[1::4, :] = bm.concatenate([ep[0], cp[1], ep[1], cc], axis=1) # cell[2::4, :] = bm.concatenate([cc, ep[1], cp[2], ep[2]], axis=1) # cell[3::4, :] = bm.concatenate([ep[3], cc, ep[2], cp[3]], axis=1) - # if bm.backend_name in ["numpy", "pytorch"]: # cell[0::4, :] = bm.concatenate([cp[0], ep[0], cc, ep[3]], axis=1) # cell[1::4, :] = bm.concatenate([ep[0], cp[1], ep[1], cc], axis=1) @@ -426,10 +424,10 @@ def uniform_refine(self, n=1, surface=None, interface=None, returnim=False) -> ' self.node = bm.concatenate([self.node, edgeCenter, cellCenter], axis=0) self.cell = cell - + self.construct() if returnim is True: - return IM + return IM def vtk_cell_type(self, etype='cell'): if etype in {'cell', 2}: @@ -439,9 +437,9 @@ def vtk_cell_type(self, etype='cell'): VTK_LINE = 3 return VTK_LINE - def to_vtk(self, fname=None, etype='cell', index: Index=_S): - - from fealpy.mesh.vtk_extent import write_to_vtu + def to_vtk(self, fname=None, etype='cell', index: Index = _S): + + from fealpy.mesh.vtk_extent import write_to_vtu node = self.entity('node') GD = self.GD @@ -461,8 +459,8 @@ def to_vtk(self, fname=None, etype='cell', index: Index=_S): else: print("Writting to vtk...") write_to_vtu(fname, node, NC, cellType, cell.flatten(), - nodedata=self.nodedata, - celldata=self.celldata) + nodedata=self.nodedata, + celldata=self.celldata) def show_function(self, plot, uh, cmap=None): """ @@ -481,14 +479,14 @@ def show_function(self, plot, uh, cmap=None): node = self.node cax = axes.plot_trisurf( - node[:, 0], node[:, 1], - uh, cmap=cmap, lw=0.0) + node[:, 0], node[:, 1], + uh, cmap=cmap, lw=0.0) axes.figure.colorbar(cax, ax=axes) return axes @classmethod - def from_box(cls, box=[0, 1, 0, 1], nx=10, ny=10, - threshold:Optional[Callable]=None, device:str=None) -> 'QuadrangleMesh': + def from_box(cls, box=[0, 1, 0, 1], nx=10, ny=10, + threshold: Optional[Callable] = None, device: str = None) -> 'QuadrangleMesh': """ Generate a quadrilateral mesh for a rectangular domain. """ @@ -520,7 +518,7 @@ def from_box(cls, box=[0, 1, 0, 1], nx=10, ny=10, return cls(node, cell) @classmethod - def from_unit_square(cls, nx=10, ny=10, threshold:Optional[Callable]=None) -> 'QuadrangleMesh': + def from_unit_square(cls, nx=10, ny=10, threshold: Optional[Callable] = None) -> 'QuadrangleMesh': """ Generate a quadrilateral mesh for a unit square. @@ -554,7 +552,7 @@ def from_polygon_gmsh(cls, vertices: List[tuple], h: float) -> 'QuadrangleMesh': # 添加线段和循环 lines = [] for i in range(len(polygon_points)): - line = gmsh.model.geo.addLine(polygon_points[i], polygon_points[(i+1) % len(polygon_points)]) + line = gmsh.model.geo.addLine(polygon_points[i], polygon_points[(i + 1) % len(polygon_points)]) lines.append(line) curve_loop = gmsh.model.geo.addCurveLoop(lines) @@ -609,7 +607,7 @@ def from_polygon_gmsh(cls, vertices: List[tuple], h: float) -> 'QuadrangleMesh': return cls(node, cell) @classmethod - def from_fuel_rod_gmsh(cls,R1,R2,L,w,h,meshtype='normal'): + def from_fuel_rod_gmsh(cls, R1, R2, L, w, h, meshtype='normal'): raise NotImplementedError @classmethod @@ -641,7 +639,7 @@ def from_one_quadrangle(cls, meshtype='square') -> 'QuadrangleMesh': [0.5, math.sqrt(3) / 2]], dtype=bm.float64) cell = bm.tensor([[0, 1, 2, 3]], dtype=bm.int64) return cls(node, cell) - + @classmethod def from_square_domain_with_fracture(cls): node = bm.tensor([ @@ -683,7 +681,7 @@ def from_triangle_mesh(cls, mesh) -> 'QuadrangleMesh': node = bm.concatenate([node0, ec, cc], axis=0) idx = bm.arange(NC) - cell1 = bm.concatenate([(NN+NE+idx).reshape(-1, 1), + cell1 = bm.concatenate([(NN + NE + idx).reshape(-1, 1), (cell2edge[:, 0] + NN).reshape(-1, 1), (cell0[:, 2]).reshape(-1, 1), (cell2edge[:, 1] + NN).reshape(-1, 1)], axis=1) @@ -699,7 +697,6 @@ def from_triangle_mesh(cls, mesh) -> 'QuadrangleMesh': return cls(node, cell) - @classmethod def polygon_domain_generator(cls, num_vertices=20, radius=1.0, center=[0.0, 0.0]): raise NotImplementedError @@ -826,5 +823,47 @@ def sub_domain_mesh_generator(cls, half_edge, origin_node, separator_streamlines return quad_mesh + @classmethod + def from_unit_sphere_surface(cls, refine=3, itype=None, ftype=None, device=None): + if itype is None: + itype = bm.int32 + if ftype is None: + ftype = bm.float64 + + def project_to_sphere(nodes): + norms = bm.sqrt(bm.sum(nodes**2, axis=1, keepdims=True)) + return nodes / norms + + origin_node = bm.array([ + [-1, -1, -1], + [1, -1, -1], + [1, 1, -1], + [-1, 1, -1], + [-1, -1, 1], + [1, -1, 1], + [1, 1, 1], + [-1, 1, 1] + ], dtype=ftype, device=device) + origin_cell = bm.array( + [ + [0, 3, 2, 1], + [4, 5, 6, 7], + [3, 7, 6, 2], + [0, 1, 5, 4], + [0, 4, 7, 3], + [1, 2, 6, 5] + ], dtype=itype, device=device) + origin_node = project_to_sphere(origin_node) + quad_mesh = cls(origin_node, origin_cell) + + for i in range(refine): + quad_mesh.uniform_refine() + refine_node = quad_mesh.node + refine_node = project_to_sphere(refine_node) + refine_cell = quad_mesh.cell + quad_mesh = cls(refine_node, refine_cell) + + return quad_mesh + QuadrangleMesh.set_ploter('2d') From f18397f8625c9bb6d1d26466e527e3cb7b43c524 Mon Sep 17 00:00:00 2001 From: "brighthe98@gmail.com" Date: Sat, 23 Nov 2024 20:46:41 +0800 Subject: [PATCH 29/63] update --- app/soptx/soptx/filter/filter.py | 2 +- app/soptx/soptx/material/elastic.py | 8 +- .../soptx/material/interpolation_scheme.py | 29 +-- .../soptx/material/material_properties.py | 173 ------------------ app/soptx/soptx/opt/compliance.py | 30 ++- app/soptx/soptx/solver/__init__.py | 7 +- app/soptx/soptx/solver/elastic_fem_solver.py | 107 ++++++++++- app/soptx/soptx/tests/test_compliance.py | 146 ++++++++++----- app/soptx/soptx/utils/__init__.py | 5 + app/soptx/soptx/utils/filter_parameters.py | 144 --------------- fealpy/fem/linear_elastic_integrator.py | 31 +--- 11 files changed, 255 insertions(+), 427 deletions(-) delete mode 100644 app/soptx/soptx/material/material_properties.py delete mode 100644 app/soptx/soptx/utils/filter_parameters.py diff --git a/app/soptx/soptx/filter/filter.py b/app/soptx/soptx/filter/filter.py index 8019e2615..c770fa2ae 100644 --- a/app/soptx/soptx/filter/filter.py +++ b/app/soptx/soptx/filter/filter.py @@ -134,7 +134,7 @@ def filter_sensitivity(self, # 应用滤波矩阵 filtered_gradient = self._H.matmul(weighted_gradient) # 计算修正因子 - correction_factor = self._Hs * bm.maximum(0.001, design_vars) + correction_factor = self._Hs * bm.maximum(bm.tensor(0.001, dtype=bm.float64), design_vars) # 返回最终修改的灵敏度 modified_gradient = filtered_gradient / correction_factor diff --git a/app/soptx/soptx/material/elastic.py b/app/soptx/soptx/material/elastic.py index fc49dcf63..833fc2baa 100644 --- a/app/soptx/soptx/material/elastic.py +++ b/app/soptx/soptx/material/elastic.py @@ -35,7 +35,13 @@ def elastic_modulus(self) -> TensorLike: def elastic_matrix(self, bcs: Optional[TensorLike] = None) -> TensorLike: """计算弹性矩阵""" base_D = super().elastic_matrix() - D = self._E[:, None, None, None] * base_D + + # 处理不同类型的张量 + if len(self._E.shape) > 0: + D = bm.einsum('b, ijkl -> bjkl', self._E, base_D) + else: + D = self._E * base_D + return D class ElasticMaterialProperties: diff --git a/app/soptx/soptx/material/interpolation_scheme.py b/app/soptx/soptx/material/interpolation_scheme.py index 3cf2faf1a..b4a25dd92 100644 --- a/app/soptx/soptx/material/interpolation_scheme.py +++ b/app/soptx/soptx/material/interpolation_scheme.py @@ -14,15 +14,6 @@ def __init__(self, penalty_factor: float = 3.0): self.penalty_factor = penalty_factor - def _validate_inputs(self, rho: TensorLike, P0: float, Pmin: float = None): - """Validate input parameters.""" - if bm.any(rho < 0) or bm.any(rho > 1): - raise ValueError("Density values must be between 0 and 1") - if P0 <= 0: - raise ValueError("P0 must be positive") - if Pmin is not None and Pmin < 0: - raise ValueError("Pmin must be non-negative") - def calculate_property(self, rho: TensorLike, P0: float, @@ -30,12 +21,11 @@ def calculate_property(self, penalty_factor: float ) -> TensorLike: """Calculate interpolated property using SIMP model.""" - self._validate_inputs(rho, P0, Pmin) if Pmin is None: - P = rho[:] ** penalty_factor * P0 + P = rho ** penalty_factor * P0 else: - P = Pmin + rho[:] ** penalty_factor * (P0 - Pmin) + P = Pmin + rho ** penalty_factor * (P0 - Pmin) return P def calculate_property_derivative(self, @@ -45,13 +35,12 @@ def calculate_property_derivative(self, penalty_factor: float ) -> TensorLike: """Calculate derivative of interpolated property using SIMP model.""" - self._validate_inputs(rho, P0, Pmin) if Pmin is None: - dP = penalty_factor * rho[:] ** (penalty_factor - 1) * P0 + dP = penalty_factor * rho ** (penalty_factor - 1) * P0 return dP else: - dP = penalty_factor * rho[:] ** (penalty_factor - 1) * (P0 - Pmin) + dP = penalty_factor * rho ** (penalty_factor - 1) * (P0 - Pmin) return dP class RAMPInterpolation(MaterialInterpolation): @@ -66,14 +55,6 @@ def __init__(self, penalty_factor: float = 3.0): self.penalty_factor = penalty_factor - def _validate_inputs(self, rho: TensorLike, P0: float, Pmin: float = None): - if bm.any(rho < 0) or bm.any(rho > 1): - raise ValueError("Density values must be between 0 and 1") - if P0 <= 0: - raise ValueError("P0 must be positive") - if Pmin is not None and Pmin < 0: - raise ValueError("Pmin must be non-negative") - def calculate_property(self, rho: TensorLike, P0: float, @@ -81,7 +62,6 @@ def calculate_property(self, penalty_factor: float ) -> TensorLike: """Calculate interpolated property using 'RAMP' model.""" - self._validate_inputs(rho, P0, Pmin) if Pmin is None: P = rho * (1 + penalty_factor * (1 - rho)) ** (-1) * P0 @@ -96,7 +76,6 @@ def calculate_property_derivative(self, penalty_factor: float ) -> TensorLike: """Calculate derivative of interpolated property using 'RAMP' model.""" - self._validate_inputs(rho, P0, Pmin) if Pmin is None: return P0 * (1 + penalty_factor) * (1 + penalty_factor * (1 - rho)) ** (-2) diff --git a/app/soptx/soptx/material/material_properties.py b/app/soptx/soptx/material/material_properties.py deleted file mode 100644 index 3e87a9b96..000000000 --- a/app/soptx/soptx/material/material_properties.py +++ /dev/null @@ -1,173 +0,0 @@ -from fealpy.backend import backend_manager as bm -from fealpy.typing import TensorLike -from fealpy.material.elastic_material import LinearElasticMaterial - -from builtins import float, int, str -from typing import Optional, Literal -from dataclasses import dataclass -from abc import ABC, abstractmethod - -class MaterialInterpolation(ABC): - def __init__(self, name: str): - """ - Initialize the material interpolation model. - """ - self.name = name - - @abstractmethod - def calculate_property(self, - rho: TensorLike, - P0: float, Pmin: float, - penal: float) -> TensorLike: - pass - - @abstractmethod - def calculate_property_derivative(self, - rho: TensorLike, - P0: float, Pmin: float, - penal: float) -> TensorLike: - pass - -@dataclass -class ElasticMaterialConfig: - elastic_modulus: float = 1.0 - minimal_modulus: float = 1e-9 - poisson_ratio: float = 0.3 - plane_assumption: Literal["plane_stress", "plane_strain", "3d"] = "plane_stress" - -class ElasticMaterialProperties(LinearElasticMaterial): - def __init__(self, - config: ElasticMaterialConfig, - rho: TensorLike, - interpolation_model: MaterialInterpolation): - if not isinstance(config, ElasticMaterialConfig): - raise TypeError("'config' must be an instance of ElasticMaterialConfig") - if rho is None or not isinstance(rho, TensorLike): - raise TypeError("'rho' must be of type TensorLike and cannot be None") - if interpolation_model is None or not isinstance(interpolation_model, MaterialInterpolation): - raise TypeError("'interpolation_model' must be an instance of MaterialInterpolation and cannot be None") - - super().__init__(name="ElasticMaterialProperties", - elastic_modulus=config.elastic_modulus, - poisson_ratio=config.poisson_ratio, - hypo=config.plane_assumption) - """ - Initialize material properties. - - This class inherits from "LinearElasticMaterial" and adds material interpolation models - for topology optimization. - """ - self.config = config - self.rho = rho - self.interpolation_model = interpolation_model if interpolation_model else SIMPInterpolation() - - def material_model(self) -> TensorLike: - """ - Use the interpolation model to calculate Young's modulus. - """ - rho = self.rho - E0 = self.config.elastic_modulus - Emin = self.config.minimal_modulus - penalty_factor = self.interpolation_model.penalty_factor - - E = self.interpolation_model.calculate_property(rho, - E0, Emin, - penalty_factor) - return E - - def material_model_derivative(self) -> TensorLike: - """ - Use the interpolation model to calculate the derivative of Young's modulus. - """ - rho = self.rho - E0 = self.config.elastic_modulus - Emin = self.config.minimal_modulus - penalty_factor = self.interpolation_model.penalty_factor - - dE = self.interpolation_model.calculate_property_derivative(rho, - E0, Emin, - penalty_factor) - return dE - - def elastic_matrix(self, bcs: Optional[TensorLike] = None) -> TensorLike: - """ - Calculate the elastic matrix D for each element based on the density distribution. - - Returns: - TensorLike: A tensor representing the elastic matrix D for each element. - Shape: (NC, 1, 3, 3) for 2D problems. - Shape: (NC, 1, 6, 6) for 3D problems. - """ - if self.rho is None: - raise ValueError("Density rho must be set for MaterialProperties.") - - E = self.material_model() - base_D = super().elastic_matrix() - D = E[:, None, None, None] * base_D - return D - -class ThermalMaterialProperties: - def __init__(self, k0: float = 1.0, kmin: float = 1e-9, - penal: int = 3, rho: Optional[TensorLike] = None, - interpolation_model: MaterialInterpolation = None): - """ - Initialize thermal material properties for topology optimization. - - Args: - k0 (float): Thermal conductivity of the solid material. - kmin (float): Thermal conductivity of the void or empty space. - penal (int): Penalization factor to control thermal conductivity interpolation. - rho (Optional[TensorLike]): Density distribution of the material (default is None). - interpolation_model (MaterialInterpolation): Material interpolation model, - default is SIMP interpolation. - """ - self.k0 = k0 - self.kmin = kmin - self.penal = penal - self.rho = rho - self.interpolation_model = interpolation_model if interpolation_model else SIMPInterpolation() - - def thermal_conductivity(self) -> TensorLike: - """ - Use the interpolation model to calculate the effective thermal conductivity. - """ - return self.interpolation_model.calculate_property(self.rho, - self.k0, self.kmin, self.penal) - - def thermal_conductivity_derivative(self) -> TensorLike: - """ - Use the interpolation model to calculate the derivative of the thermal conductivity. - """ - return self.interpolation_model.calculate_property_derivative(self.rho, - self.k0, self.kmin, self.penal) - -class SIMPInterpolation(MaterialInterpolation): - def __init__(self, penalty_factor: float = 3.0): - super().__init__(name="SIMP") - self.penalty_factor = penalty_factor - - def calculate_property(self, - rho: TensorLike, - P0: float, Pmin: float, - penalty_factor: float) -> TensorLike: - """ - Calculate the interpolated property using the 'SIMP' model. - """ - if Pmin is None: - P = rho[:] ** penalty_factor * P0 - return P - else: - P = Pmin + rho[:] ** penalty_factor * (P0 - Pmin) - return P - - def calculate_property_derivative(self, - rho: TensorLike, - P0: float, Pmin: float, - penalty_factor: float) -> TensorLike: - """ - Calculate the derivative of the interpolated property using the 'SIMP' model. - """ - if Pmin is None: - return penalty_factor * rho[:] ** (penalty_factor - 1) * P0 - else: - return penalty_factor * rho[:] ** (penalty_factor - 1) * (P0 - Pmin) diff --git a/app/soptx/soptx/opt/compliance.py b/app/soptx/soptx/opt/compliance.py index e59882799..4189f71e3 100644 --- a/app/soptx/soptx/opt/compliance.py +++ b/app/soptx/soptx/opt/compliance.py @@ -1,11 +1,19 @@ from fealpy.backend import backend_manager as bm -from fealpy.typing import TensorLike +from fealpy.typing import TensorLike, Literal + from typing import Dict, Optional, Any +from dataclasses import dataclass from soptx.material import ElasticMaterialProperties from soptx.solver import ElasticFEMSolver from soptx.opt import ObjectiveBase from soptx.filter import Filter +from soptx.utils import timer + +@dataclass +class ComplianceConfig: + """Configuration for compliance objective computation""" + diff_mode: Literal["auto", "manual"] = "manual" # 微分模式选择 class ComplianceObjective(ObjectiveBase): """结构柔度最小化目标函数 @@ -66,8 +74,6 @@ def _update_u(self, rho: TensorLike) -> TensorLike: # 更新求解器中的密度并求解 self.solver.update_density(rho) self._current_u = self.solver.solve().displacement - # self._current_u = self.solver.solve_cg().displacement - # self._current_rho = bm.copy(rho) # 这里的 copy 导致内部状态与外部不同步 self._current_rho = rho # 直接引用,内部状态会随外部更新 return self._current_u @@ -143,7 +149,18 @@ def jac(self, ------- dc : 目标函数对密度的梯度 """ + # 创建计时器 + t = timer("Grad Timing") + next(t) # 启动计时器 + # 获取位移场 + dc_func = bm.vmap(bm.jacfwd(func=self.fun)) # 输入比输出少 + dc_value = dc_func(rho) + t.send('auto grad') + print("dc_func:", dc_func) + print("dc_auto:", dc_value) + # dc = bm.jacrev(func=self.fun) # 输入比输出多 + if u is None: u = self._update_u(rho) @@ -155,6 +172,13 @@ def jac(self, # 计算梯度 dE = self.material_properties.calculate_elastic_modulus_derivative(rho) dc = -bm.einsum('c, c -> c', dE, ce) + print("dc_manual:", dc) + t.send('manual grad') + + # 结束计时 + t.send(None) + + # 应用滤波 if self.filter is None: diff --git a/app/soptx/soptx/solver/__init__.py b/app/soptx/soptx/solver/__init__.py index cefd80413..8ff71e531 100644 --- a/app/soptx/soptx/solver/__init__.py +++ b/app/soptx/soptx/solver/__init__.py @@ -1,8 +1,11 @@ from .elastic_fem_solver import (ElasticFEMSolver, - IterativeSolverResult, DirectSolverResult) + IterativeSolverResult, DirectSolverResult, + AssemblyMethod, AssemblyConfig) __all__ = [ 'ElasticFEMSolver', 'IterativeSolverResult', - 'DirectSolverResult' + 'DirectSolverResult', + 'AssemblyMethod', + 'AssemblyConfig', ] \ No newline at end of file diff --git a/app/soptx/soptx/solver/elastic_fem_solver.py b/app/soptx/soptx/solver/elastic_fem_solver.py index f2052904b..e0e343eb5 100644 --- a/app/soptx/soptx/solver/elastic_fem_solver.py +++ b/app/soptx/soptx/solver/elastic_fem_solver.py @@ -1,5 +1,6 @@ from dataclasses import dataclass from typing import Optional +from enum import Enum, auto from fealpy.backend import backend_manager as bm from fealpy.typing import TensorLike, Union @@ -9,6 +10,7 @@ from fealpy.solver import cg, spsolve from soptx.material import ElasticMaterialInstance, ElasticMaterialProperties +from soptx.utils import timer @dataclass class IterativeSolverResult: @@ -20,6 +22,19 @@ class DirectSolverResult: """直接求解器的结果""" displacement: TensorLike +class AssemblyMethod(Enum): + """矩阵组装方法的枚举类""" + STANDARD = auto() # 标准组装方法 + FAST_STRAIN = auto() # 应变快速组装 + FAST_STRESS = auto() # 应力快速组装 + FAST_3D = auto() # 3D 快速组装 + +@dataclass +class AssemblyConfig: + """矩阵组装的配置类""" + method: AssemblyMethod = AssemblyMethod.STANDARD + quadrature_degree_increase: int = 3 + class ElasticFEMSolver: """专门用于求解线弹性问题的有限元求解器 @@ -34,6 +49,7 @@ def __init__(self, material_properties: ElasticMaterialProperties, tensor_space: TensorFunctionSpace, pde, + assembly_config: Optional[AssemblyConfig] = None, solver_type: str = 'cg', solver_params: Optional[dict] = None): """ @@ -42,6 +58,7 @@ def __init__(self, material_properties : 材料属性计算器 tensor_space : 张量函数空间 pde : 包含荷载和边界条件的PDE模型 + assembly_config : 矩阵组装配置 solver_type : 求解器类型, 'cg' 或 'direct' solver_params : 求解器参数 cg: maxiter, atol, rtol @@ -50,6 +67,7 @@ def __init__(self, self.material_properties = material_properties self.tensor_space = tensor_space self.pde = pde + self.assembly_config = assembly_config or AssemblyConfig() self.solver_type = solver_type self.solver_params = solver_params or {} @@ -125,30 +143,77 @@ def compute_local_stiffness_matrix(self) -> TensorLike: if self._current_material is None: raise ValueError("Material not initialized. Call update_density first.") - integrator = LinearElasticIntegrator( - material=self._current_material, - q=self.tensor_space.p + 3 - ) + # 创建适当的积分器 + integrator = self._create_integrator() - KE = integrator.assembly(space=self.tensor_space) + # 根据 assembly_config.method 选择对应的组装函数 + method_map = { + AssemblyMethod.STANDARD: integrator.assembly, + AssemblyMethod.FAST_STRAIN: integrator.fast_assembly_strain, + AssemblyMethod.FAST_STRESS: integrator.fast_assembly_stress, + AssemblyMethod.FAST_3D: integrator.fast_assembly + } + + try: + assembly_func = method_map[self.assembly_config.method] + except KeyError: + raise RuntimeError(f"Unsupported assembly method: {self.assembly_config.method}") + + # 调用选定的组装函数 + KE = assembly_func(space=self.tensor_space) return KE #--------------------------------------------------------------------------- # 内部方法:组装和边界条件 #--------------------------------------------------------------------------- + def _create_integrator(self) -> LinearElasticIntegrator: + """创建适当的积分器实例 + + 根据配置创建对应的积分器,并设置正确的积分方法 + """ + # 确定积分方法 + method_map = { + AssemblyMethod.STANDARD: 'assembly', + AssemblyMethod.FAST_STRAIN: 'fast_strain', + AssemblyMethod.FAST_STRESS: 'fast_stress', + AssemblyMethod.FAST_3D: 'fast_3d' + } + + method = method_map[self.assembly_config.method] + + # 创建积分器 + q = self.tensor_space.p + self.assembly_config.quadrature_degree_increase + + integrator = LinearElasticIntegrator( + material=self._current_material, + q=q, + method=method + ) + + return integrator + + def _assemble_global_stiffness_matrix(self) -> CSRTensor: """组装全局刚度矩阵""" if self._current_material is None: raise ValueError("Material not initialized. Call update_density first.") + + # 创建计时器 + # t = timer("Assemble Timing") + # next(t) # 启动计时器 - integrator = LinearElasticIntegrator( - material=self._current_material, - q=self.tensor_space.p + 3 - ) + # 创建适当的积分器 + integrator = self._create_integrator() + bform = BilinearForm(self.tensor_space) bform.add_integrator(integrator) + # t.send('Local Assembly') K = bform.assembly(format='csr') + # t.send('Global Assembly') + + # 结束计时 + # t.send(None) self._global_stiffness_matrix = K return K @@ -157,6 +222,13 @@ def _assemble_global_force_vector(self) -> TensorLike: """组装全局载荷向量""" force = self.pde.force F = self.tensor_space.interpolate(force) + + # # 让 F 依赖于当前密度 + # if self._current_density is not None: + # # 创建一个非常小的系数,使 F 依赖于密度但几乎不改变其值 + # epsilon = 1e-30 + # F = F * (1.0 + epsilon * bm.sum(self._current_density)) + self._global_force_vector = F return F @@ -181,7 +253,7 @@ def _apply_boundary_conditions(self, K: CSRTensor, F: TensorLike) -> tuple[CSRTe dtype=bm.float64, device=bm.get_device(self.tensor_space)) isBdDof = self.tensor_space.is_boundary_dof(threshold=threshold, method='interp') - + F = F - K.matmul(uh_bd) F[isBdDof] = uh_bd[isBdDof] @@ -224,20 +296,35 @@ def solve_cg(self, """ if self._current_density is None: raise ValueError("Density not set. Call update_density first.") + + # 创建计时器 + t = timer("CG Solver Timing") + next(t) # 启动计时器 K0 = self._assemble_global_stiffness_matrix() + t.send('Stiffness Matrix Assembly') + F0 = self._assemble_global_force_vector() + t.send('Force Vector Assembly') + K, F = self._apply_boundary_conditions(K0, F0) + t.send('Boundary Conditions') uh = self.tensor_space.function() + try: # logger.setLevel('INFO') # uh[:], info = cg(K, F[:], x0=x0, maxiter=maxiter, atol=atol, rtol=rtol) # TODO 目前 FEALPy 中的 cg 只能通过 logger 获取迭代步数,无法直接返回 uh[:] = cg(K, F[:], x0=x0, maxiter=maxiter, atol=atol, rtol=rtol) + t.send('Solving Phase') # 记录求解阶段时间 + + # 结束计时 + t.send(None) except Exception as e: raise RuntimeError(f"CG solver failed: {str(e)}") + return IterativeSolverResult(displacement=uh) def solve_direct(self, solver_type: str = 'mumps') -> DirectSolverResult: diff --git a/app/soptx/soptx/tests/test_compliance.py b/app/soptx/soptx/tests/test_compliance.py index f0bc36247..888a0b23a 100644 --- a/app/soptx/soptx/tests/test_compliance.py +++ b/app/soptx/soptx/tests/test_compliance.py @@ -1,11 +1,11 @@ """Test cases for compliance objective and volume constraint.""" from dataclasses import dataclass +import dataclasses from typing import Literal, Optional, Union, Dict, Any -from pathlib import Path from fealpy.backend import backend_manager as bm -from fealpy.mesh import UniformMesh2d, UniformMesh3d +from fealpy.mesh import UniformMesh2d, UniformMesh3d, TriangleMesh from fealpy.functionspace import LagrangeFESpace, TensorFunctionSpace from soptx.material import ( @@ -14,10 +14,13 @@ SIMPInterpolation ) from soptx.pde import MBBBeam2dData1, Cantilever2dData1, Cantilever3dData1 -from soptx.solver import ElasticFEMSolver +from soptx.solver import ElasticFEMSolver, AssemblyMethod, AssemblyConfig from soptx.opt import ComplianceObjective, VolumeConstraint from soptx.filter import Filter, FilterConfig +bm.set_backend('pytorch') + +mesh = TriangleMesh.from_box(box=[0,1,0,1], nx=10, ny=10) @dataclass class TestConfig: """Configuration for topology optimization test cases.""" @@ -41,6 +44,11 @@ class TestConfig: initial_lambda: float = 1e9 bisection_tol: float = 1e-3 + assembly_method: AssemblyMethod = AssemblyMethod.STANDARD # 矩阵组装方法 + quadrature_degree_increase: int = 3 # 积分阶数增量 + solver_type: Literal['cg', 'direct'] = 'cg' # 求解器类型 + solver_params: Optional[Dict[str, Any]] = None # 求解器参数 + def create_base_components(config: TestConfig): """Create basic components needed for topology optimization based on configuration.""" # Create mesh and determine dimensionality @@ -63,6 +71,8 @@ def create_base_components(config: TestConfig): ipoints_ordering='yx', flip_direction='y', device='cpu' ) + # mesh = TriangleMesh.from_box(box=[0, config.nx*h[0], 0, config.ny*h[1]], + # nx=config.nx, ny=config.ny, device='cpu') dimension = 2 # Create function spaces @@ -95,26 +105,32 @@ def create_base_components(config: TestConfig): xmin=0, xmax=config.nx*h[0], ymin=0, ymax=config.ny*h[1] ) - else: # cantilever_3d + elif config.problem_type == 'cantilever_3d': pde = Cantilever3dData1( xmin=0, xmax=config.nx*h[0], ymin=0, ymax=config.ny*h[1], zmin=0, zmax=config.nz*h[2] ) - - # Create solver - # solver = ElasticFEMSolver( - # material_properties=material_properties, - # tensor_space=tensor_space_C, - # pde=pde - # ) + + assembly_config = AssemblyConfig( + method=config.assembly_method, + quadrature_degree_increase=config.quadrature_degree_increase + ) + + # 设置默认的求解器参数 + default_solver_params = { + 'cg': {'maxiter': 5000, 'atol': 1e-12, 'rtol': 1e-12}, + 'direct': {'solver_type': 'mumps'} + } + solver_params = config.solver_params or default_solver_params[config.solver_type] solver = ElasticFEMSolver( material_properties=material_properties, tensor_space=tensor_space_C, pde=pde, - solver_type='cg', # 添加默认求解器类型 - solver_params={'maxiter': 5000, 'atol': 1e-12, 'rtol': 1e-12} # 添加求解器参数 + assembly_config=assembly_config, + solver_type=config.solver_type, # 添加默认求解器类型 + solver_params=solver_params # 添加求解器参数 ) # Initialize density field @@ -123,6 +139,35 @@ def create_base_components(config: TestConfig): return mesh, space_D, material_properties, solver, rho +def test_solver(config: TestConfig): + """测试不同矩阵组装方法的 solver.""" + print(f"\n=== Testing Solver with {config.assembly_method} ===") + + # Create base components + mesh, space_D, material_properties, solver, rho = create_base_components(config) + + # Test solver + solver.update_density(rho[:]) + solver_result = solver.solve_cg() + displacement = solver_result.displacement + print(f"\nSolver information:") + print(f"- Displacement shape: {displacement.shape}:\n {displacement[:]}") + + # base_local_K = solver.get_base_local_stiffness_matrix() + # print("\n=== 基础局部刚度矩阵信息 ===") + # print(f"基础局部刚度矩阵 - {base_local_K.shape}:\n {base_local_K[0]}") + # local_K = solver.compute_local_stiffness_matrix() + # print("\n=== 当前材料局部刚度矩阵 ===") + # print(f"局部刚度矩阵 - {local_K.shape}:\n {local_K.round(4)}") + + # K = solver.get_global_stiffness_matrix() + # F = solver.get_global_force_vector() + # print("\n=== 全局矩阵和载荷向量信息 ===") + # print(f"全局刚度矩阵 - {K.shape}:\n {K.to_dense().round(4)}") + # print(f"全局刚度矩阵最大值: {bm.max(bm.abs(K.to_dense()))}") + # print(f"全局载荷向量 - {F.shape}:\n {F[:]}") + + def test_compliance_objective(config: TestConfig): """Test compliance objective computation and sensitivity analysis.""" print(f"\n=== Testing Compliance Objective with {config.filter_type} filter ===") @@ -136,10 +181,6 @@ def test_compliance_objective(config: TestConfig): displacement = solver_result.displacement print(f"\nSolver information:") print(f"- Displacement shape: {displacement.shape}:\n {displacement[:]}") - - # 获取求解后的全局矩阵和向量 - K = solver.get_global_stiffness_matrix() - F = solver.get_global_force_vector() # Create filter filter_config = FilterConfig( @@ -161,17 +202,17 @@ def test_compliance_objective(config: TestConfig): filter=filter_obj ) - # Test objective function - obj_value = objective.fun(rho=rho[:], u=displacement) - print(f"Objective function value: {obj_value:.6e}") + # # Test objective function + # obj_value = objective.fun(rho=rho[:], u=displacement) + # print(f"Objective function value: {obj_value:.6e}") - # Test element compliance - ce = objective.get_element_compliance() - print(f"\nElement compliance information:") - print(f"- Shape: {ce.shape}:\n {ce}") - print(f"- Min: {bm.min(ce):.6e}") - print(f"- Max: {bm.max(ce):.6e}") - print(f"- Mean: {bm.mean(ce):.6e}") + # # Test element compliance + # ce = objective.get_element_compliance() + # print(f"\nElement compliance information:") + # print(f"- Shape: {ce.shape}:\n {ce}") + # print(f"- Min: {bm.min(ce):.6e}") + # print(f"- Max: {bm.max(ce):.6e}") + # print(f"- Mean: {bm.mean(ce):.6e}") # Test sensitivity dce = objective.jac(rho=rho[:], u=displacement) @@ -201,32 +242,47 @@ def test_compliance_objective(config: TestConfig): } if __name__ == "__main__": - # # Test 3D case with density filter - # config_3d = TestConfig( - # problem_type='cantilever_3d', - # nx=60, ny=20, nz=4, - # volume_fraction=0.3, - # filter_radius=1.5, - # filter_type='density' - # ) + # Test 3D case with density filter + config_3d = TestConfig( + problem_type='cantilever_3d', + nx=60, ny=20, nz=4, + volume_fraction=0.3, + filter_radius=1.5, + filter_type='density' + ) # results_3d = test_compliance_objective(config_3d) - # # Test 2D case with sensitivity filter - # config_2d = TestConfig( - # problem_type='mbb_2d', - # nx=60, ny=20, - # volume_fraction=0.5, - # filter_radius=2.4, - # filter_type='sensitivity' - # ) + # Test 2D case with sensitivity filter + config_2d = TestConfig( + problem_type='mbb_2d', + nx=60, ny=20, + volume_fraction=0.5, + filter_radius=2.4, + filter_type='sensitivity' + ) # results_2d = test_compliance_objective(config_2d) # Test 2D case with sensitivity filter config_cantilever_2d = TestConfig( problem_type='cantilever_2d', - nx=4, ny=3, + # nx=160, ny=100, + nx=8, ny=5, volume_fraction=0.4, filter_radius=6, filter_type='sensitivity' ) - results_cantilever_2d = test_compliance_objective(config_cantilever_2d) \ No newline at end of file + # # 测试标准组装方法 + # config_standard = dataclasses.replace( + # config_cantilever_2d, + # assembly_method=AssemblyMethod.STANDARD + # ) + # results_standard = test_solver(config_standard) + + # # 测试快速应力组装方法 + # config_fast_stress = dataclasses.replace( + # config_cantilever_2d, + # assembly_method=AssemblyMethod.FAST_STRESS + # ) + # results_fast_stress = test_solver(config_fast_stress) + + result = test_compliance_objective(config_cantilever_2d) \ No newline at end of file diff --git a/app/soptx/soptx/utils/__init__.py b/app/soptx/soptx/utils/__init__.py index e69de29bb..5ec1979c7 100644 --- a/app/soptx/soptx/utils/__init__.py +++ b/app/soptx/soptx/utils/__init__.py @@ -0,0 +1,5 @@ +from .timer import timer + +__all__ = [ + 'timer', +] diff --git a/app/soptx/soptx/utils/filter_parameters.py b/app/soptx/soptx/utils/filter_parameters.py deleted file mode 100644 index 039cdc282..000000000 --- a/app/soptx/soptx/utils/filter_parameters.py +++ /dev/null @@ -1,144 +0,0 @@ -from fealpy.backend import backend_manager as bm - -from fealpy.typing import TensorLike, Tuple -from fealpy.sparse import COOTensor - -from builtins import int, float -from math import ceil, sqrt - -def compute_filter_2d(nx: int, ny: int, rmin: float) -> Tuple[TensorLike, TensorLike]: - """ - Compute the filter matrix for the topology optimization. - - This function calculates the filter matrix and its scaling vector - based on the specified filter radius. The filter is used to control - the minimum length scale of the optimized structure, ensuring a - more manufacturable design. - - Args: - nx (int): The number of elements in the x-direction of the mesh. - ny (int): The number of elements in the y-direction of the mesh. - rmin (int): The filter radius, which controls the minimum feature size. - - Returns: - Tuple[COOTensor, bm.ndarray]: A tuple containing the filter matrix (H) - in COO format and the scaling vector (Hs). - """ - if nx <= 0 or ny <= 0: - raise ValueError("The number of elements in both x and y directions (nx, ny) must be positive integers.") - if rmin <= 0: - raise ValueError("The filter radius (rmin) must be a positive integer.") - - nfilter = int(nx * ny * ((2 * (ceil(rmin) - 1) + 1) ** 2)) - iH = bm.zeros(nfilter, dtype=bm.int32) - jH = bm.zeros(nfilter, dtype=bm.int32) - sH = bm.ones(nfilter, dtype=bm.float64) - cc = 0 - - for i in range(nx): - for j in range(ny): - row = i * ny + j - kk1 = int(max(i - (ceil(rmin) - 1), 0)) - kk2 = int(min(i + ceil(rmin), nx)) - ll1 = int(max(j - (ceil(rmin) - 1), 0)) - ll2 = int(min(j + ceil(rmin), ny)) - for k in range(kk1, kk2): - for l in range(ll1, ll2): - col = k * ny + l - fac = rmin - sqrt((i - k) ** 2 + (j - l) ** 2) - iH[cc] = row - jH[cc] = col - sH[cc] = max(0.0, fac) - cc += 1 - H = COOTensor(indices=bm.astype(bm.stack((iH[:cc], jH[:cc]), axis=0), bm.int32), - values=sH[:cc], - spshape=(nx * ny, nx * ny)) - Hs = H @ bm.ones(H.shape[1], dtype=bm.float64) - - return H, Hs - -def compute_filter_3d(nx: int, ny: int, nz: int, rmin: float) -> Tuple[TensorLike, TensorLike]: - """ - Compute the filter matrix for 3D topology optimization. - - This function calculates the filter matrix and its scaling vector - based on the specified filter radius for a 3D problem. The filter is used to control - the minimum length scale of the optimized structure, ensuring a - more manufacturable design. - - Args: - nx (int): The number of elements in the x-direction of the mesh. - ny (int): The number of elements in the y-direction of the mesh. - nz (int): The number of elements in the z-direction of the mesh. - rmin (float): The filter radius, which controls the minimum feature size. - - Returns: - Tuple[COOTensor, bm.ndarray]: A tuple containing the filter matrix (H) - in COO format and the scaling vector (Hs). - """ - if nx <= 0 or ny <= 0 or nz <= 0: - raise ValueError("The number of elements in all directions (nx, ny, nz) must be positive integers.") - if rmin <= 0: - raise ValueError("The filter radius (rmin) must be a positive number.") - - ceil_rmin = int(ceil(rmin)) - nfilter = nx * ny * nz * ((2 * (ceil_rmin - 1) + 1) ** 3) - iH = bm.zeros(nfilter, dtype=bm.int32) - jH = bm.zeros(nfilter, dtype=bm.int32) - sH = bm.zeros(nfilter, dtype=bm.float64) - cc = 0 - - for i in range(nx): - for j in range(ny): - for k in range(nz): - row = i * ny * nz + j * nz + k - ii1 = max(i - (ceil_rmin - 1), 0) - ii2 = min(i + ceil_rmin, nx) - jj1 = max(j - (ceil_rmin - 1), 0) - jj2 = min(j + ceil_rmin, ny) - kk1 = max(k - (ceil_rmin - 1), 0) - kk2 = min(k + ceil_rmin, nz) - for ii in range(ii1, ii2): - for jj in range(jj1, jj2): - for kk in range(kk1, kk2): - col = ii * ny * nz + jj * nz + kk - fac = rmin - sqrt((i - ii)**2 + (j - jj)**2 + (k - kk)**2) - iH[cc] = row - jH[cc] = col - sH[cc] = max(0.0, fac) - cc += 1 - - H = COOTensor(indices=bm.astype(bm.stack((iH[:cc], jH[:cc]), axis=0), bm.int32), - values=sH[:cc], - spshape=(nx * ny * nz, nx * ny * nz)) - Hs = H @ bm.ones(H.shape[1], dtype=bm.float64) - - return H, Hs - -def apply_filter(ft: int, rho: TensorLike, dce: TensorLike, dve: TensorLike, - H: TensorLike, Hs: TensorLike) -> Tuple[TensorLike, TensorLike]: - """ - Apply the filter to the sensitivities. - - Args: - ft (int): Filter type, 0 for sensitivity filter, 1 for density filter. - rho (TensorLike): The density distribution of the material. - dc (TensorLike): The sensitivity of the objective function. - dv (TensorLike): The sensitivity of the volume constraint. - H (TensorLike): The filter matrix. - Hs (TensorLike): The scaling vector for the filter. - - Returns: - tuple: Filtered sensitivity of the objective function and volume constraint. - """ - - if ft == 0: - rho_dce = bm.multiply(rho, dce) - filtered_dce = H.matmul(rho_dce) - dce[:] = filtered_dce / Hs / bm.maximum(bm.array(0.001), rho) - # dce[:] = filtered_dce / Hs / bm.set_at(rho[:], rho[:] < 0.001, 0.001) - elif ft == 1: - dce[:] = H.to_dense() * (dce / Hs) - dve[:] = H.to_dense() * (dve / Hs) - - return dce, dve diff --git a/fealpy/fem/linear_elastic_integrator.py b/fealpy/fem/linear_elastic_integrator.py index 4317b1c9a..2e46f2629 100644 --- a/fealpy/fem/linear_elastic_integrator.py +++ b/fealpy/fem/linear_elastic_integrator.py @@ -106,26 +106,19 @@ def fast_assembly_strain(self, space: _TS) -> TensorLike: # Fill the diagonal part KK = bm.set_at(KK, (slice(None), slice(0, ldof, 1), slice(0, ldof, 1)), D00 * A_xx + D22 * A_yy) KK = bm.set_at(KK, (slice(None), slice(ldof, KK.shape[1], 1), slice(ldof, KK.shape[1], 1)), D00 * A_yy + D22 * A_xx) - # KK[:, 0:ldof:1, 0:ldof:1] = D00 * A_xx + D22 * A_yy - # KK[:, ldof:KK.shape[1]:1, ldof:KK.shape[1]:1] = D00 * A_yy + D22 * A_xx # Fill the off-diagonal part KK = bm.set_at(KK, (slice(None), slice(0, ldof, 1), slice(ldof, KK.shape[1], 1)), D01 * A_xy + D22 * A_yx) KK = bm.set_at(KK, (slice(None), slice(ldof, KK.shape[1], 1), slice(0, ldof, 1)), D01 * A_yx + D22 * A_xy) - # KK[:, 0:ldof:1, ldof:KK.shape[1]:1] = D01 * A_xy + D22 * A_yx - # KK[:, ldof:KK.shape[1]:1, 0:ldof:1] = D01 * A_yx + D22 * A_xy else: # Fill the diagonal part KK = bm.set_at(KK, (slice(None), slice(0, KK.shape[1], GD), slice(0, KK.shape[2], GD)), D00 * A_xx + D22 * A_yy) KK = bm.set_at(KK, (slice(None), slice(1, KK.shape[1], GD), slice(1, KK.shape[2], GD)), D00 * A_yy + D22 * A_xx) - # KK[:, 0:KK.shape[1]:GD, 0:KK.shape[2]:GD] = D00 * A_xx + D22 * A_yy - # KK[:, 1:KK.shape[1]:GD, 1:KK.shape[2]:GD] = D00 * A_yy + D22 * A_xx # Fill the off-diagonal part KK = bm.set_at(KK, (slice(None), slice(0, KK.shape[1], GD), slice(1, KK.shape[2], GD)), D01 * A_xy + D22 * A_yx) KK = bm.set_at(KK, (slice(None), slice(1, KK.shape[1], GD), slice(0, KK.shape[2], GD)), D01 * A_yx + D22 * A_xy) - # KK[:, 0:KK.shape[1]:GD, 1:KK.shape[2]:GD] = D01 * A_xy + D22 * A_yx - # KK[:, 1:KK.shape[1]:GD, 0:KK.shape[2]:GD] = D01 * A_yx + D22 * A_xy + return KK @@ -166,35 +159,27 @@ def fast_assembly_stress(self, space: _TS) -> TensorLike: if D.shape[1] != 1: raise ValueError("fast_assembly_stress currently only supports elastic matrices " "with shape (NC, 1, 3, 3) or (1, 1, 3, 3).") - D00 = D[..., 0, 0, None] - D01 = D[..., 0, 1, None] - D22 = D[..., 2, 2, None] + D00 = D[..., 0, 0, None] # 2*\mu + \lambda + D01 = D[..., 0, 1, None] # \lambda + D22 = D[..., 2, 2, None] # \mu if space.dof_priority: # Fill the diagonal part KK = bm.set_at(KK, (slice(None), slice(0, ldof), slice(0, ldof)), D00 * A_xx + D22 * A_yy) KK = bm.set_at(KK, (slice(None), slice(ldof, KK.shape[1]), slice(ldof, KK.shape[1])), D00 * A_yy + D22 * A_xx) - # KK[:, 0:ldof, 0:ldof] = D00 * A_xx + D22 * A_yy - # KK[:, ldof:KK.shape[1]:1, ldof:KK.shape[1]:1] = D00 * A_yy + D22 * A_xx # Fill the off-diagonal part KK = bm.set_at(KK, (slice(None), slice(0, ldof), slice(ldof, KK.shape[1])), D01 * A_xy + D22 * A_yx) - KK = bm.set_at(KK, (slice(None), slice(ldof, KK.shape[1]), slice(0, ldof)), D22 * A_yx + D01 * A_xy) - # KK[:, 0:ldof, ldof:KK.shape[1]:1] = D01 * A_xy + D22 * A_yx - # KK[:, ldof:KK.shape[1]:1, 0:ldof] = D22 * A_yx + D01 * A_xy + KK = bm.set_at(KK, (slice(None), slice(ldof, KK.shape[1]), slice(0, ldof)), D01 * A_yx + D22 * A_xy) else: # Fill the diagonal part KK = bm.set_at(KK, (slice(None), slice(0, KK.shape[1], GD), slice(0, KK.shape[2], GD)), D00 * A_xx + D22 * A_yy) KK = bm.set_at(KK, (slice(None), slice(1, KK.shape[1], GD), slice(1, KK.shape[2], GD)), D00 * A_yy + D22 * A_xx) - # KK[:, 0:KK.shape[1]:GD, 0:KK.shape[2]:GD] = D00 * A_xx + D22 * A_yy - # KK[:, 1:KK.shape[1]:GD, 1:KK.shape[2]:GD] = D00 * A_yy + D22 * A_xx # Fill the off-diagonal part KK = bm.set_at(KK, (slice(None), slice(0, KK.shape[1], GD), slice(1, KK.shape[2], GD)), D01 * A_xy + D22 * A_yx) - KK = bm.set_at(KK, (slice(None), slice(1, KK.shape[1], GD), slice(0, KK.shape[2], GD)), D22 * A_yx + D01 * A_xy) - # KK[:, 0:KK.shape[1]:GD, 1:KK.shape[2]:GD] = D01 * A_xy + D22 * A_yx - # KK[:, 1:KK.shape[1]:GD, 0:KK.shape[2]:GD] = D22 * A_yx + D01 * A_xy - + KK = bm.set_at(KK, (slice(None), slice(1, KK.shape[1], GD), slice(0, KK.shape[2], GD)), D01 * A_yx + D22 * A_xy) + return KK @assemblymethod('fast_3d') @@ -291,4 +276,4 @@ def fast_assembly(self, space: _TS) -> TensorLike: # KK[:, 2:KK.shape[1]:GD, 0:KK.shape[2]:GD] = D01 * A_zx + D55 * A_xz # KK[:, 2:KK.shape[1]:GD, 1:KK.shape[2]:GD] = D01 * A_zy + D55 * A_yz - return KK + return KK# From 5271be1584a7c71a0d6285322ad22e1caf10667b Mon Sep 17 00:00:00 2001 From: "brighthe98@gmail.com" Date: Sun, 24 Nov 2024 21:45:26 +0800 Subject: [PATCH 30/63] update --- app/soptx/soptx/solver/test.py | 130 +++++++++++++++ app/soptx/soptx/tests/test_assemble.py | 210 +++++++++++++++++++++++++ app/soptx/soptx/tests/test_solver.py | 124 --------------- 3 files changed, 340 insertions(+), 124 deletions(-) create mode 100644 app/soptx/soptx/solver/test.py create mode 100644 app/soptx/soptx/tests/test_assemble.py delete mode 100644 app/soptx/soptx/tests/test_solver.py diff --git a/app/soptx/soptx/solver/test.py b/app/soptx/soptx/solver/test.py new file mode 100644 index 000000000..1357de668 --- /dev/null +++ b/app/soptx/soptx/solver/test.py @@ -0,0 +1,130 @@ +import sympy as sp + +from fealpy.backend import backend_manager as bm + +from fealpy.functionspace import LagrangeFESpace +from fealpy.fem import ScalarMassIntegrator +from fealpy.mesh import TriangleMesh + +class SymbolicIntegration: + def __init__(self, space1, space2=None): + self.space1 = space1 + self.mesh = space1.mesh + self.p1 = space1.p # 第一个空间的多项式次数 + self.GD = self.mesh.geo_dimension() # 几何维度 + self.ldof1 = space1.number_of_local_dofs() # 第一个空间的局部自由度数量 + self.mi1 = self.mesh.multi_index_matrix(p=self.p1, etype=self.GD) # 第一个空间的多重指标矩阵 + + # 如果没有提供第二个空间,则假设两个空间相同 + if space2 is None: + self.space2 = space1 + self.p2 = self.p1 + self.ldof2 = self.ldof1 + self.mi2 = self.mi1 + else: + self.space2 = space2 + self.p2 = space2.p # 第二个空间的多项式次数 + self.ldof2 = space2.number_of_local_dofs() # 第二个空间的局部自由度数量 + self.mi2 = self.mesh.multi_index_matrix(p=self.p2, etype=self.GD) # 第二个空间的多重指标矩阵 + + # 定义符号重心坐标 λ_i + self.l = sp.symbols('l0:%d' % (self.GD+1), real=True) + + def basis(self, p, mi): + GD = self.GD + ldof = mi.shape[0] + l = self.l + + # 初始化矩阵 A,大小为 (p+1) x (GD+1) + A = sp.ones(p+1, GD+1) + + # 计算 A 矩阵的元素 + for i in range(1, p+1): + for j in range(GD+1): + A[i, j] = (p*l[j] - (i-1))*A[i-1, j] + # 除以阶乘 i! + A[i, :] /= sp.factorial(i) + + # 初始化基函数数组 + phi = sp.ones(ldof) + + # 计算基函数 + for i in range(ldof): + phi_i = 1 + for j in range(GD+1): + mi_ij = mi[i, j] + phi_i *= A[mi_ij, j] + phi[i] = phi_i + + return phi + + def multi_index(self, monomial): + l = self.l + GD = self.GD + + m = monomial.as_powers_dict() + a = bm.zeros(GD+1, dtype=bm.int32) # 返回幂指标 + for i in range(GD+1): + a[i] = int(m.get(l[i]) or 0) + + return a + + def integrate(self, f): + GD = self.GD + + f = f.expand() + r = 0 # 积分值 + for m in f.as_coeff_add()[1]: + c = m.as_coeff_mul()[0] # 返回系数 + a = self.multi_index(m) # 返回单项式的幂指标 + temp = 1 + for i in range(GD+1): + temp *= sp.factorial(a[i]) + r += sp.factorial(GD) * c * temp / sp.factorial(sum(a) + GD) + + return r + f.as_coeff_add()[0] + + def phi_phi_matrix(self): + phi1 = self.basis(self.p1, self.mi1) + phi2 = self.basis(self.p2, self.mi2) + ldof1 = self.ldof1 + ldof2 = self.ldof2 + + # 初始化矩阵 M + M = sp.zeros(ldof1, ldof2) + + # 计算单元面积 + area = self.mesh.entity_measure('cell')[0] # 获取第一个单元的面积 + + for i in range(ldof1): + for j in range(ldof2): + integrand = phi1[i] * phi2[j] + M[i, j] = self.integrate(integrand) + integral_value = self.integrate(integrand) + M[i, j] = integral_value * area # 将面积乘到积分结果上 + + return M + +# 初始化网格和空间 +mesh = TriangleMesh.from_box(box=[0, 1, 0, 1], nx=2, ny=2, device='cpu') +# 定义两个不同的多项式次数 +p1 = 1 +p2 = 1 + +# 创建两个有限元空间 +space1 = LagrangeFESpace(mesh=mesh, p=p1, ctype='C') +space2 = LagrangeFESpace(mesh=mesh, p=p2, ctype='C') + +# 创建符号积分类的实例,传入两个空间 +symbolic_int = SymbolicIntegration(space1, space2) + +# 计算质量矩阵 +M = symbolic_int.phi_phi_matrix() + +integrator = ScalarMassIntegrator(q=5) +M1 = integrator.assembly(space=space1) +print("M1:", M1[0]) + +# 输出质量矩阵 +print("Mass Matrix M:") +sp.pprint(M) \ No newline at end of file diff --git a/app/soptx/soptx/tests/test_assemble.py b/app/soptx/soptx/tests/test_assemble.py new file mode 100644 index 000000000..4cb92dc64 --- /dev/null +++ b/app/soptx/soptx/tests/test_assemble.py @@ -0,0 +1,210 @@ +"""Test cases for compliance objective and volume constraint.""" + +from dataclasses import dataclass +import dataclasses +from typing import Literal, Optional, Union, Dict, Any + +from fealpy.backend import backend_manager as bm +from fealpy.mesh import UniformMesh2d, UniformMesh3d, TriangleMesh +from fealpy.functionspace import LagrangeFESpace, TensorFunctionSpace + +from soptx.material import ( + ElasticMaterialConfig, + ElasticMaterialProperties, + SIMPInterpolation +) +from soptx.pde import MBBBeam2dData1, Cantilever2dData1, Cantilever3dData1 +from soptx.solver import ElasticFEMSolver, AssemblyMethod, AssemblyConfig + +bm.set_backend('numpy') + +@dataclass +class TestConfig: + """Configuration for topology optimization test cases.""" + # Required parameters (no default values) + problem_type: Literal['mbb_2d', 'cantilever_2d', 'cantilever_3d'] + nx: int + ny: int + volume_fraction: float + filter_radius: float + filter_type: Literal['sensitivity', 'density', 'heaviside'] + + # Optional parameters (with default values) + nz: Optional[int] = None # Only for 3D problems + elastic_modulus: float = 1.0 + poisson_ratio: float = 0.3 + minimal_modulus: float = 1e-9 + penalty_factor: float = 3.0 + projection_beta: Optional[float] = None # Only for Heaviside projection + move_limit: float = 0.2 + tolerance: float = 0.01 + initial_lambda: float = 1e9 + bisection_tol: float = 1e-3 + + assembly_method: AssemblyMethod = AssemblyMethod.STANDARD # 矩阵组装方法 + quadrature_degree_increase: int = 3 # 积分阶数增量 + solver_type: Literal['cg', 'direct'] = 'cg' # 求解器类型 + solver_params: Optional[Dict[str, Any]] = None # 求解器参数 + +def create_base_components(config: TestConfig): + """Create basic components needed for topology optimization based on configuration.""" + # Create mesh and determine dimensionality + if config.problem_type == 'cantilever_3d': + extent = [0, config.nx, 0, config.ny, 0, config.nz] + h = [1.0, 1.0, 1.0] + origin = [0.0, 0.0, 0.0] + mesh = UniformMesh3d( + extent=extent, h=h, origin=origin, + ipoints_ordering='zyx', flip_direction='y', + device='cpu' + ) + dimension = 3 + else: + extent = [0, config.nx, 0, config.ny] + h = [1.0, 1.0] + origin = [0.0, 0.0] + # mesh = UniformMesh2d( + # extent=extent, h=h, origin=origin, + # ipoints_ordering='yx', flip_direction='y', + # device='cpu' + # ) + mesh = TriangleMesh.from_box(box=[0, config.nx*h[0], 0, config.ny*h[1]], + nx=config.nx, ny=config.ny, device='cpu') + dimension = 2 + + # Create function spaces + p = 1 + space_C = LagrangeFESpace(mesh=mesh, p=p, ctype='C') + tensor_space_C = TensorFunctionSpace(space_C, (-1, dimension)) + space_D = LagrangeFESpace(mesh=mesh, p=p-1, ctype='D') + + # Create material properties + material_config = ElasticMaterialConfig( + elastic_modulus=config.elastic_modulus, + minimal_modulus=config.minimal_modulus, + poisson_ratio=config.poisson_ratio, + plane_assumption="3D" if dimension == 3 else "plane_stress" + ) + interpolation_model = SIMPInterpolation(penalty_factor=config.penalty_factor) + material_properties = ElasticMaterialProperties( + config=material_config, + interpolation_model=interpolation_model + ) + + # Create PDE problem based on problem type + if config.problem_type == 'mbb_2d': + pde = MBBBeam2dData1( + xmin=0, xmax=config.nx*h[0], + ymin=0, ymax=config.ny*h[1] + ) + elif config.problem_type == 'cantilever_2d': + pde = Cantilever2dData1( + xmin=0, xmax=config.nx*h[0], + ymin=0, ymax=config.ny*h[1] + ) + elif config.problem_type == 'cantilever_3d': + pde = Cantilever3dData1( + xmin=0, xmax=config.nx*h[0], + ymin=0, ymax=config.ny*h[1], + zmin=0, zmax=config.nz*h[2] + ) + + assembly_config = AssemblyConfig( + method=config.assembly_method, + quadrature_degree_increase=config.quadrature_degree_increase + ) + + # 设置默认的求解器参数 + default_solver_params = { + 'cg': {'maxiter': 5000, 'atol': 1e-12, 'rtol': 1e-12}, + 'direct': {'solver_type': 'mumps'} + } + solver_params = config.solver_params or default_solver_params[config.solver_type] + + solver = ElasticFEMSolver( + material_properties=material_properties, + tensor_space=tensor_space_C, + pde=pde, + assembly_config=assembly_config, + solver_type=config.solver_type, # 添加默认求解器类型 + solver_params=solver_params # 添加求解器参数 + ) + + # Initialize density field + array = config.volume_fraction * bm.ones(mesh.number_of_cells(), dtype=bm.float64) + rho = space_D.function(array) + + return mesh, space_D, material_properties, solver, rho + +def test_solver(config: TestConfig): + """测试不同矩阵组装方法的 solver.""" + print(f"\n=== Testing Solver with {config.assembly_method} ===") + + # Create base components + mesh, space_D, material_properties, solver, rho = create_base_components(config) + + # Test solver + solver.update_density(rho[:]) + solver_result = solver.solve_cg() + displacement = solver_result.displacement + # print(f"\nSolver information:") + # print(f"- Displacement shape: {displacement.shape}:\n {displacement[:]}") + + # base_local_K = solver.get_base_local_stiffness_matrix() + # print("\n=== 基础局部刚度矩阵信息 ===") + # print(f"基础局部刚度矩阵 - {base_local_K.shape}:\n {base_local_K[0]}") + # local_K = solver.compute_local_stiffness_matrix() + # print("\n=== 当前材料局部刚度矩阵 ===") + # print(f"局部刚度矩阵 - {local_K.shape}:\n {local_K.round(4)}") + + # K = solver.get_global_stiffness_matrix() + # F = solver.get_global_force_vector() + # print("\n=== 全局矩阵和载荷向量信息 ===") + # print(f"全局刚度矩阵 - {K.shape}:\n {K.to_dense().round(4)}") + # print(f"全局刚度矩阵最大值: {bm.max(bm.abs(K.to_dense()))}") + # print(f"全局载荷向量 - {F.shape}:\n {F[:]}") + + +if __name__ == "__main__": + # Test 3D case with density filter + config_3d = TestConfig( + problem_type='cantilever_3d', + nx=60, ny=20, nz=4, + volume_fraction=0.3, + filter_radius=1.5, + filter_type='density' + ) + # results_3d = test_compliance_objective(config_3d) + + # Test 2D case with sensitivity filter + config_2d = TestConfig( + problem_type='mbb_2d', + nx=60, ny=20, + volume_fraction=0.5, + filter_radius=2.4, + filter_type='sensitivity' + ) + # results_2d = test_compliance_objective(config_2d) + + # Test 2D case with sensitivity filter + config_cantilever_2d = TestConfig( + problem_type='cantilever_2d', + nx=160, ny=100, + # nx=8, ny=5, + volume_fraction=0.4, + filter_radius=6, + filter_type='sensitivity' + ) + # 测试标准组装方法 + config_standard = dataclasses.replace( + config_cantilever_2d, + assembly_method=AssemblyMethod.STANDARD + ) + results_standard = test_solver(config_standard) + + # 测试快速应力组装方法 + config_fast_stress = dataclasses.replace( + config_cantilever_2d, + assembly_method=AssemblyMethod.FAST_STRESS + ) + results_fast_stress = test_solver(config_fast_stress) diff --git a/app/soptx/soptx/tests/test_solver.py b/app/soptx/soptx/tests/test_solver.py deleted file mode 100644 index 7c2ddf176..000000000 --- a/app/soptx/soptx/tests/test_solver.py +++ /dev/null @@ -1,124 +0,0 @@ -""" -测试 elastic_fem_solver.py 的功能 -""" -from fealpy.backend import backend_manager as bm -from fealpy.mesh import UniformMesh2d -from fealpy.functionspace import LagrangeFESpace, TensorFunctionSpace - -from soptx.material import ( - ElasticMaterialConfig, - ElasticMaterialProperties, - SIMPInterpolation -) -from soptx.solver import ElasticFEMSolver - -from soptx.pde import MBBBeam2dData1 - - -def test_elastic_fem_solver(): - print("\n=== 弹性有限元求解测试 ===") - - # 1. 创建网格 - nx, ny = 2, 2 - extent = [0, nx, 0, ny] - h = [1.0, 1.0] - origin = [0.0, 0.0] - mesh = UniformMesh2d( - extent=extent, h=h, origin=origin, - ipoints_ordering='yx', flip_direction='y', - device='cpu' - ) - - # 2. 创建函数空间 - p = 1 - space_C = LagrangeFESpace(mesh=mesh, p=p, ctype='C') - tensor_space_C = TensorFunctionSpace(space_C, (-1, 2)) - space_D = LagrangeFESpace(mesh=mesh, p=p-1, ctype='D') - - # 3. 创建材料属性 - # 材料配置 - material_config = ElasticMaterialConfig( - elastic_modulus=1.0, - minimal_modulus=1e-9, - poisson_ratio=0.3, - plane_assumption="plane_stress" - ) - - # 插值模型 - interpolation_model = SIMPInterpolation(penalty_factor=3.0) - - # 密度场(初始为均匀分布) - volfrac = 0.5 - array = volfrac * bm.ones(mesh.number_of_cells(), dtype=bm.float64) - rho_elem = space_D.function(array) - - # 创建材料属性对象 - material_properties = ElasticMaterialProperties( - config=material_config, - rho=rho_elem[:], - interpolation_model=interpolation_model - ) - - # 4. 创建PDE问题 - pde = MBBBeam2dData1(xmin=0, xmax=nx*h[0], ymin=0, ymax=ny*h[1]) - - # 5. 创建求解器 - solver = ElasticFEMSolver( - material_properties=material_properties, - tensor_space=tensor_space_C, - pde=pde - ) - - # 7. 测试基础刚度矩阵 - # 测试基础局部刚度矩阵 - base_local_K = solver.get_base_local_stiffness_matrix() - print("\n=== 基础局部刚度矩阵信息 ===") - print(f"基础局部刚度矩阵 - {base_local_K.shape}:\n {base_local_K[0]}") - - # 测试局部刚度矩阵计算 - local_K = solver.compute_local_stiffness_matrix() - print("\n=== 当前材料局部刚度矩阵信息 ===") - print(f"局部刚度矩阵 - {local_K.shape}:\n {local_K[0]}") - - print("\n=== 测试共轭梯度求解器 ===") - try: - cg_result = solver.solve_cg() - - print(f"CG求解结果:") - # print(f"- 迭代次数: {cg_result.iterations}") - # print(f"- 是否收敛: {cg_result.converged}") - # print(f"- 残差: {cg_result.residual}") - print(f"- 位移场形状: {cg_result.displacement.shape}") - print(f"- 最大位移: {bm.max(bm.abs(cg_result.displacement))}") - - # 获取求解后的全局矩阵和向量 - K = solver.get_global_stiffness_matrix() - F = solver.get_global_force_vector() - if K is not None and F is not None: - print("\n=== 全局矩阵和载荷向量信息 ===") - print(f"全局刚度矩阵 - {K.shape}:\n {K.to_dense().round(4)}") - print(f"全局载荷向量 - {F.shape}:\n {F[:]}") - print(f"载荷向量最大值: {bm.max(bm.abs(F))}") - except Exception as e: - print(f"CG求解失败: {str(e)}") - - print("\n=== 测试直接求解器 ===") - try: - direct_result = solver.solve_direct(solver_type='mumps') - print(f"直接求解结果:") - print(f"- 位移场形状: {direct_result.displacement.shape}") - print(f"- 最大位移: {bm.max(bm.abs(direct_result.displacement))}") - - # 获取求解后的全局矩阵和向量 - K = solver.get_global_stiffness_matrix() - F = solver.get_global_force_vector() - if K is not None and F is not None: - print("\n=== 全局矩阵和载荷向量信息 ===") - print(f"全局刚度矩阵 - {K.shape}:\n {K.to_dense().round(4)}") - print(f"全局载荷向量 - {F.shape}:\n {F[:]}") - print(f"载荷向量最大值: {bm.max(bm.abs(F))}") - except Exception as e: - print(f"直接求解失败: {str(e)}") - -if __name__ == "__main__": - test_elastic_fem_solver() \ No newline at end of file From ba563e35f947761511acc570566379a01b514114 Mon Sep 17 00:00:00 2001 From: BenHBLiu Date: Sun, 24 Nov 2024 23:20:25 +0800 Subject: [PATCH 31/63] update --- fealpy/opt/differentialted_creative_search.py | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/fealpy/opt/differentialted_creative_search.py b/fealpy/opt/differentialted_creative_search.py index 6df481abe..1baf68c61 100644 --- a/fealpy/opt/differentialted_creative_search.py +++ b/fealpy/opt/differentialted_creative_search.py @@ -35,41 +35,50 @@ def run(self): golden_ratio = 2 / (1 + bm.sqrt(bm.array(5))) ngS = int(bm.max(bm.array([6, bm.round(N * (golden_ratio / 3))]))) pc = 0.5 - phi_qKR = 0.25 + 0.55 * ((0 + (bm.arange(0, N) / N)) ** 0.5)[:, None] + phi_qKR = 0.25 + 0.55 * ((0 + ((1 + bm.arange(0, N)) / N)) ** 0.5)[:, None] # Eq.(8) x_new = bm.zeros((N, dim)) for it in range(MaxIT): bestInd = 0 - lamda_t = 0.1 + (0.518 * ((1 - (it / MaxIT) ** 0.5))) + lamda_t = 0.1 + (0.518 * ((1 - (it / MaxIT) ** 0.5))) # Eq.(13) index = bm.argsort(fit, axis=0) fit = bm.sort(fit, axis=0) x = x[index[:, 0]] - eta_qKR = (bm.round(bm.random.rand(N, 1) * phi_qKR) + 1 * (bm.random.rand(N, 1) <= phi_qKR)) / 2 - jrand = bm.floor(dim * bm.random.rand(N, 1)) - + # Divergent thinking + eta_qKR = (bm.round(bm.random.rand(N, 1) * phi_qKR) + 1 * (bm.random.rand(N, 1) <= phi_qKR)) / 2 # Eq.(7) + jrand = bm.floor(dim * bm.random.rand(N, 1)) # Eq.(9) r1 = bm.random.randint(0, N, (ngS,)) - aa = (bm.random.rand(ngS, dim) < eta_qKR[0 : ngS]) + (jrand[0 : ngS] == bm.arange(0, dim)) + aa = (bm.random.rand(ngS, dim) < eta_qKR[0 : ngS]) + (jrand[0 : ngS] == bm.arange(0, dim)) R = bm.sin(0.5 * bm.pi * golden_ratio) * bm.tan(0.5 * bm.pi * (1 - golden_ratio * bm.random.rand(ngS, dim))) - Y = 0.05 * bm.sign(bm.random.rand(ngS, dim) - 0.5) * bm.log(bm.random.rand(ngS, dim) / bm.random.rand(ngS, dim)) * (R ** (1 / golden_ratio)) + Y = 0.05 * bm.sign(bm.random.rand(ngS, dim) - 0.5) * bm.log(bm.random.rand(ngS, dim) / bm.random.rand(ngS, dim)) * (R ** (1 / golden_ratio)) # Eq.(17) x_new[0 : ngS] = (~aa * x[0 : ngS] + - aa * (x[r1[0 : ngS]] + Y)) + aa * (x[r1[0 : ngS]] + Y)) # Eq.(19) + # Convergent thinking r1 = bm.random.randint(0, N, (N - ngS - 1,)) r2 = bm.random.randint(0, N - ngS, (N - ngS - 1,)) + ngS aa = (bm.random.rand(N - ngS - 1, dim) < eta_qKR[ngS : N - 1]) + (jrand[ngS : N - 1] == bm.arange(0, dim)) x_new[ngS : N - 1] = (~aa * x[ngS : N - 1] + - aa * (x[bestInd] + ((x[r2] - x[ngS : N - 1]) * lamda_t) + ((x[r1] - x[ngS : N - 1])) * bm.random.rand(N - ngS - 1, 1))) - - + aa * (x[bestInd] + ((x[r2] - x[ngS : N - 1]) * lamda_t) + ((x[r1] - x[ngS : N - 1])) * bm.random.rand(N - ngS - 1, 1))) # Eq.(15) + # Team diversification if bm.random.rand(1, 1) < pc: - x_new[N - 1] = lb + (ub - lb) * bm.random.rand(1, dim) + x_new[N - 1] = lb + (ub - lb) * bm.random.rand(1, dim) # Eq.(21) + + # Boundary handling + # Eq.(20) + po = x_new < lb + x_new[po] = (x[po] + lb) / 2 + po = x_new > ub + x_new[po] = (x[po] + ub) / 2 x_new = x_new + (lb - x_new) * (x_new < lb) + (ub - x_new) * (x_new > ub) + + # Retrospective assessment fit_new = self.fun(x_new)[:, None] mask = fit_new < fit - x, fit = bm.where(mask, x_new, x), bm.where(mask, fit_new, fit) + x, fit = bm.where(mask, x_new, x), bm.where(mask, fit_new, fit) # Eq.(22) gbest_idx = bm.argmin(fit) - (gbest, gbest_f) = (x[gbest_idx], fit[gbest_idx]) if fit[gbest_idx] < gbest_f else (gbest, gbest_f) + (gbest, gbest_f) = (x[gbest_idx], fit[gbest_idx]) if fit[gbest_idx] < gbest_f else (gbest, gbest_f) # Eq.(23) return gbest, gbest_f \ No newline at end of file From 38c21b884c95bee8acd603d787762ea16825af54 Mon Sep 17 00:00:00 2001 From: BellaLq <2655493031@qq.com> Date: Mon, 25 Nov 2024 16:13:43 +0800 Subject: [PATCH 32/63] update --- fealpy/mesh/lagrange_quadrangle_mesh.py | 88 +++++++++++++++------- test/mesh/lagrange_quadrangle_mesh_data.py | 21 ++++++ test/mesh/lagrange_triangle_mesh_data.py | 10 --- test/mesh/test_lagrange_quadrangle_mesh.py | 53 +++++++++++++ test/mesh/test_lagrange_triangle_mesh.py | 20 +---- 5 files changed, 139 insertions(+), 53 deletions(-) create mode 100644 test/mesh/lagrange_quadrangle_mesh_data.py create mode 100644 test/mesh/test_lagrange_quadrangle_mesh.py diff --git a/fealpy/mesh/lagrange_quadrangle_mesh.py b/fealpy/mesh/lagrange_quadrangle_mesh.py index 6f3da3207..2765bbc2f 100644 --- a/fealpy/mesh/lagrange_quadrangle_mesh.py +++ b/fealpy/mesh/lagrange_quadrangle_mesh.py @@ -15,54 +15,59 @@ def __init__(self, node: TensorLike, cell: TensorLike, p=1, surface=None, kwargs = bm.context(cell) self.p = p - self.node = node + self.GD = node.shape[1] self.cell = cell self.surface = surface - self.localEdge = self.generate_local_lagrange_edges(p) - self.localFace = self.localEdge - self.ccw = bm.tensor([0, 1, 2, 3], **kwargs) - self.localCell = None + if p == 1: + self.node = node + else: + NN = self.number_of_nodes() + self.node = bm.zeros((NN, self.GD), dtype=bm.float64) + bc = bm.multi_index_matrix(p, TD)/p + bc = bm.einsum('im, jn -> ijmn', bc, bc).reshape(-1, 4) + self.node[self.cell] = bm.einsum('ijn, kj -> ikn', node[cell], bc) + + self.surface is not None: + self.node,_ = self.surface.project(self.node) + + self.ccw = bm.tensor([0, 2, 3, 1], **kwargs) if construct: self.construct() self.meshtype = 'lquad' - - def generate_local_lagrange_edges(self, p: int) -> TensorLike: - """ - Generate the local edges for Lagrange elements of order p. - """ - TD = self.top_dimension() - multiIndex = bm.multi_index_matrix(p, TD) - - localEdge = bm.zeros((4, p+1), dtype=bm.int32) - localEdge[3, :], = bm.where(multiIndex[:, 2] == 0) - localEdge[2,:] = bm.flip(bm.where(multiIndex[:, 1] == 0)[1]) - localEdge[1,:] = bm.flip(bm.where(multiIndex[:, 1] == 0)[0]) - localEdge[0, :], = bm.where(multiIndex[:, 0] == 0) - - return localEdge + self.nodedata = {} + self.edgedata = {} + self.celldata = {} + self.meshdata = {} def reference_cell_measure(self): return 1 + def interpolation_points(self, p: int, index: Index=_S): + """Fetch all p-order interpolation points on the quadrangle mesh.""" + pass @classmethod def from_quadrangle_mesh(cls, mesh, p: int, surface=None): + bnode = mesh.entity('node') node = mesh.interpolation_points(p) cell = mesh.cell_to_ipoint(p) if surface is not None: - node, _ = surface.project(node) + bnode[:],_ = surface.project(bnode) + node,_ = surface.project(node) - lmesh = cls(node, cell, p=p, construct=True) + lemsh = cls(node, mesh, p=p, construct=True) + lmesh.qmesh = mesh lmesh.edge2cell = mesh.edge2cell # (NF, 4) lmesh.cell2edge = mesh.cell_to_edge() - #lmesh.edge = mesh.edge_to_ipoint(p) - return lmesh - + lmesh.edge = mesh.edge_to_ipoint(p) + return lmesh + + # quadrature def quadrature_formula(self, q, etype: Union[int, str] = 'cell'): from ..quadrature import GaussLegendreQuadrature, TensorProductQuadrature if isinstance(etype, str): @@ -71,6 +76,37 @@ def quadrature_formula(self, q, etype: Union[int, str] = 'cell'): if etype == 2: return TensorProductQuadrature((qf, qf)) elif etype == 1: - return qf + return TensorProductQuadrature((qf, )) else: raise ValueError(f"entity type: {etype} is wrong!") + + def bc_to_point(self, bc: TensorLike, index: Index=_S, etype='cell'): + node = =self.node + TD = len(bc) + phi = self.shape_function(bc) + p = bm.einsum() + return p + + # shape function + def shape_function(self, bc: TensorLike, p=None): + """ + @berif + bc 是一个长度为 TD 的 tuple 数组 + bc[i] 是一个一维积分公式的重心坐标数组 + 假设 bc[0]==bc[1]== ... ==bc[TD-1] + """ + p = self.p if p is None else p + TD = len(bc) + phi = bm.simplex_shape_function(bc[0], p) + if TD == 2: + phi = bm.einsum('im, jn -> ijmn', phi, phi) #TODo + shape = phi.reshape[:-2] + (-1,) + phi = phi.reshape(shape) # 展平自由度 + shape = (-1, 1) + phi.shape[-1:] # 增加一个单元轴,方便广播运算 + phi = phi.reshape(shape) # 展平积分点 + return phi + + def grad_shape_function(self, bc: TensorLike, p=None, + index: Index=_S, variables='x'): + pass + diff --git a/test/mesh/lagrange_quadrangle_mesh_data.py b/test/mesh/lagrange_quadrangle_mesh_data.py new file mode 100644 index 000000000..723bc20f3 --- /dev/null +++ b/test/mesh/lagrange_quadrangle_mesh_data.py @@ -0,0 +1,21 @@ +import numpy as np +from fealpy.geometry.implicit_surface import SphereSurface +from fealpy.mesh.quadrangle_mesh import QuadrangleMesh + +# 定义多个典型的 LagrangeTriangleMesh 对象 +surface = SphereSurface() #以原点为球心,1 为半径的球 +mesh = QuadrangleMesh.from_unit_sphere_surface() +node = mesh.interpolation_points(3) +cell = mesh.cell_to_ipoint(3) + +from_triangle_mesh_data = [ + { + "p": 3, + "surface": surface, + "cell": np.array(dtype=np.int32), + "NN": + "NE": + "NF": + "NC": + } +] diff --git a/test/mesh/lagrange_triangle_mesh_data.py b/test/mesh/lagrange_triangle_mesh_data.py index b924ae935..b6f539380 100644 --- a/test/mesh/lagrange_triangle_mesh_data.py +++ b/test/mesh/lagrange_triangle_mesh_data.py @@ -167,13 +167,3 @@ "sphere_cm": 4*np.pi*(1**2) } ] - -edge_length_data = [ - { - "el": np.array([1.1097288, 1.1097288, 1.1097288, 1.1097288, 1.1097288, 1.1097288, - 1.1097288, 1.1097288, 1.1097288, 1.1097288, 1.1097288, 1.1097288, - 1.1097288, 1.1097288, 1.1097288, 1.1097288, 1.1097288, 1.1097288, - 1.1097288, 1.1097288, 1.1097288, 1.1097288, 1.1097288, 1.1097288, - 1.1097288, 1.1097288, 1.1097288, 1.1097288, 1.1097288, 1.1097288], dtype=np.float64) - } -] diff --git a/test/mesh/test_lagrange_quadrangle_mesh.py b/test/mesh/test_lagrange_quadrangle_mesh.py new file mode 100644 index 000000000..c3a6128e8 --- /dev/null +++ b/test/mesh/test_lagrange_quadrangle_mesh.py @@ -0,0 +1,53 @@ +import numpy as np +import sympy as sp + +import pytest +from fealpy.pde.surface_poisson_model import SurfaceLevelSetPDEData +from fealpy.geometry.implicit_surface import SphereSurface +from fealpy.mesh.quadrangle_mesh import QuadrangleMesh +from fealpy.backend import backend_manager as bm +from fealpy.mesh.lagrange_quadrangle_mesh import LagrangeQuadrangleMesh +from fealpy.functionspace.lagrange_fe_space import LagrangeFESpace +from fealpy.functionspace.parametric_lagrange_fe_space import ParametricLagrangeFESpace + +from lagrange_quadrangle_mesh_data import * + + +class TestLagrangeQuadrangleMeshInterfaces: + @pytest.mark.parametrize("backend", ['numpy']) + @pytest.mark.parametrize("data", from_triangle_mesh_data) + def test_from_triangle_mesh(self, data, backend): + bm.set_backend(backend) + + p = data['p'] + surface = data['surface'] + mesh = TriangleMesh.from_unit_sphere_surface() + + lmesh = LagrangeTriangleMesh.from_triangle_mesh(mesh, p, surface=surface) + + assert lmesh.number_of_nodes() == data["NN"] + assert lmesh.number_of_edges() == data["NE"] + assert lmesh.number_of_faces() == data["NF"] + assert lmesh.number_of_cells() == data["NC"] + + cell = lmesh.entity('cell') + np.testing.assert_allclose(bm.to_numpy(cell), data["cell"], atol=1e-14) + + @pytest.mark.parametrize("backend", ['numpy']) + def test_surface_mesh(self, backend): + bm.set_backend(backend) + + surface = SphereSurface() + mesh = QuadrangleMesh.from_unit_sphere_surface() + + lmesh = LagrangeQuadrangleMesh.from_quadrangle_mesh(mesh, p=3, surface=surface) + fname = f"sphere_qtest.vtu" + lmesh.to_vtk(fname=fname) + + +if __name__ == "__main__": + a = TestLagrangeTriangleMeshInterfaces() + a.test_init_mesh(init_data[0], 'numpy') + #a.test_from_triangle_mesh(from_triangle_mesh_data[0], 'numpy') + #a.test_surface_mesh('numpy') + #pytest.main(["./test_lagrange_triangle_mesh.py"]) diff --git a/test/mesh/test_lagrange_triangle_mesh.py b/test/mesh/test_lagrange_triangle_mesh.py index 2e365d2ce..93e23aa85 100644 --- a/test/mesh/test_lagrange_triangle_mesh.py +++ b/test/mesh/test_lagrange_triangle_mesh.py @@ -92,19 +92,6 @@ def test_cell_area(self, data, backend): em_ratio = em[0:-1] / em[1:] print("unit_sphere:", em_ratio) - - @pytest.mark.parametrize("backend", ['numpy']) - @pytest.mark.parametrize("data", edge_length_data) - def test_edge_length(self, data, backend): - bm.set_backend(backend) - - surface = SphereSurface() #以原点为球心,1 为半径的球 - mesh = TriangleMesh.from_unit_sphere_surface() - lmesh = LagrangeTriangleMesh.from_triangle_mesh(mesh, p=3, surface=surface) - el = lmesh.edge_length() - - np.testing.assert_allclose(bm.to_numpy(el), data["el"], atol=1e-14) - @pytest.mark.parametrize("backend", ['numpy']) def test_error(self, backend): bm.set_backend(backend) @@ -139,12 +126,11 @@ def test_error(self, backend): if __name__ == "__main__": - a = TestLagrangeTriangleMeshInterfaces() + #a = TestLagrangeTriangleMeshInterfaces() #a.test_init_mesh(init_data[0], 'numpy') #a.test_from_triangle_mesh(from_triangle_mesh_data[0], 'numpy') #a.test_surface_mesh('numpy') #a.test_cell_area(cell_area_data[0], 'numpy') - #a.test_edge_length(edge_length_data[0], 'numpy') #a.test_(cell_[0], 'numpy') - a.test_error('numpy') - #pytest.main(["./test_lagrange_triangle_mesh.py"]) + #a.test_error('numpy') + pytest.main(["./test_lagrange_triangle_mesh.py"]) From 289e1152284a05049a8ccc9b1079cc1ca72e2876 Mon Sep 17 00:00:00 2001 From: chaos <2250213115@qq.com> Date: Mon, 25 Nov 2024 19:05:24 +0800 Subject: [PATCH 33/63] update --- app/lafem-eit/script/eit_data_generator.py | 2 - app/lafem-ims/functional.py | 112 ++++++++++ .../near_field_data_generator.py | 4 +- .../default_data/reciever_points_50.npy | Bin 0 -> 928 bytes .../test/test_near_field_data_generator.py | 74 ------- .../test/test_near_field_data_generator_1.py | 51 +++++ .../test/test_near_field_data_generator_2.py | 54 +++++ .../test/test_parallel_data_generation.py | 0 fealpy/fem/scalar_diffusion_integrator.py | 1 - .../ml/generator/near_field_data_generator.py | 200 +++++++++++------- 10 files changed, 347 insertions(+), 151 deletions(-) create mode 100644 app/lafem-ims/functional.py create mode 100644 app/lafem-ims/lafemims/default_data/reciever_points_50.npy delete mode 100644 app/lafem-ims/test/test_near_field_data_generator.py create mode 100644 app/lafem-ims/test/test_near_field_data_generator_1.py create mode 100644 app/lafem-ims/test/test_near_field_data_generator_2.py delete mode 100644 app/lafem-ims/test/test_parallel_data_generation.py diff --git a/app/lafem-eit/script/eit_data_generator.py b/app/lafem-eit/script/eit_data_generator.py index 596c2ff98..e04cdd2f2 100644 --- a/app/lafem-eit/script/eit_data_generator.py +++ b/app/lafem-eit/script/eit_data_generator.py @@ -104,9 +104,7 @@ def main(sigma_iterable: Sequence[int], seed: int = 0, index: int = 0): ctrs_ = np.random.rand(NUM_CIR, 2) * 1.6 - 0.8 # (NCir, GD) b = np.min(0.9-np.abs(ctrs_), axis=-1) # (NCir, ) rads_ = np.random.rand(NUM_CIR) * (b-0.1) + 0.1 # (NCir, ) - ctrs = bm.from_numpy(ctrs_) ctrs = bm.astype(ctrs, bm.float64) - rads = bm.from_numpy(rads_) rads = bm.astype(rads_, bm.float64) ls_fn = lambda p: levelset(p, ctrs, rads) diff --git a/app/lafem-ims/functional.py b/app/lafem-ims/functional.py new file mode 100644 index 000000000..a68703118 --- /dev/null +++ b/app/lafem-ims/functional.py @@ -0,0 +1,112 @@ + +from numpy.typing import NDArray +from fealpy.backend import backend_manager as bm +from fealpy.typing import TensorLike + +#多圆形并集-型 散射体 +def levelset_circles(p: NDArray, centers: NDArray, radius: NDArray) -> NDArray: + """ + 参数: + - p: 坐标点数组,形状为 (N, 2) + - centers: 圆心坐标数组,形状为 (NCir, 2) + - radius: 半径数组,形状为 (NCir,) + + 返回: + - 水平集函数值,形状为 (N,) + """ + struct = p.shape[:-1] # 获取输入点的结构形状 + p = p.reshape(-1, p.shape[-1]) # 将输入点展平为 (N, 2) + + # 计算每个点到所有圆心的距离 + dis = bm.linalg.norm(p[:, None, :] - centers[None, :, :], axis=-1) # 形状为 (N, NCir) + + # 计算每个点到最近圆的距离 + ret = bm.min(dis - radius[None, :], axis=-1) # 形状为 (N,) + + return ret.reshape(struct) # 恢复输入点的原始结构形状 + +#半径函数为傅里叶系数-型 散射体 +def levelset_fourier(points: NDArray, complexity: int, coefficient: NDArray, origin: NDArray) -> NDArray: + """ + 参数: + - points: NDArray, 形状为 (N, 2),表示 N 个点的坐标。 + - complexity: int, 水平集函数的复杂度。 + - coefficient: NDArray, 形状为 (2 * M + 1,),表示傅里叶系数。 + - origin: NDArray, 形状为 (2,),表示原点坐标。 + + 返回: + - flag: NDArray, 形状为 (N,),表示每个点是否在水平集函数内。 + """ + points = points - origin + angles_rad = bm.zeros_like(points[:, 0], dtype=bm.float64) # 创建一个和点集大小相同的数组,用于存储角度弧度 + + # 处理分母为零的情况 + zero_indices = bm.where(points[:, 0] == 0) + angles_rad[zero_indices] = bm.pi / 2 if bm.any(points[zero_indices, 1] > 0) else 3 * bm.pi / 2 + + # 处理分母不为零的情况 + non_zero_indices = bm.where(points[:, 0] != 0) + slopes = points[non_zero_indices, 1] / points[non_zero_indices, 0] # 计算斜率 + angles_rad[non_zero_indices] = bm.arctan(slopes) # 计算角度弧度 + + # 将负值转换为正值 + negative_angle_indices = bm.where(angles_rad < 0) + angles_rad[negative_angle_indices] += bm.pi + + # 调整角度弧度,确保在0到2*pi之间 + angles_rad = angles_rad % (2 * bm.pi) + + # 处理负斜率的情况 + negative_slope_indices = bm.where((points[:, 0] >= 0) & (points[:, 1] < 0)) + angles_rad[negative_slope_indices] += bm.pi + + r_t = coefficient[0] + + for i in range(complexity): + r_t += coefficient[i + 1] * bm.cos((i + 1) * angles_rad) + coefficient[i + complexity + 1] * bm.sin((i + 1) * angles_rad) + + distances = r_t - bm.linalg.norm(points, axis=1) + flag = distances >= 0 + + return flag + +################################################################################################################################# + +def genarate_scatterers_circles(num_of_scatterers: int, seed: int) ->TensorLike: + """ + 参数: + - num_of_scatterers: int, 散射体的数量。 + + 返回: + - ctrs: TensorLike, 形状为 (num_of_scatterers, 2),表示每个散射体的中心坐标。 + - rads: TensorLike, 形状为 (num_of_scatterers,),表示每个散射体的半径。 + """ + bm.random.seed(seed) + + ctrs = bm.random.rand(num_of_scatterers, 2) * 1.6 - 0.8 # (NCir, GD) + b = bm.min(0.9-bm.abs(ctrs), axis=-1) # (NCir, ) + rads = bm.random.rand(num_of_scatterers) * (b-0.1) + 0.1 # (NCir, ) + ctrs = bm.astype(ctrs, bm.float64) + rads = bm.astype(rads, bm.float64) + + return ctrs, rads +def genarate_scatterers_fourier(complexity: int, num_of_scatterers: int, seed: int) ->TensorLike: + """ + 参数: + - complexity: int, 水平集函数的复杂度。 + - num_of_scatterers: int, 散射体的数量。 + + 返回: + - c: TensorLike, 形状为 (num_of_scatterers, 2*complexity+1),表示每个散射体的参数。 + """ + bm.random.seed(seed) + + c = bm.zeros((num_of_scatterers, 2*complexity+1), dtype=bm.float64) + radius = bm.random.uniform(0, 0.1, (num_of_scatterers, complexity)) + theta = bm.random.uniform(0, 2*bm.pi, (num_of_scatterers, complexity)) + + c[:, 0:1] = bm.random.uniform(1, 1.2, (num_of_scatterers, 1)) + c[:, 1:complexity+1] = radius * bm.cos(theta) + c[:, complexity+1: ] = radius * bm.sin(theta) + + return c diff --git a/app/lafem-ims/lafemims/data_generator/near_field_data_generator.py b/app/lafem-ims/lafemims/data_generator/near_field_data_generator.py index 5f8221f79..19bbb4b13 100644 --- a/app/lafem-ims/lafemims/data_generator/near_field_data_generator.py +++ b/app/lafem-ims/lafemims/data_generator/near_field_data_generator.py @@ -2,9 +2,9 @@ import os import matplotlib.pyplot as plt from numpy.typing import NDArray +import numpy as bm from typing import Sequence, Callable -from fealpy.backend import backend_manager as bm from fealpy.mesh import TriangleMesh, QuadrangleMesh, UniformMesh2d from fealpy.pde.pml_2d import PMLPDEModel2d from fealpy.functionspace import LagrangeFESpace @@ -199,7 +199,7 @@ def save(self, save_path: str, scatterer_index: int): d_name = d_values[j] name = f"{k_name}, d={d_name}" data_dict[name] = self.data_for_dsm(k=k_values[i], d=d_values[j]) - filename = os.path.join(save_path, f"data_for_dsm_{scatterer_index}.bmz") + filename = os.path.join(save_path, f"data_for_dsm_{scatterer_index}.npz") bm.savez(filename, **data_dict) def visualization_of_nearfield_data(self, k: float, d: Sequence[float]): diff --git a/app/lafem-ims/lafemims/default_data/reciever_points_50.npy b/app/lafem-ims/lafemims/default_data/reciever_points_50.npy new file mode 100644 index 0000000000000000000000000000000000000000..8e1b75c0a39dd560e56f7f694dbd9d10e7629164 GIT binary patch literal 928 zcmbR27wQ`j$;eQ~P_3SlTAW;@Zl$1ZlV+i=qoAIaUsO_*m=~X4l#&V(cT3DEP6dh= zXCxM+0{I%I2098xnmP)#3giMV1~3qDfY1yKi`p+tS#Vw0;l?IT(`CF*?0tl_f>h&$ z9rm>Z+Gs!hY=3UX)VgdE~8U%F*b{MX(>LU7}THbIBWC%;E(xH3Du{9$)cOHaVz z+lEO;UpKKk$fO-_S**e9Aej-WbvTjRLCYy=pG7^V!xJ|n8@@|?4k~7Hr|;&mI*81j zSO4{qfCE$E_Pf8>867HmIbUs)5OP@Z_&3Yy9Y5^lw{o4yU=?-{igc+{{{7b8UTu1} z|59Oxz#TUhoe{rcU%%?z^rv5i9ikxo{V=`)ME!o4`ehLF_QTAB+IOHFYM%zgJqLuL z?$L(0_W;bjvJn3q5QF;X1;oDxzC-;B4UYqtpy6>I5?%+Qpy34#&jYaVgogKl3()X} z#>asgH(>>hA9wb^^2gFgs_kag#_#Y@kjh_R0sPT7TJ~Vz6 zAn|*E5jFk~s6o>QG(HaKLDSD8sC(t1=}QC>9|vAT)8Bijf6>zC0cd NDArray: - """ - 计算水平集函数值。 - - 参数: - - p: 坐标点数组,形状为 (N, 2) - - centers: 圆心坐标数组,形状为 (NCir, 2) - - radius: 半径数组,形状为 (NCir,) - - 返回: - - 水平集函数值,形状为 (N,) - """ - struct = p.shape[:-1] # 获取输入点的结构形状 - p = p.reshape(-1, p.shape[-1]) # 将输入点展平为 (N, 2) - - # 计算每个点到所有圆心的距离 - dis = np.linalg.norm(p[:, None, :] - centers[None, :, :], axis=-1) # 形状为 (N, NCir) - - # 计算每个点到最近圆的距离 - ret = np.min(dis - radius[None, :], axis=-1) # 形状为 (N,) - - return ret.reshape(struct) # 恢复输入点的原始结构形状 - -# 生成接收点 -reciever_points = CircleCollocator(0, 0, 5).run(50) - -# 定义圆的中心和半径 -cirs = np.array([ - [0.4, -0.6, 0.2], - [0.6, -0.5, 0.1], - [0.3, 0.2, 0.3] -], dtype=np.float64) - -centers = cirs[:, 0:2] # 圆心坐标 -radius = cirs[:, 2] # 圆的半径 - -# 定义水平集函数 -ls_fn = lambda p: levelset(p, centers, radius) - -# 创建近场数据生成器 -generator = NearFieldDataFEMGenerator2d( - domain=domain, - mesh='UniformMesh', - nx=100, - ny=100, - p=1, - q=3, - u_inc=u_inc, - levelset=ls_fn, - d=d, - k=k, - reciever_points=reciever_points -) - -# 可视化近场数据 -generator.visualization_of_nearfield_data(k=k[0], d=d[0]) diff --git a/app/lafem-ims/test/test_near_field_data_generator_1.py b/app/lafem-ims/test/test_near_field_data_generator_1.py new file mode 100644 index 000000000..5437993e3 --- /dev/null +++ b/app/lafem-ims/test/test_near_field_data_generator_1.py @@ -0,0 +1,51 @@ + +from fealpy.backend import backend_manager as bm +from fealpy.ml.sampler import CircleCollocator + +from lafemims.data_generator import NearFieldDataFEMGenerator2d +from functional import levelset_circles, genarate_scatterers_circles + + +# 设置随机种子 +SEED = 2024 + +# 定义计算域 +domain = [-6, 6, -6, 6] + +# 定义入射波函数 +u_inc = 'cos(d_0*k*x + d_1*k*y) + sin(d_0*k*x + d_1*k*y) * 1j' + +# 定义波矢量方向和波数 +d = [[-bm.sqrt(0.5), bm.sqrt(0.5)]] +k = [2 * bm.pi] + +#散射体个数 +num_of_scatterers = 40000 +# 生成接收点 +num_of_reciever_points = 50 +reciever_points = CircleCollocator(0, 0, 5).run(num_of_reciever_points) + +#选择某个散射体 +idx = 0 +centers, radius = genarate_scatterers_circles(num_of_scatterers, SEED) + +# 定义水平集函数 +ls_fn = lambda p: levelset_circles(p, centers[idx:idx+1, ...], radius[idx:idx+1, ...]) + +# 创建近场数据生成器 +generator = NearFieldDataFEMGenerator2d( + domain=domain, + mesh='UniformMesh', + nx=100, + ny=100, + p=1, + q=3, + u_inc=u_inc, + levelset=ls_fn, + d=d, + k=k, + reciever_points=reciever_points +) + +# 可视化近场数据 +generator.visualization_of_nearfield_data(k=k[0], d=d[0]) diff --git a/app/lafem-ims/test/test_near_field_data_generator_2.py b/app/lafem-ims/test/test_near_field_data_generator_2.py new file mode 100644 index 000000000..fe7aeaa97 --- /dev/null +++ b/app/lafem-ims/test/test_near_field_data_generator_2.py @@ -0,0 +1,54 @@ + +from fealpy.backend import backend_manager as bm +from fealpy.ml.sampler import CircleCollocator + +from lafemims.data_generator import NearFieldDataFEMGenerator2d +from functional import levelset_fourier, genarate_scatterers_fourier + + +# 设置随机种子 +SEED = 2025 + +# 定义计算域 +domain = [-6, 6, -6, 6] + +# 定义入射波函数 +u_inc = 'cos(d_0*k*x + d_1*k*y) + sin(d_0*k*x + d_1*k*y) * 1j' + +# 定义波矢量方向和波数 +d = [[-bm.sqrt(0.5), bm.sqrt(0.5)]] +k = [2 * bm.pi] + +#选择散射体复杂度、散射体中心点以及生成散射体个数 +M = 20 +origin_point = bm.array([0.0, 0.0]) +num_of_scatterers = 40000 + +#确定外层接收点 +num_of_reciever_points = 50 +reciever_points = CircleCollocator(0, 0, 5).run(num_of_reciever_points) + +#选择某个散射体 +idx = 0 +coefficients = genarate_scatterers_fourier(M, num_of_scatterers, SEED) # shape == [2 * M + 1] + +# 定义指示函数 +ind_func = lambda p: levelset_fourier(p, M, coefficients[idx, ...], origin_point) + +# 创建近场数据生成器 +generator = NearFieldDataFEMGenerator2d( + domain=domain, + mesh='UniformMesh', + nx=100, + ny=100, + p=1, + q=3, + u_inc=u_inc, + levelset=ind_func, + d=d, + k=k, + reciever_points=reciever_points +) + +# 可视化近场数据 +generator.visualization_of_nearfield_data(k=k[0], d=d[0]) diff --git a/app/lafem-ims/test/test_parallel_data_generation.py b/app/lafem-ims/test/test_parallel_data_generation.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/fealpy/fem/scalar_diffusion_integrator.py b/fealpy/fem/scalar_diffusion_integrator.py index 4371c3129..f4665c930 100644 --- a/fealpy/fem/scalar_diffusion_integrator.py +++ b/fealpy/fem/scalar_diffusion_integrator.py @@ -63,7 +63,6 @@ def assembly(self, space: _FS) -> TensorLike: bcs, ws, cm = self.fetch(space) coef = process_coef_func(coef, bcs=bcs, mesh=mesh, etype='cell', index=self.index) gphi = self.fetch_gphix(space) - return bilinear_integral(gphi, gphi, ws, cm, coef, batched=self.batched) @assemblymethod('fast') diff --git a/fealpy/ml/generator/near_field_data_generator.py b/fealpy/ml/generator/near_field_data_generator.py index 44af20148..92d4f00db 100644 --- a/fealpy/ml/generator/near_field_data_generator.py +++ b/fealpy/ml/generator/near_field_data_generator.py @@ -1,34 +1,40 @@ -import os -from fealpy.backend import backend_manager as bm +import os +import matplotlib.pyplot as plt from numpy.typing import NDArray import numpy as np -from fealpy.solver import spsolve from typing import Sequence, Callable -import matplotlib.pyplot as plt +# from scipy.sparse.linalg import spsolve +# from fealpy.backend import backend_manager as np from fealpy.mesh import TriangleMesh, QuadrangleMesh, UniformMesh2d -from fealpy.functionspace import LagrangeFESpace -from fealpy.fem import ScalarDiffusionIntegrator, ScalarMassIntegrator, ScalarSourceIntegrator, ScalarConvectionIntegrator, DirichletBC -from fealpy.fem import BilinearForm, LinearForm from fealpy.pde.pml_2d import PMLPDEModel2d - -bm.set_backend('numpy') +from fealpy.functionspace import LagrangeFESpace +from fealpy.fem import ( + ScalarDiffusionIntegrator, + ScalarMassIntegrator, + ScalarSourceIntegrator, + ScalarConvectionIntegrator, + BilinearForm, + LinearForm, + DirichletBC +) +from fealpy.solver import spsolve, cg class NearFieldDataFEMGenerator2d: def __init__(self, - domain:Sequence[float], - mesh:str, - nx:int, - ny:int, - p:int, - q:int, - u_inc:str, - levelset:Callable[[NDArray], NDArray], - d:Sequence[float], - k:Sequence[float], - reciever_points:NDArray): + domain: Sequence[float], + mesh: str, + nx: int, + ny: int, + p: int, + q: int, + u_inc: str, + levelset: Callable[[NDArray], NDArray], + d: Sequence[float], + k: Sequence[float], + reciever_points: NDArray): self.domain = domain self.nx = nx @@ -38,52 +44,65 @@ def __init__(self, self.u_inc = u_inc self.levelset = levelset + # 验证并创建网格 if mesh not in ['InterfaceMesh', 'QuadrangleMesh', 'UniformMesh']: raise ValueError("Invalid value for 'mesh'. Choose from 'InterfaceMesh', 'QuadrangleMesh' or 'UniformMesh'.") + + if mesh == 'InterfaceMesh': + self.mesh = TriangleMesh.interfacemesh_generator(box=self.domain, nx=self.nx, ny=self.ny, phi=self.levelset) + self.meshtype = 'InterfaceMesh' + elif mesh == 'QuadrangleMesh': + self.mesh = QuadrangleMesh.from_box(box=self.domain, nx=self.nx, ny=self.ny) + self.meshtype = 'QuadrangleMesh' else: - if mesh == 'InterfaceMesh': - self.mesh = TriangleMesh.interfacemesh_generator(box=self.domain, nx=self.nx, ny=self.ny, phi=self.levelset) - self.meshtype = 'InterfaceMesh' - elif mesh == 'QuadrangleMesh': - self.mesh = QuadrangleMesh.from_box(box=self.domain, nx=self.nx, ny=self.ny) - self.meshtype = 'QuadrangleMesh' - else: - EXTC_1 = self.nx - EXTC_2 = self.ny - HC_1 = 1/EXTC_1 * (self.domain[1] - self.domain[0]) - HC_2 = 1/EXTC_2 * (self.domain[3] - self.domain[2]) - self.mesh = UniformMesh2d((0, EXTC_1, 0, EXTC_2), (HC_1, HC_2), origin=(self.domain[0], self.domain[2])) - self.meshtype = 'UniformMesh' - - self.d = d + EXTC_1 = self.nx + EXTC_2 = self.ny + HC_1 = 1 / EXTC_1 * (self.domain[1] - self.domain[0]) + HC_2 = 1 / EXTC_2 * (self.domain[3] - self.domain[2]) + self.mesh = UniformMesh2d((0, EXTC_1, 0, EXTC_2), (HC_1, HC_2), origin=(self.domain[0], self.domain[2])) + self.meshtype = 'UniformMesh' + + self.d = d self.k = k self.reciever_points = reciever_points qf = self.mesh.quadrature_formula(self.q, 'cell') - self.bc, _= qf.get_quadrature_points_and_weights() - - def get_nearfield_data(self, k:float, d:Sequence[float]): - - k_index = (self.k).index(k) - d_index = (self.d).index(d) - pde = PMLPDEModel2d(levelset=self.levelset, - domain=self.domain, - u_inc=self.u_inc, - A=1, - k=self.k[k_index], - d=self.d[d_index], - refractive_index=[1, 1+1/self.k[k_index]**2], - absortion_constant=1.79, - lx=1.0, - ly=1.0 - ) + self.bc, _ = qf.get_quadrature_points_and_weights() + + def get_nearfield_data(self, k: float, d: Sequence[float]): + """ + 获取近场数据。 + + 参数: + - k: 波数 + - d: 波矢量方向 + + 返回: + - uh: 近场数据 + """ + k_index = self.k.index(k) + d_index = self.d.index(d) + pde = PMLPDEModel2d( + levelset=self.levelset, + domain=self.domain, + u_inc=self.u_inc, + A=1, + k=self.k[k_index], + d=self.d[d_index], + refractive_index=[1, 1 + 1 / self.k[k_index]**2], + absortion_constant=1.79, + lx=1.0, + ly=1.0 + ) space = LagrangeFESpace(self.mesh, p=self.p) + # 定义积分器 D = ScalarDiffusionIntegrator(pde.diffusion_coefficient, q=self.q) C = ScalarConvectionIntegrator(pde.convection_coefficient, q=self.q) M = ScalarMassIntegrator(pde.reaction_coefficient, q=self.q) f = ScalarSourceIntegrator(pde.source, q=self.q) + # 组装双线性形式和线性形式 b = BilinearForm(space) b.add_integrator([D, C, M]) @@ -92,14 +111,30 @@ def get_nearfield_data(self, k:float, d:Sequence[float]): A = b.assembly() F = l.assembly() + bc = DirichletBC(space, pde.dirichlet) - uh = space.function(dtype=bm.complex128) + uh = space.function(dtype=np.complex128) A, F = bc.apply(A, F) uh[:] = spsolve(A, F, solver='scipy') + # uh[:] = cg(A, F) + # uh[:] = spsolve(A.to_scipy(), F) + print(uh.shape) return uh - - def points_location_and_bc(self, p, domain:Sequence[float], nx:int, ny:int): + def points_location_and_bc(self, p: NDArray, domain: Sequence[float], nx: int, ny: int): + """ + 计算接收点的位置和重心坐标。 + + 参数: + - p: 接收点坐标 + - domain: 计算域 + - nx: x方向的网格数 + - ny: y方向的网格数 + + 返回: + - location: 接收点所在单元的索引 + - bc: 重心坐标 + """ x = p[..., 0] y = p[..., 1] cell_length_x = (domain[1] - domain[0]) / nx @@ -110,22 +145,31 @@ def points_location_and_bc(self, p, domain:Sequence[float], nx:int, ny:int): bc_x_ = ((x - domain[0]) / cell_length_x) % 1 bc_y_ = ((y - domain[2]) / cell_length_y) % 1 - bc_x = bm.array([[bc_x_, 1 - bc_x_]], dtype=bm.float64) - bc_y = bm.array([[bc_y_, 1 - bc_y_]], dtype=bm.float64) + bc_x = np.array([[bc_x_, 1 - bc_x_]], dtype=np.float64) + bc_y = np.array([[bc_y_, 1 - bc_y_]], dtype=np.float64) bc = (bc_x, bc_y) return location, bc - def data_for_dsm(self, k:float, d:Sequence[float]): + def data_for_dsm(self, k: float, d: Sequence[float]): + """ + 获取用于DSM的数据。 + + 参数: + - k: 波数 + - d: 波矢量方向 + 返回: + - data: DSM数据 + """ reciever_points = self.reciever_points data_length = reciever_points.shape[0] - data = bm.zeros((data_length, ), dtype=bm.complex128) + data = np.zeros((data_length,), dtype=np.complex128) uh = self.get_nearfield_data(k=k, d=d) - - if self.meshtype =='InterfaceMesh': + + if self.meshtype == 'InterfaceMesh': b = self.mesh.point_to_bc(reciever_points) location = self.mesh.location(reciever_points) - for i in range (data_length): + for i in range(data_length): data[i] = uh(b[i])[location[i]] elif self.meshtype == 'QuadrangleMesh': for i in range(data_length): @@ -142,14 +186,20 @@ def data_for_dsm(self, k:float, d:Sequence[float]): u = uh(b).reshape(-1) data[i] = u[location] return data - - def save(self, save_path:str, scatterer_index:int): + def save(self, save_path: str, scatterer_index: int): + """ + 保存数据。 + + 参数: + - save_path: 保存路径 + - scatterer_index: 散射体索引 + """ k_values = self.k d_values = self.d data_dict = {} - for i in range (len(k_values)): - for j in range (len(d_values)): + for i in range(len(k_values)): + for j in range(len(d_values)): k_name = f'k={k_values[i]}' d_name = d_values[j] name = f"{k_name}, d={d_name}" @@ -157,24 +207,30 @@ def save(self, save_path:str, scatterer_index:int): filename = os.path.join(save_path, f"data_for_dsm_{scatterer_index}.npz") np.savez(filename, **data_dict) - def visualization_of_nearfield_data(self, k:float, d:Sequence[float]): + def visualization_of_nearfield_data(self, k: float, d: Sequence[float]): + """ + 可视化近场数据。 + 参数: + - k: 波数 + - d: 波矢量方向 + """ uh = self.get_nearfield_data(k=k, d=d) value = uh(self.bc) if self.meshtype == 'UniformMesh': - self.mesh.ftype = bm.float64 + self.mesh.ftype = np.float64 self.mesh.add_plot(plt, cellcolor=value[..., 0].real, linewidths=0) self.mesh.add_plot(plt, cellcolor=value[..., 0].imag, linewidths=0) - #TODO + # TODO: 添加更多可视化选项 # fig = plt.figure() # axes = fig.add_subplot(1, 3, 1) # self.mesh.add_plot(axes) # if self.meshtype == 'UniformMesh': - # uh = uh.view(bm.ndarray) + # uh = uh.view(np.ndarray) # axes = fig.add_subplot(1, 3, 2, projection='3d') - # self.mesh.show_function(axes, bm.real(uh)) + # self.mesh.show_function(axes, np.real(uh)) # axes = fig.add_subplot(1, 3, 3, projection='3d') - # self.mesh.show_function(axes, bm.imag(uh)) + # self.mesh.show_function(axes, np.imag(uh)) plt.show() \ No newline at end of file From 4c85bd38c7696655ec7e24e606da3710dcae3e4b Mon Sep 17 00:00:00 2001 From: chaos <2250213115@qq.com> Date: Mon, 25 Nov 2024 19:07:57 +0800 Subject: [PATCH 34/63] - --- fealpy/fem/scalar_diffusion_integrator.py | 1 + 1 file changed, 1 insertion(+) diff --git a/fealpy/fem/scalar_diffusion_integrator.py b/fealpy/fem/scalar_diffusion_integrator.py index f4665c930..3b21c1dad 100644 --- a/fealpy/fem/scalar_diffusion_integrator.py +++ b/fealpy/fem/scalar_diffusion_integrator.py @@ -63,6 +63,7 @@ def assembly(self, space: _FS) -> TensorLike: bcs, ws, cm = self.fetch(space) coef = process_coef_func(coef, bcs=bcs, mesh=mesh, etype='cell', index=self.index) gphi = self.fetch_gphix(space) + return bilinear_integral(gphi, gphi, ws, cm, coef, batched=self.batched) @assemblymethod('fast') From cb98c39c6a6660039513abcc8859a915d9c86897 Mon Sep 17 00:00:00 2001 From: chaos <2250213115@qq.com> Date: Mon, 25 Nov 2024 19:09:53 +0800 Subject: [PATCH 35/63] - --- .../ml/generator/near_field_data_generator.py | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/fealpy/ml/generator/near_field_data_generator.py b/fealpy/ml/generator/near_field_data_generator.py index 92d4f00db..19bbb4b13 100644 --- a/fealpy/ml/generator/near_field_data_generator.py +++ b/fealpy/ml/generator/near_field_data_generator.py @@ -2,11 +2,9 @@ import os import matplotlib.pyplot as plt from numpy.typing import NDArray -import numpy as np +import numpy as bm from typing import Sequence, Callable -# from scipy.sparse.linalg import spsolve -# from fealpy.backend import backend_manager as np from fealpy.mesh import TriangleMesh, QuadrangleMesh, UniformMesh2d from fealpy.pde.pml_2d import PMLPDEModel2d from fealpy.functionspace import LagrangeFESpace @@ -19,7 +17,7 @@ LinearForm, DirichletBC ) -from fealpy.solver import spsolve, cg +from fealpy.solver import spsolve class NearFieldDataFEMGenerator2d: @@ -113,12 +111,9 @@ def get_nearfield_data(self, k: float, d: Sequence[float]): F = l.assembly() bc = DirichletBC(space, pde.dirichlet) - uh = space.function(dtype=np.complex128) + uh = space.function(dtype=bm.complex128) A, F = bc.apply(A, F) uh[:] = spsolve(A, F, solver='scipy') - # uh[:] = cg(A, F) - # uh[:] = spsolve(A.to_scipy(), F) - print(uh.shape) return uh def points_location_and_bc(self, p: NDArray, domain: Sequence[float], nx: int, ny: int): @@ -145,8 +140,8 @@ def points_location_and_bc(self, p: NDArray, domain: Sequence[float], nx: int, n bc_x_ = ((x - domain[0]) / cell_length_x) % 1 bc_y_ = ((y - domain[2]) / cell_length_y) % 1 - bc_x = np.array([[bc_x_, 1 - bc_x_]], dtype=np.float64) - bc_y = np.array([[bc_y_, 1 - bc_y_]], dtype=np.float64) + bc_x = bm.array([[bc_x_, 1 - bc_x_]], dtype=bm.float64) + bc_y = bm.array([[bc_y_, 1 - bc_y_]], dtype=bm.float64) bc = (bc_x, bc_y) return location, bc @@ -163,7 +158,7 @@ def data_for_dsm(self, k: float, d: Sequence[float]): """ reciever_points = self.reciever_points data_length = reciever_points.shape[0] - data = np.zeros((data_length,), dtype=np.complex128) + data = bm.zeros((data_length,), dtype=bm.complex128) uh = self.get_nearfield_data(k=k, d=d) if self.meshtype == 'InterfaceMesh': @@ -205,7 +200,7 @@ def save(self, save_path: str, scatterer_index: int): name = f"{k_name}, d={d_name}" data_dict[name] = self.data_for_dsm(k=k_values[i], d=d_values[j]) filename = os.path.join(save_path, f"data_for_dsm_{scatterer_index}.npz") - np.savez(filename, **data_dict) + bm.savez(filename, **data_dict) def visualization_of_nearfield_data(self, k: float, d: Sequence[float]): """ @@ -218,7 +213,7 @@ def visualization_of_nearfield_data(self, k: float, d: Sequence[float]): uh = self.get_nearfield_data(k=k, d=d) value = uh(self.bc) if self.meshtype == 'UniformMesh': - self.mesh.ftype = np.float64 + self.mesh.ftype = bm.float64 self.mesh.add_plot(plt, cellcolor=value[..., 0].real, linewidths=0) self.mesh.add_plot(plt, cellcolor=value[..., 0].imag, linewidths=0) @@ -227,10 +222,10 @@ def visualization_of_nearfield_data(self, k: float, d: Sequence[float]): # axes = fig.add_subplot(1, 3, 1) # self.mesh.add_plot(axes) # if self.meshtype == 'UniformMesh': - # uh = uh.view(np.ndarray) + # uh = uh.view(bm.ndarray) # axes = fig.add_subplot(1, 3, 2, projection='3d') - # self.mesh.show_function(axes, np.real(uh)) + # self.mesh.show_function(axes, bm.real(uh)) # axes = fig.add_subplot(1, 3, 3, projection='3d') - # self.mesh.show_function(axes, np.imag(uh)) + # self.mesh.show_function(axes, bm.imag(uh)) plt.show() \ No newline at end of file From b21b95a4f361b87767530f9ed62e3b1dd849d2d5 Mon Sep 17 00:00:00 2001 From: july <1985547582@qq.com> Date: Mon, 25 Nov 2024 19:58:57 +0800 Subject: [PATCH 36/63] update --- example/fem/poisson_BDM_neumann.py | 7 +- ...BrezziDouglasMariniFiniteElementSpace3d.py | 472 ++++++++++++++++++ fealpy/functionspace/__init__.py | 3 +- ...iDouglasMariniFiniteElementSpace3d_data.py | 431 ++++++++++++++++ ...BrezziDouglasMariniFiniteElementSpace3d.py | 73 +++ 5 files changed, 981 insertions(+), 5 deletions(-) create mode 100644 fealpy/functionspace/BrezziDouglasMariniFiniteElementSpace3d.py create mode 100644 test/functionspace/BrezziDouglasMariniFiniteElementSpace3d_data.py create mode 100644 test/functionspace/test_BrezziDouglasMariniFiniteElementSpace3d.py diff --git a/example/fem/poisson_BDM_neumann.py b/example/fem/poisson_BDM_neumann.py index efccd2347..a2f8a2afd 100644 --- a/example/fem/poisson_BDM_neumann.py +++ b/example/fem/poisson_BDM_neumann.py @@ -4,7 +4,7 @@ import matplotlib.pyplot as plt from fealpy.mesh import TriangleMesh, TetrahedronMesh -from fealpy.functionspace import BDMFiniteElementSpace2d +from fealpy.functionspace import BDMFiniteElementSpace2d,BDMFiniteElementSpace3d from fealpy.functionspace import LagrangeFESpace from fealpy import logger logger.setLevel('WARNING') @@ -59,7 +59,6 @@ else: pde = PDE3d() - errorType = ['$|| p - p_h||_0$ with k=2', '$|| p - p_h||_0$ with k=3', '$|| p - p_h||_0$ with k=4', @@ -83,8 +82,8 @@ space2 = BDMFiniteElementSpace2d(mesh, p=p) else: mesh = TetrahedronMesh.from_box([0, 1, 0, 1, 0, 1], nx=2**i, ny=2**i, nz=2**i) - space1 = LagrangeFESpace(mesh,p=p,ctype="D") - #space2 = RTFiniteElementSpace3d(mesh, p=p) + space1 = LagrangeFESpace(mesh,p=p-1,ctype="D") + space2 = BDMFiniteElementSpace3d(mesh, p=p) tmr.send(f'第{i}次网格和pde生成时间') pdof = space1.dof.number_of_global_dofs() diff --git a/fealpy/functionspace/BrezziDouglasMariniFiniteElementSpace3d.py b/fealpy/functionspace/BrezziDouglasMariniFiniteElementSpace3d.py new file mode 100644 index 000000000..b0a6ef846 --- /dev/null +++ b/fealpy/functionspace/BrezziDouglasMariniFiniteElementSpace3d.py @@ -0,0 +1,472 @@ + +from typing import Union, TypeVar, Generic, Callable,Optional + +from ..backend import TensorLike +from ..backend import backend_manager as bm +from .space import FunctionSpace +from .lagrange_fe_space import LagrangeFESpace +from .function import Function + +from scipy.sparse import csr_matrix +from ..mesh.mesh_base import Mesh +from ..decorator import barycentric, cartesian + + +_MT = TypeVar('_MT', bound=Mesh) +Index = Union[int, slice, TensorLike] +Number = Union[int, float] +_S = slice(None) + + +class BDMDof(): + def __init__(self, mesh, p): + self.mesh = mesh + self.p = p + self.multiindex2 = mesh.multi_index_matrix(p,2) + self.multiindex3 = mesh.multi_index_matrix(p,3) + self.ftype = mesh.ftype + self.itype = mesh.itype + self.device=mesh.device + + + def edge_to_local_face_dof(self): + multiindex = self.multiindex2 + ldof = self.number_of_local_dofs() + + e2ld = bm.zeros((3, self.p+1),device=self.device, dtype=self.itype) + + e2ld[0], = bm.where(multiindex[:, 0]==0) + + #e2ld[1] = bm.where(multiindex[:, 1]==0)[0][::-1] + array = bm.where(multiindex[:, 1]==0)[0] + e2ld[1] = bm.flip(array) + + e2ld[2], = bm.where(multiindex[:, 2]==0) + return e2ld + + def cell_to_dof(self): + face = self.mesh.entity("face") + cell = self.mesh.entity("cell") + + multiindex = self.multiindex2.T #(3, ldof) + cell2face = self.mesh.cell_to_face() + localFace = bm.array([(1, 2, 3), (0, 2, 3), (0, 1, 3), (0, 1, 2)],device=self.device, dtype=self.itype) + + f2d = self.face_to_dof() + f2ld = self.face_to_local_dof() + NC = self.mesh.number_of_cells() + NF = self.mesh.number_of_faces() + ldof = self.number_of_local_dofs() + gdof = self.number_of_global_dofs() + cdof = self.number_of_local_dofs('cell') + fdof = self.number_of_local_dofs('face') + + isndof = bm.zeros(ldof,device=self.device, dtype=bm.bool) + isndof[f2ld] = True + + c2d = bm.zeros((NC, ldof),device=self.device, dtype=self.itype) + idx = bm.zeros((NC, 3),device=self.device, dtype=self.itype) + for i in range(4): + fi = face[cell2face[:, i]] #(NC, 3) + fj = cell[:, localFace[i]] + _, idx[:, 0] = bm.where(fj==fi[:, 0, None]) + _, idx[:, 1] = bm.where(fj==fi[:, 1, None]) + _, idx[:, 2] = bm.where(fj==fi[:, 2, None]) + k = multiindex[idx[:, 1]] + multiindex[idx[:, 2]] #(NC, fdof) + didx = k*(k+1)//2+multiindex[idx[:, 2]] + #c2d[:, f2ld[i]] = f2d[cell2face[:, [i]], didx] + c2d = bm.set_at(c2d,(slice(None), f2ld[i]), f2d[cell2face[:, [i]], didx]) + #c2d[:, ~isndof] = bm.arange(NF*fdof, gdof).reshape(NC, cdof) + c2d = bm.set_at(c2d,(slice(None), ~isndof), bm.arange(NF*fdof, gdof).reshape(NC, cdof)) + return c2d + + @property + def cell2dof(self): + return self.cell_to_dof() + + def face_to_local_dof(self): + multiindex = self.multiindex3 + ldof = self.number_of_local_dofs() + + fdof = self.number_of_local_dofs('face') + f2ld = bm.zeros((4, fdof),device=self.device, dtype=self.itype) + eldof = self.edge_to_local_face_dof() + + f2ld[0], = bm.where(multiindex[:, 0]==0) + # f2ld[0, eldof[:, 1:-1]] += ldof//3 # 底面编号最大 + # f2ld[0, eldof[:, 0]] += 2*(ldof//3) + f2ld = bm.set_at(f2ld,(0, eldof[:, 1:-1]), f2ld[0, eldof[:, 1:-1]]+ldof//3) + f2ld = bm.set_at(f2ld,(0, eldof[:, 0]), f2ld[0, eldof[:, 0]]+2*(ldof//3)) + + f2ld[1], = bm.where(multiindex[:, 1]==0) + # f2ld[1, eldof[[1, 2], :]] += ldof//3 # 底面编号最大 + # f2ld[1, eldof[1, -1]] += ldof//3 + f2ld = bm.set_at(f2ld,(1, eldof[[1, 2], :]), f2ld[1, eldof[[1, 2], :]]+ldof//3) + f2ld = bm.set_at(f2ld,(1, eldof[1, -1]), f2ld[1, eldof[1, -1]]+ldof//3) + + f2ld[2], = bm.where(multiindex[:, 2]==0) + # f2ld[2, eldof[2, :]] += ldof//3 # 底面编号最大 + f2ld = bm.set_at(f2ld,(2, eldof[2, :]), f2ld[2, eldof[2, :]]+ldof//3) + + f2ld[3], = bm.where(multiindex[:, 3]==0) + return f2ld + + def face_to_dof(self, index=_S): + NF = self.mesh.number_of_faces() + fdof = self.number_of_local_dofs(doftype='face') + return bm.arange(NF*fdof,device=self.device, dtype=self.itype).reshape(NF, fdof)[index] + + def number_of_local_dofs(self, doftype='all'): + p = self.p + if doftype == 'all': # number of all dofs on a cell + return (p+1)*(p+2)*(p+3)//2 + elif doftype in {'cell'}: # number of dofs inside the cell + return ((p+1)*(p+2)*(p+3)//2) - 2*(p+1)*(p+2) + elif doftype in {'face'}: # number of dofs on each edge + return (p+1)*(p+2)//2 + elif doftype in {'node', 0}: # number of dofs on each node + return 0 + + def number_of_global_dofs(self): + NC = self.mesh.number_of_cells() + NF = self.mesh.number_of_faces() + fdof = self.number_of_local_dofs(doftype='face') + cdof = self.number_of_local_dofs(doftype='cell') + return NF*fdof + NC*cdof + + def is_boundary_dof(self): + + gdof = self.number_of_global_dofs() + index = self.mesh.boundary_face_index() + face2dof = self.face_to_dof()[index] + + isDDof = bm.zeros(gdof, dtype=bm.bool) + isDDof[face2dof] = True + return isDDof + +class BDMFiniteElementSpace3d(FunctionSpace, Generic[_MT]): + def __init__(self, mesh, p, space=None, q = None): + + self.p = p + self.mesh = mesh + self.dof = BDMDof(mesh, p) + + self.lspace = LagrangeFESpace(mesh, p) + + self.cellmeasure = mesh.entity_measure('cell') + self.qf = mesh.quadrature_formula(p+3) + self.ftype = mesh.ftype + self.itype = mesh.itype + + #TODO:JAX + self.device = mesh.device + self.TD = mesh.top_dimension() + self.GD = mesh.geo_dimension() + + + @barycentric + def basis(self, bcs: TensorLike,index=_S)-> TensorLike: + mesh = self.mesh + NC = mesh.number_of_cells() + GD = mesh.geo_dimension() + ldof = self.dof.number_of_local_dofs() + gdof = self.dof.number_of_global_dofs() + + node = mesh.entity("node") + cell = mesh.entity("cell") + + c2v = self.basis_vector()#(NC, ldof, GD) + + shape = bcs.shape[:-1] + val = bm.zeros((NC,)+shape+(ldof, GD),device=self.device, dtype=self.ftype) + + bval = self.lspace.basis(bcs) #(NQ, NC, ldof//3) + c2v = c2v[:,None,:,:] + c2v = bm.broadcast_to(c2v, val.shape) + + # val[..., :ldof//3, :] = bval[..., None]*c2v[..., :ldof//3, :] + # val[..., ldof//3 : 2*(ldof//3):, :] = bval[..., None]*c2v[..., ldof//3:2*(ldof//3), :] + # val[..., 2*(ldof//3):, :] = bval[..., None]*c2v[..., 2*(ldof//3):, :] + val =bm.set_at(val,(...,slice(None,ldof//3),slice(None)), bval[..., None]*c2v[..., :ldof//3, :]) + val =bm.set_at(val,(...,slice(ldof//3,2*(ldof//3)),slice(None)), bval[..., None]*c2v[..., ldof//3:2*(ldof//3), :]) + val =bm.set_at(val,(...,slice(2*(ldof//3),None),slice(None)), bval[..., None]*c2v[..., 2*(ldof//3):, :]) + + return val[index] + + @barycentric + def face_basis(self, bcs, index=_S): + mesh = self.mesh + GD = mesh.geo_dimension() + fdof = self.dof.number_of_local_dofs('face') + sphi = self.lspace.basis(bcs, index=index) #(NQ, NF, edof) + + f2n = mesh.face_unit_normal() + val = sphi[..., None]*f2n[index,None, None, :] + return val + + @barycentric + def div_basis(self, bcs: TensorLike,index=_S)->TensorLike: + mesh = self.mesh + NC = mesh.number_of_cells() + GD = mesh.geo_dimension() + ldof = self.dof.number_of_local_dofs() + gdof = self.dof.number_of_global_dofs() + + node = mesh.entity("node") + cell = mesh.entity("cell") + + c2v = self.basis_vector()#(NC, ldof, GD) + + shape = bcs.shape[:-1] + val = bm.zeros((NC,)+shape+(ldof,), dtype=self.ftype, device=self.device) + + sgval = self.lspace.grad_basis(bcs) #(NQ, NC, ldof, GD) + c2v = c2v[:,None,:,:] + c2v = bm.broadcast_to(c2v, val.shape+(GD,)) + # val[..., :ldof//3] = bm.einsum('ijkl, ijkl->ijk', sgval, c2v[..., :ldof//3, :]) + # val[..., ldof//3:2*(ldof//3)] = bm.einsum('ijkl, ijkl->ijk', sgval, c2v[..., ldof//3:2*(ldof//3), :]) + # val[..., -ldof//3:] = bm.einsum('ijkl, ijkl->ijk', sgval, c2v[..., -ldof//3:, :]) + val = bm.set_at(val,(...,slice(None,ldof//3)), bm.einsum('ijkl, ijkl->ijk', sgval, c2v[..., :ldof//3, :])) + val = bm.set_at(val,(...,slice(ldof//3,2*(ldof//3))), bm.einsum('ijkl, ijkl->ijk', sgval, c2v[..., ldof//3:2*(ldof//3), :])) + val = bm.set_at(val,(...,slice(2*(ldof//3),None)), bm.einsum('ijkl, ijkl->ijk', sgval, c2v[..., 2*(ldof//3):, :])) + + return val[index] + + def basis_vector(self): + + NC = self.mesh.number_of_cells() + + c2e = self.mesh.cell_to_edge() + c2f = self.mesh.cell_to_face() + f2e = self.mesh.face_to_edge() + e2t = self.mesh.edge_tangent() + em = self.mesh.entity_measure("edge") + e2t = e2t/em[:, None] + f2n = self.mesh.face_unit_normal() + + lf2f = bm.tensor([(1, 2, 3), (0, 2, 3), (0, 1, 3), (0, 1, 2)],device=self.device, dtype=self.itype) + lf2e = bm.tensor([[5, 4, 3], [5, 2, 1], [4, 2, 0], [3, 1, 0]],device=self.device, dtype=self.itype) #(4, 3) + + e2fdof = self.dof.edge_to_local_face_dof() #(3, p+1) 面上, 在边界的自由度 + f2ldof = self.dof.face_to_local_dof() # 单元上, 在面上的自由度 + ldof = self.dof.number_of_local_dofs() + fdof = self.dof.number_of_local_dofs('face') + + bv = bm.zeros((NC, ldof, 3), device=self.device, dtype=self.ftype) + + # 内部向量 + # bv[:, :ldof//3, 0] = 1 + # bv[:, ldof//3:2*(ldof//3), 1] = 1 + # bv[:, -ldof//3:, 2] = 1 + + bv = bm.set_at(bv,(slice(None), slice(None,ldof//3), 0), 1) + bv = bm.set_at(bv,(slice(None), slice(ldof//3,2*(ldof//3)), 1), 1) + bv = bm.set_at(bv,(slice(None), slice(-ldof//3,None), 2), 1) + + # 面上的内部自由度 + isfndof = bm.ones(fdof, dtype=bm.bool) + isfndof[e2fdof] = False + + # 面上的法向与切向 + # bv[:, f2d] : (NC, 4, fdof, 3), f2n[c2f] : (NC, 4, 3), c2e[:, lf2e[:, 1]] : (NC, 4) + # bv[:, f2ldof[:, isfndof]] = f2n[c2f, None] + # bv[:, f2ldof[:, isfndof]+ldof//3] = e2t[c2e[:, lf2e[:, 0]], None] + # bv[:, f2ldof[:, isfndof]+2*(ldof//3)] = bm.linalg.cross(f2n[c2f, None], e2t[c2e[:, lf2e[:, 0]], None]) + + bv = bm.set_at(bv,(slice(None), f2ldof[:, isfndof]), f2n[c2f, None]) + bv = bm.set_at(bv,(slice(None), f2ldof[:, isfndof]+ldof//3), e2t[c2e[:, lf2e[:, 0]], None]) + bv = bm.set_at(bv,(slice(None), f2ldof[:, isfndof]+2*(ldof//3)), bm.linalg.cross(f2n[c2f, None], e2t[c2e[:, lf2e[:, 0]], None])) + + #边上的法向 + # bv[:, f2ldof[:, eldof]] (NC, 4, 3, eldof, 3) + # e2t[c2e[:, lf2e], None] : (NC, 4, 3, 1, 3), f2n[c2f[:, lf2f], None] : (NC, 4, 3, 1, 3) + tmp = bm.linalg.cross(e2t[c2e[:, lf2e], None], f2n[c2f[:, lf2f], None]) + #bv[:, f2ldof[:, e2fdof[:, 1:-1]]] = tmp/bm.sum(tmp*f2n[c2f, None, None], axis=-1)[..., None] + bv = bm.set_at(bv,(slice(None), f2ldof[:, e2fdof[:, 1:-1]]), tmp/bm.sum(tmp*f2n[c2f, None,None], axis=-1)[..., None]) + + # 边上的切向 + # bv[:, f2ldof[3, e2fdof[:, 1:-1]]+2*(ldof//3)] = e2t[c2e[:, lf2e[3]], None] + # bv[:, f2ldof[2, e2fdof[[0, 1], 1:-1]]+2*(ldof//3)] = e2t[c2e[:, lf2e[2, [0, 1]]], None] + # bv[:, f2ldof[1, e2fdof[[0], 1:-1]]+2*(ldof//3)] = e2t[c2e[:, lf2e[1, [0]]], None] + + bv = bm.set_at(bv,(slice(None), f2ldof[3, e2fdof[:, 1:-1]]+2*(ldof//3)), e2t[c2e[:, lf2e[3]], None]) + bv = bm.set_at(bv,(slice(None), f2ldof[2, e2fdof[[0, 1], 1:-1]]+2*(ldof//3)), e2t[c2e[:, lf2e[2, [0, 1]]], None]) + bv = bm.set_at(bv,(slice(None), f2ldof[1, e2fdof[[0], 1:-1]]+2*(ldof//3)), e2t[c2e[:, lf2e[1, [0]]], None]) + + # 面的三个顶点连接的, 不再这个面上的边 + lf2se = bm.tensor([[1, 2, 0], [3, 4, 0], [3, 5, 1], [4, 5, 2]],device=self.device, dtype=self.itype) #(4, 3) + + # 顶点上的法向 + tmp = e2t[c2e[:, lf2se]] #(NC, 4, 3, 3) + # bv[:, f2ldof[:, e2fdof[:, 0]]] = tmp/bm.sum(tmp*f2n[c2f, None], axis=-1)[..., None] + bv = bm.set_at(bv,(slice(None), f2ldof[:, e2fdof[:, 0]]), tmp/bm.sum(tmp*f2n[c2f, None], axis=-1)[..., None]) + return bv + + def dof_vector(self): + bv = self.basis_vector() + NC = bv.shape[0] + dv = bm.linalg.inv(bv.reshape(NC, 3, -1, 3).swapaxes(1, 2)).swapaxes(-1, + -2).swapaxes(1, 2).reshape(NC, -1, 3) + return dv + + def mass_matrix(self): + mesh = self.mesh + NC = mesh.number_of_cells() + ldof = self.dof.number_of_local_dofs() + gdof = self.dof.number_of_global_dofs() + cm = self.cellmeasure + c2d = self.dof.cell_to_dof() #(NC, ldof) + + bcs, ws = self.qf.get_quadrature_points_and_weights() + phi = self.basis(bcs) #(NQ, NC, ldof, GD) + mass = bm.einsum("cqlg, cqdg, c, q->cld", phi, phi, cm, ws) + + I = bm.broadcast_to(c2d[:, :, None], shape=mass.shape) + J = bm.broadcast_to(c2d[:, None, :], shape=mass.shape) + M = csr_matrix((mass.flat, (I.flat, J.flat)), shape=(gdof, gdof)) + return M + + def div_matrix(self, space): + mesh = self.mesh + NC = mesh.number_of_cells() + ldof = self.dof.number_of_local_dofs() + gdof0 = self.dof.number_of_global_dofs() + gdof1 = space.dof.number_of_global_dofs() + cm = self.cellmeasure + + c2d = self.dof.cell_to_dof() #(NC, ldof) + c2d_space = space.dof.cell_to_dof() + + bcs, ws = self.qf.get_quadrature_points_and_weights() + + # if space.basis.coordtype == 'barycentric': + fval = space.basis(bcs) #(NQ, NC, ldof1) + # else: + # points = self.mesh.bc_to_point(bcs) + # fval = space.basis(points) + + phi = self.div_basis(bcs) #(NQ, NC, ldof) + A = bm.einsum("cql, cqd, c, q->cld", phi, fval, cm, ws) + + I = bm.broadcast_to(c2d[:, :, None], shape=A.shape) + J = bm.broadcast_to(c2d_space[:, None, :], shape=A.shape) + B = csr_matrix((A.flat, (I.flat, J.flat)), shape=(gdof0, gdof1)) + return B + +# def projection(self, f, method="L2"): +# M = self.mass_matrix() +# b = self.source_vector(f) +# x = spsolve(M, b) +# return self.function(array=x) + +# def function(self, dim=None, array=None, dtype=np.float_): +# if array is None: +# gdof = self.dof.number_of_global_dofs() +# array = np.zeros(gdof, dtype=np.float_) +# return Function(self, dim=dim, array=array, coordtype='barycentric', dtype=dtype) + +# def interplation(self, f): +# pass + def cell_to_dof(self): + return self.dof.cell2dof + + def number_of_global_dofs(self): + return self.dof.number_of_global_dofs() + + def number_of_local_dofs(self, doftype='all'): + return self.dof.number_of_local_dofs(doftype) + + def is_boundary_dof(self, threshold=None,method=None): + return self.dof.is_boundary_dof() + + @barycentric + def value(self, uh, bcs, index=_S): + '''@ + @brief 计算一个有限元函数在每个单元的 bc 处的值 + @param bc : (..., GD+1) + @return val : (..., NC, GD) + ''' + phi = self.basis(bcs) + c2d = self.dof.cell_to_dof() + # uh[c2d].shape = (NC, ldof); phi.shape = (..., NC, ldof, GD) + val = bm.einsum("cl, cqlk->cqk", uh[c2d], phi) + return val + +# def L2_error(self, u, uh): +# '''@ +# @brief 计算 ||u - uh||_{L_2} +# ''' +# mesh = self.mesh +# cm = self.cellmeasure +# bcs, ws = self.integrator.get_quadrature_points_and_weights() +# p = mesh.bc_to_point(bcs) #(NQ, NC, GD) +# uval = u(p) #(NQ, NC, GD) +# uhval = uh(bcs) #(NQ, NC, GD) +# errval = np.sum((uval-uhval)*(uval-uhval), axis=-1)#(NQ, NC) +# val = np.einsum("qc, q, c->", errval, ws, cm) +# return np.sqrt(val) + + def source_vector(self, f): + mesh = self.mesh + cm = self.cellmeasure + ldof = self.dof.number_of_local_dofs() + gdof = self.dof.number_of_global_dofs() + bcs, ws = self.qf.get_quadrature_points_and_weights() + c2d = self.dof.cell_to_dof() #(NC, ldof) + + p = mesh.bc_to_point(bcs) #(NQ, NC, GD) + fval = f(p) #(NQ, NC, GD) + + phi = self.basis(bcs) #(NQ, NC, ldof, GD) + val = bm.einsum("cqg, cqlg, q, c->cl", fval, phi, ws, cm)# (NC, ldof) + vec = bm.zeros(gdof, dtype=self.ftype, device=self.device) + bm.add.at(vec, c2d, val) + return vec + + def set_neumann_bc(self, g): + p = self.p + mesh = self.mesh + + qf = self.mesh.quadrature_formula(p+3, 'face') + bcs, ws = qf.get_quadrature_points_and_weights() + + fidx = self.mesh.boundary_face_index() + gdof = self.dof.number_of_global_dofs() + face2dof = self.dof.face_to_dof()[fidx] + + phi = self.face_basis(bcs)[fidx] #(NF, NQ, fdof, GD) + f2n = self.mesh.face_unit_normal(index=fidx) + phi = bm.einsum("fqlg, fg->fql", phi, f2n) #(NF, NQ, fdof) + + points = mesh.bc_to_point(bcs)[fidx] + gval = g(points) + + fm = self.mesh.entity_measure("face")[fidx] + vec = bm.zeros(gdof, device=self.device, dtype=self.ftype) + k= bm.einsum("fql, fq, f, q->fl", phi, gval, fm, ws) + bm.add.at(vec,face2dof,k) + return vec + +# def set_neumann_bc(self, g): +# bcs, ws = self.integralalg.faceintegrator.get_quadrature_points_and_weights() + +# fdof = self.dof.number_of_local_dofs('face') +# fidx = self.mesh.ds.boundary_face_index() +# phi = self.face_basis(bcs, index=fidx) #(NQ, NE0, edof, GD) +# f2n = self.mesh.face_unit_normal(index=fidx) +# phi = np.einsum("qelg, eg->qel", phi, f2n) #(NQ, NE0, edof) + +# point = self.mesh.bc_to_point(bcs, index=fidx) +# gval = g(point) #(NQ, NE0) + +# fm = self.mesh.entity_measure("face")[fidx] +# integ = np.einsum("qel, qe, e, q->el", phi, gval, fm, ws) + +# f2d = np.ones((len(fidx), fdof), dtype=np.int_) +# f2d[:, 0] = fdof*fidx +# f2d = np.cumsum(f2d, axis=-1) + +# gdof = self.dof.number_of_global_dofs() +# val = np.zeros(gdof, dtype=np.float_) +# np.add.at(val, f2d, integ) +# return val diff --git a/fealpy/functionspace/__init__.py b/fealpy/functionspace/__init__.py index b73ffef80..a8928e9d4 100644 --- a/fealpy/functionspace/__init__.py +++ b/fealpy/functionspace/__init__.py @@ -23,4 +23,5 @@ from .huzhang_fe_space_2d import HuZhangFESpace2D -from .BrezziDouglasMariniFiniteElementSpace2d import BDMFiniteElementSpace2d \ No newline at end of file +from .BrezziDouglasMariniFiniteElementSpace2d import BDMFiniteElementSpace2d +from .BrezziDouglasMariniFiniteElementSpace3d import BDMFiniteElementSpace3d \ No newline at end of file diff --git a/test/functionspace/BrezziDouglasMariniFiniteElementSpace3d_data.py b/test/functionspace/BrezziDouglasMariniFiniteElementSpace3d_data.py new file mode 100644 index 000000000..3a962c0e9 --- /dev/null +++ b/test/functionspace/BrezziDouglasMariniFiniteElementSpace3d_data.py @@ -0,0 +1,431 @@ +import numpy as np + +# 定义多个典型的 BDMDof 对象 +# TetrahedronMesh.from_box(nx = 1,ny =1,nz=1) +# p = 3 + +init_data = [ + { + "cdof":60, + "gdof":300, + "cell2dof":np.array([[ 80, 82, 81, 92, 85, 84, 94, 83, 114, 95, 89, 88, 97, + 87, 174, 98, 86, 118, 117, 99, 90, 91, 112, 111, 93, 180, + 181, 115, 182, 113, 96, 171, 172, 173, 183, 175, 119, 177, 178, + 116, 110, 184, 185, 186, 187, 188, 189, 190, 191, 192, 170, 193, + 194, 195, 196, 197, 176, 198, 199, 179], + [ 70, 72, 71, 102, 75, 74, 104, 73, 94, 105, 79, 78, 107, + 77, 164, 108, 76, 97, 98, 109, 100, 101, 91, 92, 103, 200, + 201, 93, 202, 95, 106, 161, 162, 163, 203, 165, 96, 167, 168, + 99, 90, 204, 205, 206, 207, 208, 209, 210, 211, 212, 160, 213, + 214, 215, 216, 217, 166, 218, 219, 169], + [ 10, 12, 11, 22, 15, 14, 24, 13, 104, 25, 19, 18, 27, + 17, 134, 28, 16, 107, 108, 29, 20, 21, 101, 102, 23, 220, + 221, 103, 222, 105, 26, 131, 132, 133, 223, 135, 106, 137, 138, + 109, 100, 224, 225, 226, 227, 228, 229, 230, 231, 232, 130, 233, + 234, 235, 236, 237, 136, 238, 239, 139], + [ 0, 2, 1, 62, 5, 4, 64, 3, 24, 65, 9, 8, 67, + 7, 124, 68, 6, 27, 28, 69, 60, 61, 21, 22, 63, 240, + 241, 23, 242, 25, 66, 121, 122, 123, 243, 125, 26, 127, 128, + 29, 20, 244, 245, 246, 247, 248, 249, 250, 251, 252, 120, 253, + 254, 255, 256, 257, 126, 258, 259, 129], + [ 30, 32, 31, 52, 35, 34, 54, 33, 64, 55, 39, 38, 57, + 37, 144, 58, 36, 67, 68, 59, 50, 51, 61, 62, 53, 260, + 261, 63, 262, 65, 56, 141, 142, 143, 263, 145, 66, 147, 148, + 69, 60, 264, 265, 266, 267, 268, 269, 270, 271, 272, 140, 273, + 274, 275, 276, 277, 146, 278, 279, 149], + [ 40, 42, 41, 111, 45, 44, 114, 43, 54, 113, 49, 48, 118, + 47, 154, 117, 46, 57, 58, 116, 110, 112, 51, 52, 115, 280, + 281, 53, 282, 55, 119, 151, 152, 153, 283, 155, 56, 157, 158, + 59, 50, 284, 285, 286, 287, 288, 289, 290, 291, 292, 150, 293, + 294, 295, 296, 297, 156, 298, 299, 159]], dtype=np.int32), + + "basis_vector":np.array([[[-1. , -1. , -1. ], + [ 0. , -1. , -1. ], + [-0. , 0. , -1. ], + [-0.47140452, -0.47140452, 0.94280904], + [-0. , -1. , -1. ], + [ 0. , 0. , -1. ], + [-0. , -0.70710678, 0.70710678], + [-0. , -0. , -1. ], + [ 0. , 1.41421356, 0. ], + [ 0. , 0. , 1.41421356], + [-1.41421356, -1.41421356, -0. ], + [ 0. , -1.41421356, -0. ], + [-0.70710678, 0.70710678, 0. ], + [-0.94280904, 0.47140452, 0.47140452], + [-0. , -1.41421356, -0. ], + [ 1. , -0. , -0. ], + [ 1. , 0. , -0. ], + [ 0. , 1.41421356, 0. ], + [ 1. , 1. , -0. ], + [ 0. , 1.41421356, 1.41421356], + [-1.41421356, -0. , -0. ], + [ 1. , 0. , 0. ], + [ 0.70710678, 0.70710678, 0. ], + [ 0.57735027, 0.57735027, 0.57735027], + [ 1. , 0. , 0. ], + [ 0. , 1. , 0. ], + [ 0. , 0.70710678, 0.70710678], + [ 1. , 1. , 0. ], + [ 0. , 0. , 1. ], + [ 1. , 1. , 1. ]], + + [[-1. , -1. , -1. ], + [ 0. , -1. , 0. ], + [ 0. , -1. , -1. ], + [-0.94280904, 0.47140452, 0.47140452], + [-0. , -1. , -0. ], + [ 0. , -1. , -0. ], + [-0. , -0. , 1.41421356], + [-0. , -1. , -1. ], + [-0. , -0.70710678, 0.70710678], + [ 0. , 1.41421356, 1.41421356], + [-1.41421356, -0. , -0. ], + [-0.70710678, -0. , 0.70710678], + [-0. , -0. , 1.41421356], + [ 0.47140452, -0.94280904, 0.47140452], + [-0. , -0. , 1.41421356], + [ 1. , 0. , 0. ], + [ 1. , -0. , 1. ], + [-0. , -0. , 1.41421356], + [ 1. , 0. , -0. ], + [-0. , -1.41421356, -0. ], + [ 1.41421356, 0. , 1.41421356], + [ 0.70710678, 0. , 0.70710678], + [ 1. , 0. , 0. ], + [ 0.57735027, 0.57735027, 0.57735027], + [ 1. , 0. , 1. ], + [ 0. , 0. , -1. ], + [ 0. , 1. , 0. ], + [ 1. , 0. , 0. ], + [ 0. , 0.70710678, 0.70710678], + [ 1. , 1. , 1. ]], + + [[-1. , -1. , -1. ], + [-1. , -1. , 0. ], + [ 0. , -1. , 0. ], + [-0.47140452, 0.94280904, -0.47140452], + [-1. , -1. , -0. ], + [ 0. , -1. , 0. ], + [-0.70710678, 0.70710678, -0. ], + [-0. , -1. , -0. ], + [-1.41421356, -0. , -0. ], + [ 0. , 1.41421356, 0. ], + [-1.41421356, -0. , -1.41421356], + [-1.41421356, -0. , 0. ], + [-0.70710678, -0. , 0.70710678], + [-0.47140452, -0.47140452, 0.94280904], + [-1.41421356, -0. , -0. ], + [-0. , -0. , 1. ], + [ 0. , -0. , 1. ], + [-1.41421356, -0. , -0. ], + [ 1. , -0. , 1. ], + [-1.41421356, -1.41421356, -0. ], + [ 0. , 0. , 1.41421356], + [ 0. , 0. , 1. ], + [ 0.70710678, 0. , 0.70710678], + [ 0.57735027, 0.57735027, 0.57735027], + [ 0. , 0. , 1. ], + [ 1. , 0. , 0. ], + [ 0.70710678, 0.70710678, 0. ], + [ 1. , 0. , 1. ], + [ 0. , 1. , 0. ], + [ 1. , 1. , 1. ]], + + [[-1. , -1. , -1. ], + [-1. , 0. , 0. ], + [-1. , -1. , 0. ], + [ 0.47140452, 0.47140452, -0.94280904], + [-1. , -0. , -0. ], + [-1. , -0. , 0. ], + [-0. , 1.41421356, -0. ], + [-1. , -1. , -0. ], + [-0.70710678, 0.70710678, -0. ], + [ 1.41421356, 1.41421356, 0. ], + [-0. , -0. , -1.41421356], + [-0. , 0.70710678, -0.70710678], + [-0. , 1.41421356, -0. ], + [-0.94280904, 0.47140452, 0.47140452], + [-0. , 1.41421356, -0. ], + [ 0. , 0. , 1. ], + [-0. , 1. , 1. ], + [-0. , 1.41421356, -0. ], + [ 0. , -0. , 1. ], + [-1.41421356, -0. , -0. ], + [ 0. , 1.41421356, 1.41421356], + [ 0. , 0.70710678, 0.70710678], + [ 0. , 0. , 1. ], + [ 0.57735027, 0.57735027, 0.57735027], + [ 0. , 1. , 1. ], + [ 0. , -1. , 0. ], + [ 1. , 0. , 0. ], + [ 0. , 0. , 1. ], + [ 0.70710678, 0.70710678, 0. ], + [ 1. , 1. , 1. ]], + + [[-1. , -1. , -1. ], + [-1. , 0. , -1. ], + [-1. , 0. , 0. ], + [ 0.94280904, -0.47140452, -0.47140452], + [-1. , -0. , -1. ], + [-1. , 0. , 0. ], + [ 0.70710678, -0. , -0.70710678], + [-1. , -0. , -0. ], + [-0. , -0. , -1.41421356], + [ 1.41421356, 0. , 0. ], + [-0. , -1.41421356, -1.41421356], + [-0. , 0. , -1.41421356], + [-0. , 0.70710678, -0.70710678], + [-0.47140452, 0.94280904, -0.47140452], + [-0. , -0. , -1.41421356], + [-0. , 1. , -0. ], + [-0. , 1. , 0. ], + [-0. , -0. , -1.41421356], + [-0. , 1. , 1. ], + [-1.41421356, -0. , -1.41421356], + [ 0. , 1.41421356, 0. ], + [ 0. , 1. , 0. ], + [ 0. , 0.70710678, 0.70710678], + [ 0.57735027, 0.57735027, 0.57735027], + [ 0. , 1. , 0. ], + [ 0. , 0. , 1. ], + [ 0.70710678, 0. , 0.70710678], + [ 0. , 1. , 1. ], + [ 1. , 0. , 0. ], + [ 1. , 1. , 1. ]], + + [[-1. , -1. , -1. ], + [-0. , 0. , -1. ], + [-1. , 0. , -1. ], + [-0.47140452, 0.94280904, -0.47140452], + [-0. , -0. , -1. ], + [-0. , 0. , -1. ], + [-1.41421356, 0. , 0. ], + [-1. , -0. , -1. ], + [ 0.70710678, -0. , -0.70710678], + [-1.41421356, -0. , -1.41421356], + [ 0. , 1.41421356, 0. ], + [-0.70710678, 0.70710678, 0. ], + [ 1.41421356, -0. , -0. ], + [ 0.47140452, 0.47140452, -0.94280904], + [-1.41421356, 0. , 0. ], + [ 0. , 1. , 0. ], + [ 1. , 1. , -0. ], + [ 1.41421356, -0. , -0. ], + [-0. , 1. , 0. ], + [-0. , -0. , -1.41421356], + [ 1.41421356, 1.41421356, 0. ], + [ 0.70710678, 0.70710678, 0. ], + [ 0. , 1. , 0. ], + [ 0.57735027, 0.57735027, 0.57735027], + [ 1. , 1. , 0. ], + [-1. , 0. , 0. ], + [ 0. , 0. , 1. ], + [ 0. , 1. , 0. ], + [ 0.70710678, 0. , 0.70710678], + [ 1. , 1. , 1. ]]],dtype=np.float64), + + "basis":np.array([[[[-0.1 , -0.1 , -0.1 ], + [-0. , -0.3 , -0.3 ], + [-0. , -0. , -0.1 ], + [ 0. , 0. , 0.70710678], + [-0.14142136, -0.14142136, -0. ], + [-0. , -0.42426407, -0. ], + [ 0. , 0.14142136, 0. ], + [ 0. , 0.70710678, 0.70710678], + [-0.14142136, -0. , -0. ], + [ 0.3 , 0. , 0. ], + [ 0.1 , 0.1 , 0. ], + [ 0.5 , 0.5 , 0.5 ]], + + [[-0.2 , -0.2 , -0.2 ], + [-0. , -0.2 , -0.2 ], + [-0. , -0. , -0.2 ], + [ 0. , 0. , 0.56568542], + [-0.28284271, -0.28284271, -0. ], + [-0. , -0.28284271, -0. ], + [ 0. , 0.28284271, 0. ], + [ 0. , 0.56568542, 0.56568542], + [-0.28284271, -0. , -0. ], + [ 0.2 , 0. , 0. ], + [ 0.2 , 0.2 , 0. ], + [ 0.4 , 0.4 , 0.4 ]]], + + + [[[-0.1 , -0.1 , -0.1 ], + [-0. , -0.3 , -0. ], + [-0. , -0.1 , -0.1 ], + [ 0. , 0.70710678, 0.70710678], + [-0.14142136, -0. , -0. ], + [-0. , -0. , 0.42426407], + [-0. , -0. , 0.14142136], + [-0. , -0.70710678, -0. ], + [ 0.14142136, 0. , 0.14142136], + [ 0.3 , 0. , 0.3 ], + [ 0.1 , 0. , 0. ], + [ 0.5 , 0.5 , 0.5 ]], + + [[-0.2 , -0.2 , -0.2 ], + [-0. , -0.2 , -0. ], + [-0. , -0.2 , -0.2 ], + [ 0. , 0.56568542, 0.56568542], + [-0.28284271, -0. , -0. ], + [-0. , -0. , 0.28284271], + [-0. , -0. , 0.28284271], + [-0. , -0.56568542, -0. ], + [ 0.28284271, 0. , 0.28284271], + [ 0.2 , 0. , 0.2 ], + [ 0.2 , 0. , 0. ], + [ 0.4 , 0.4 , 0.4 ]]], + + + [[[-0.1 , -0.1 , -0.1 ], + [-0.3 , -0.3 , -0. ], + [-0. , -0.1 , -0. ], + [ 0. , 0.70710678, 0. ], + [-0.14142136, -0. , -0.14142136], + [-0.42426407, -0. , -0. ], + [-0.14142136, -0. , -0. ], + [-0.70710678, -0.70710678, -0. ], + [ 0. , 0. , 0.14142136], + [ 0. , 0. , 0.3 ], + [ 0.1 , 0. , 0.1 ], + [ 0.5 , 0.5 , 0.5 ]], + + [[-0.2 , -0.2 , -0.2 ], + [-0.2 , -0.2 , -0. ], + [-0. , -0.2 , -0. ], + [ 0. , 0.56568542, 0. ], + [-0.28284271, -0. , -0.28284271], + [-0.28284271, -0. , -0. ], + [-0.28284271, -0. , -0. ], + [-0.56568542, -0.56568542, -0. ], + [ 0. , 0. , 0.28284271], + [ 0. , 0. , 0.2 ], + [ 0.2 , 0. , 0.2 ], + [ 0.4 , 0.4 , 0.4 ]]], + + + [[[-0.1 , -0.1 , -0.1 ], + [-0.3 , -0. , -0. ], + [-0.1 , -0.1 , -0. ], + [ 0.70710678, 0.70710678, 0. ], + [-0. , -0. , -0.14142136], + [-0. , 0.42426407, -0. ], + [-0. , 0.14142136, -0. ], + [-0.70710678, -0. , -0. ], + [ 0. , 0.14142136, 0.14142136], + [ 0. , 0.3 , 0.3 ], + [ 0. , 0. , 0.1 ], + [ 0.5 , 0.5 , 0.5 ]], + + [[-0.2 , -0.2 , -0.2 ], + [-0.2 , -0. , -0. ], + [-0.2 , -0.2 , -0. ], + [ 0.56568542, 0.56568542, 0. ], + [-0. , -0. , -0.28284271], + [-0. , 0.28284271, -0. ], + [-0. , 0.28284271, -0. ], + [-0.56568542, -0. , -0. ], + [ 0. , 0.28284271, 0.28284271], + [ 0. , 0.2 , 0.2 ], + [ 0. , 0. , 0.2 ], + [ 0.4 , 0.4 , 0.4 ]]], + + + [[[-0.1 , -0.1 , -0.1 ], + [-0.3 , -0. , -0.3 ], + [-0.1 , -0. , -0. ], + [ 0.70710678, 0. , 0. ], + [-0. , -0.14142136, -0.14142136], + [-0. , -0. , -0.42426407], + [-0. , -0. , -0.14142136], + [-0.70710678, -0. , -0.70710678], + [ 0. , 0.14142136, 0. ], + [ 0. , 0.3 , 0. ], + [ 0. , 0.1 , 0.1 ], + [ 0.5 , 0.5 , 0.5 ]], + + [[-0.2 , -0.2 , -0.2 ], + [-0.2 , -0. , -0.2 ], + [-0.2 , -0. , -0. ], + [ 0.56568542, 0. , 0. ], + [-0. , -0.28284271, -0.28284271], + [-0. , -0. , -0.28284271], + [-0. , -0. , -0.28284271], + [-0.56568542, -0. , -0.56568542], + [ 0. , 0.28284271, 0. ], + [ 0. , 0.2 , 0. ], + [ 0. , 0.2 , 0.2 ], + [ 0.4 , 0.4 , 0.4 ]]], + + + [[[-0.1 , -0.1 , -0.1 ], + [-0. , -0. , -0.3 ], + [-0.1 , -0. , -0.1 ], + [-0.70710678, -0. , -0.70710678], + [ 0. , 0.14142136, 0. ], + [-0.42426407, 0. , 0. ], + [ 0.14142136, -0. , -0. ], + [-0. , -0. , -0.70710678], + [ 0.14142136, 0.14142136, 0. ], + [ 0.3 , 0.3 , 0. ], + [ 0. , 0.1 , 0. ], + [ 0.5 , 0.5 , 0.5 ]], + + [[-0.2 , -0.2 , -0.2 ], + [-0. , -0. , -0.2 ], + [-0.2 , -0. , -0.2 ], + [-0.56568542, -0. , -0.56568542], + [ 0. , 0.28284271, 0. ], + [-0.28284271, 0. , 0. ], + [ 0.28284271, -0. , -0. ], + [-0. , -0. , -0.56568542], + [ 0.28284271, 0.28284271, 0. ], + [ 0.2 , 0.2 , 0. ], + [ 0. , 0.2 , 0. ], + [ 0.4 , 0.4 , 0.4 ]]]],dtype=np.float64), + + "div_basis":np.array([[[ 1. , 1. , 1. , 1.41421356, + 1.41421356, 1.41421356, 1.41421356, 1.41421356, + 1.41421356, 1. , 1. , 1. ], + [ 1. , 1. , 1. , 1.41421356, + 1.41421356, 1.41421356, 1.41421356, 1.41421356, + 1.41421356, 1. , 1. , 1. ]], + + [[ 1. , 1. , 1. , 1.41421356, + 1.41421356, 1.41421356, -1.41421356, -1.41421356, + -1.41421356, 1. , 1. , 1. ], + [ 1. , 1. , 1. , 1.41421356, + 1.41421356, 1.41421356, -1.41421356, -1.41421356, + -1.41421356, 1. , 1. , 1. ]], + + [[ 1. , 1. , 1. , 1.41421356, + 1.41421356, 1.41421356, -1.41421356, -1.41421356, + -1.41421356, 1. , 1. , 1. ], + [ 1. , 1. , 1. , 1.41421356, + 1.41421356, 1.41421356, -1.41421356, -1.41421356, + -1.41421356, 1. , 1. , 1. ]], + + [[ 1. , 1. , 1. , 1.41421356, + 1.41421356, 1.41421356, -1.41421356, -1.41421356, + -1.41421356, 1. , 1. , 1. ], + [ 1. , 1. , 1. , 1.41421356, + 1.41421356, 1.41421356, -1.41421356, -1.41421356, + -1.41421356, 1. , 1. , 1. ]], + + [[ 1. , 1. , 1. , 1.41421356, + 1.41421356, 1.41421356, -1.41421356, -1.41421356, + -1.41421356, 1. , 1. , 1. ], + [ 1. , 1. , 1. , 1.41421356, + 1.41421356, 1.41421356, -1.41421356, -1.41421356, + -1.41421356, 1. , 1. , 1. ]], + + [[ 1. , 1. , 1. , -1.41421356, + -1.41421356, -1.41421356, -1.41421356, -1.41421356, + -1.41421356, 1. , 1. , 1. ], + [ 1. , 1. , 1. , -1.41421356, + -1.41421356, -1.41421356, -1.41421356, -1.41421356, + -1.41421356, 1. , 1. , 1. ]]],dtype=np.float64), + } +] \ No newline at end of file diff --git a/test/functionspace/test_BrezziDouglasMariniFiniteElementSpace3d.py b/test/functionspace/test_BrezziDouglasMariniFiniteElementSpace3d.py new file mode 100644 index 000000000..ac11b8127 --- /dev/null +++ b/test/functionspace/test_BrezziDouglasMariniFiniteElementSpace3d.py @@ -0,0 +1,73 @@ + +import numpy as np +import pytest + +from fealpy.functionspace.BrezziDouglasMariniFiniteElementSpace3d import BDMDof +from fealpy.functionspace.BrezziDouglasMariniFiniteElementSpace3d import BDMFiniteElementSpace3d +from fealpy.backend import backend_manager as bm +from fealpy.mesh import TetrahedronMesh + +from BrezziDouglasMariniFiniteElementSpace3d_data import * + +class TestBDMDof: + @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) + @pytest.mark.parametrize("meshdata", init_data) + def test_cell_to_dof(self,meshdata,backend): + bm.set_backend(backend) + + mesh = TetrahedronMesh.from_box(nx = 1,ny =1,nz=1) + p = 3 + a = BDMDof(mesh,p) + assert a.number_of_local_dofs() == meshdata["cdof"] + assert a.number_of_global_dofs() == meshdata["gdof"] + + cell2dof = a.cell2dof + np.testing.assert_array_equal(bm.to_numpy(cell2dof), meshdata["cell2dof"]) + +class TestBDMFiniteElementSpace3d: + @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) + @pytest.mark.parametrize("meshdata", init_data) + def test_basis_vector(self,meshdata,backend): + bm.set_backend(backend) + + mesh = TetrahedronMesh.from_box(nx = 1,ny =1,nz=1) + p = 2 + a = BDMFiniteElementSpace3d(mesh,p) + + basis_vector = a.basis_vector() + np.testing.assert_allclose(bm.to_numpy(basis_vector), meshdata["basis_vector"],1e-8) + + @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) + @pytest.mark.parametrize("meshdata", init_data) + def test_basis(self,meshdata,backend): + bm.set_backend(backend) + + mesh = TetrahedronMesh.from_box(nx = 1,ny =1,nz=1) + p = 1 + a = BDMFiniteElementSpace3d(mesh,p) + bcs = bm.tensor([[0.1,0.3,0.1,0.5], + [0.2,0.2,0.2,0.4]]) + basis = a.basis(bcs) + np.testing.assert_allclose(bm.to_numpy(basis), meshdata["basis"],1e-6) + + @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) + @pytest.mark.parametrize("meshdata", init_data) + def test_div_basis(self,meshdata,backend): + bm.set_backend(backend) + + mesh = TetrahedronMesh.from_box(nx = 1,ny =1,nz=1) + p = 1 + a = BDMFiniteElementSpace3d(mesh,p) + bcs = bm.tensor([[0.1,0.3,0.1,0.5], + [0.2,0.2,0.2,0.4]],dtype=bm.float64) + div_basis = a.div_basis(bcs) + np.testing.assert_allclose(bm.to_numpy(div_basis), meshdata["div_basis"],1e-7) + + +if __name__ == "__main__": + #test = TestBDMDof() + #test.test_cell_to_dof(init_data[0],'numpy') + test = TestBDMFiniteElementSpace3d() + #test.test_basis_vector(init_data[0],'pytorch') + #test.test_basis(init_data[0],'pytorch') + test.test_div_basis(init_data[0],'numpy') \ No newline at end of file From 660d031d571400e6ec30afd420191f1832881645 Mon Sep 17 00:00:00 2001 From: BellaLq <2655493031@qq.com> Date: Tue, 26 Nov 2024 10:01:14 +0800 Subject: [PATCH 37/63] update --- fealpy/mesh/lagrange_quadrangle_mesh.py | 65 ++++++++++++++++++++-- fealpy/mesh/quadrangle_mesh.py | 2 +- test/mesh/lagrange_quadrangle_mesh_data.py | 5 +- test/mesh/test_lagrange_quadrangle_mesh.py | 41 +++++++------- 4 files changed, 84 insertions(+), 29 deletions(-) diff --git a/fealpy/mesh/lagrange_quadrangle_mesh.py b/fealpy/mesh/lagrange_quadrangle_mesh.py index 2765bbc2f..7a5cfd77a 100644 --- a/fealpy/mesh/lagrange_quadrangle_mesh.py +++ b/fealpy/mesh/lagrange_quadrangle_mesh.py @@ -14,8 +14,8 @@ def __init__(self, node: TensorLike, cell: TensorLike, p=1, surface=None, super().__init__(TD=2, itype=cell.dtype, ftype=node.dtype) kwargs = bm.context(cell) + GD = node.shape[1] self.p = p - self.GD = node.shape[1] self.cell = cell self.surface = surface @@ -28,15 +28,15 @@ def __init__(self, node: TensorLike, cell: TensorLike, p=1, surface=None, bc = bm.einsum('im, jn -> ijmn', bc, bc).reshape(-1, 4) self.node[self.cell] = bm.einsum('ijn, kj -> ikn', node[cell], bc) - self.surface is not None: - self.node,_ = self.surface.project(self.node) - + #self.localEdge = self.generate_local_lagrange_edges(p) + #self.localFace = self.localEdge self.ccw = bm.tensor([0, 2, 3, 1], **kwargs) if construct: self.construct() self.meshtype = 'lquad' + self.qmesh = None # 网格的顶点必须在球面上 self.nodedata = {} self.edgedata = {} @@ -46,6 +46,17 @@ def __init__(self, node: TensorLike, cell: TensorLike, p=1, surface=None, def reference_cell_measure(self): return 1 + def generate_local_lagrange_edges(self, p: int) -> TensorLike: + """ + Generate the local edges for Lagrange elements of order p. + """ + multiIndex = self.multi_index_matrix(p, 1, dtype=self.ftype, device=bm.get_device(cell)) + #multiIndex = bm.multi_index_matrix(p, TD) + + localEdge = bm.zeros((4, p+1), dtype=bm.int32) + + return localEdge + def interpolation_points(self, p: int, index: Index=_S): """Fetch all p-order interpolation points on the quadrangle mesh.""" pass @@ -59,7 +70,7 @@ def from_quadrangle_mesh(cls, mesh, p: int, surface=None): bnode[:],_ = surface.project(bnode) node,_ = surface.project(node) - lemsh = cls(node, mesh, p=p, construct=True) + lmesh = cls(node, cell, p=p, construct=True) lmesh.qmesh = mesh lmesh.edge2cell = mesh.edge2cell # (NF, 4) @@ -81,7 +92,7 @@ def quadrature_formula(self, q, etype: Union[int, str] = 'cell'): raise ValueError(f"entity type: {etype} is wrong!") def bc_to_point(self, bc: TensorLike, index: Index=_S, etype='cell'): - node = =self.node + node = self.node TD = len(bc) phi = self.shape_function(bc) p = bm.einsum() @@ -110,3 +121,45 @@ def grad_shape_function(self, bc: TensorLike, p=None, index: Index=_S, variables='x'): pass + def vtk_cell_type(self, etype='cell'): + """ + @berif 返回网格单元对应的 vtk类型。 + """ + if etype in {'cell', 2}: + VTK_LAGRANGE_TRIANGLE = 69 + return VTK_LAGRANGE_TRIANGLE + elif etype in {'face', 'edge', 1}: + VTK_LAGRANGE_CURVE = 68 + return VTK_LAGRANGE_CURVE + + def to_vtk(self, etype='cell', index: Index=_S, fname=None): + """ + Parameters + ---------- + + @berif 把网格转化为 VTK 的格式 + """ + from fealpy.mesh.vtk_extent import vtk_cell_index, write_to_vtu + + node = self.entity('node') + GD = self.geo_dimension() + if GD == 2: + node = bm.concatenate((node, bm.zeros((node.shape[0], 1), dtype=bm.float64)), axis=1) + + #cell = self.entity(etype)[index] + cell = self.entity(etype, index) + cellType = self.vtk_cell_type(etype) + idx = vtk_cell_index(self.p, cellType) + NV = cell.shape[-1] + + cell = bm.concatenate((bm.zeros((len(cell), 1), dtype=cell.dtype), cell[:, idx]), axis=1) + cell[:, 0] = NV + + NC = len(cell) + if fname is None: + return node, cell.flatten(), cellType, NC + else: + print("Writting to vtk...") + write_to_vtu(fname, node, NC, cellType, cell.flatten(), + nodedata=self.nodedata, + celldata=self.celldata) diff --git a/fealpy/mesh/quadrangle_mesh.py b/fealpy/mesh/quadrangle_mesh.py index 7e9479875..4eece4ed6 100644 --- a/fealpy/mesh/quadrangle_mesh.py +++ b/fealpy/mesh/quadrangle_mesh.py @@ -824,7 +824,7 @@ def sub_domain_mesh_generator(cls, half_edge, origin_node, separator_streamlines return quad_mesh @classmethod - def from_unit_sphere_surface(cls, refine=3, itype=None, ftype=None, device=None): + def from_unit_sphere_surface(cls, refine=1, itype=None, ftype=None, device=None): if itype is None: itype = bm.int32 if ftype is None: diff --git a/test/mesh/lagrange_quadrangle_mesh_data.py b/test/mesh/lagrange_quadrangle_mesh_data.py index 723bc20f3..8f885f667 100644 --- a/test/mesh/lagrange_quadrangle_mesh_data.py +++ b/test/mesh/lagrange_quadrangle_mesh_data.py @@ -7,8 +7,8 @@ mesh = QuadrangleMesh.from_unit_sphere_surface() node = mesh.interpolation_points(3) cell = mesh.cell_to_ipoint(3) - -from_triangle_mesh_data = [ +""" +from_quadrangle_mesh_data = [ { "p": 3, "surface": surface, @@ -19,3 +19,4 @@ "NC": } ] +""" diff --git a/test/mesh/test_lagrange_quadrangle_mesh.py b/test/mesh/test_lagrange_quadrangle_mesh.py index c3a6128e8..a14a52b6b 100644 --- a/test/mesh/test_lagrange_quadrangle_mesh.py +++ b/test/mesh/test_lagrange_quadrangle_mesh.py @@ -15,15 +15,27 @@ class TestLagrangeQuadrangleMeshInterfaces: @pytest.mark.parametrize("backend", ['numpy']) - @pytest.mark.parametrize("data", from_triangle_mesh_data) - def test_from_triangle_mesh(self, data, backend): + def test_surface_mesh(self, backend): + bm.set_backend(backend) + + surface = SphereSurface() + mesh = QuadrangleMesh.from_unit_sphere_surface() + + lmesh = LagrangeQuadrangleMesh.from_quadrangle_mesh(mesh, p=1, surface=surface) + fname = f"sphere_qtest.vtu" + lmesh.to_vtk(fname=fname) + + """ + @pytest.mark.parametrize("backend", ['numpy']) + @pytest.mark.parametrize("data", from_quadrangle_mesh_data) + def test_from_quadranglemesh(self, data, backend): bm.set_backend(backend) p = data['p'] surface = data['surface'] - mesh = TriangleMesh.from_unit_sphere_surface() + mesh = QuadrangleMesh.from_unit_sphere_surface() - lmesh = LagrangeTriangleMesh.from_triangle_mesh(mesh, p, surface=surface) + lmesh = LagrangeQuadrangleMesh.from_triangle_mesh(mesh, p, surface=surface) assert lmesh.number_of_nodes() == data["NN"] assert lmesh.number_of_edges() == data["NE"] @@ -31,23 +43,12 @@ def test_from_triangle_mesh(self, data, backend): assert lmesh.number_of_cells() == data["NC"] cell = lmesh.entity('cell') - np.testing.assert_allclose(bm.to_numpy(cell), data["cell"], atol=1e-14) - - @pytest.mark.parametrize("backend", ['numpy']) - def test_surface_mesh(self, backend): - bm.set_backend(backend) - - surface = SphereSurface() - mesh = QuadrangleMesh.from_unit_sphere_surface() - - lmesh = LagrangeQuadrangleMesh.from_quadrangle_mesh(mesh, p=3, surface=surface) - fname = f"sphere_qtest.vtu" - lmesh.to_vtk(fname=fname) + np.testing.assert_allclose(bm.to_numpy(cell), data["cell"], atol=1e-14) + """ if __name__ == "__main__": - a = TestLagrangeTriangleMeshInterfaces() - a.test_init_mesh(init_data[0], 'numpy') - #a.test_from_triangle_mesh(from_triangle_mesh_data[0], 'numpy') - #a.test_surface_mesh('numpy') + a = TestLagrangeQuadrangleMeshInterfaces() + a.test_surface_mesh('numpy') + #a.test_from_quadrangle_mesh(from_quadrangle_mesh_data[0], 'numpy') #pytest.main(["./test_lagrange_triangle_mesh.py"]) From 86c07be4b21acb00cb14a0a8d9b64f7767d123bd Mon Sep 17 00:00:00 2001 From: chenchunyu Date: Tue, 26 Nov 2024 10:05:06 +0800 Subject: [PATCH 38/63] update --- .../phase_field/square_domian_with_fracture.py | 4 ++-- .../phasefield/phase_fracture_material.py | 14 +++++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/app/fracturex/fracturex/cases/phase_field/square_domian_with_fracture.py b/app/fracturex/fracturex/cases/phase_field/square_domian_with_fracture.py index b1d45307c..834f7be98 100644 --- a/app/fracturex/fracturex/cases/phase_field/square_domian_with_fracture.py +++ b/app/fracturex/fracturex/cases/phase_field/square_domian_with_fracture.py @@ -204,8 +204,8 @@ def adaptive_mesh(self, mesh, d0=0.49, d1=1.01, h=0.005): tmr.send(None) end = time.time() -force = ms.Rforce -disp = ms.force_value +force = ms.get_residual_force() +disp = model.is_force() ftname = 'force_'+args.mesh_type + '_p' + str(p) + '_' + 'model1_disp.pt' diff --git a/app/fracturex/fracturex/phasefield/phase_fracture_material.py b/app/fracturex/fracturex/phasefield/phase_fracture_material.py index a0d4ee856..c08d54635 100644 --- a/app/fracturex/fracturex/phasefield/phase_fracture_material.py +++ b/app/fracturex/fracturex/phasefield/phase_fracture_material.py @@ -1,4 +1,5 @@ import numpy as np +import torch from typing import Optional from fealpy.typing import TensorLike @@ -327,7 +328,18 @@ def strain_pm_eig_decomposition(self, s: TensorLike): @param[in] s strain,(NC, NQ, GD, GD) """ - w, v = bm.linalg.eigh(s) # w 特征值, v 特征向量 + ''' + if bm.device_type(s) == 'cuda': + torch.cuda.empty_cache() + try: + w, v = bm.linalg.eigh(s) # w 特征值, v 特征向量 + except torch.cuda.OutOfMemoryError as e: + print("CUDA out of memory. Attempting to free cache.") + torch.cuda.empty_cache() + else: + w, v = bm.linalg.eigh(s) # w 特征值, v 特征向量 + ''' + w, v = bm.linalg.eigh(s) p, m = self.macaulay_operation(w) sp = bm.zeros_like(s) From fb33a4a30bab9562725707dbe09f25a8237489fd Mon Sep 17 00:00:00 2001 From: BenHBLiu Date: Tue, 26 Nov 2024 10:24:51 +0800 Subject: [PATCH 39/63] update --- fealpy/opt/crayfish_opt_alg.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fealpy/opt/crayfish_opt_alg.py b/fealpy/opt/crayfish_opt_alg.py index b596373bb..5061b0d97 100644 --- a/fealpy/opt/crayfish_opt_alg.py +++ b/fealpy/opt/crayfish_opt_alg.py @@ -43,10 +43,11 @@ def run(self): C = 2 - (t / T) temp = bm.random.rand(1) * 15 + 20 xf = (gbest + global_position) / 2 - p = 0.2 * ( 1 / (bm.sqrt(bm.array(2 * bm.pi) * 3))) * bm.exp( bm.array(- (temp - 25) ** 2 / (2 * 3 ** 2))) + p = 0.2 * (1 / (bm.sqrt(bm.array(2 * bm.pi) * 3))) * bm.exp(bm.array(- (temp - 25) ** 2 / (2 * 3 ** 2))) rand = bm.random.rand(N, 1) rr = bm.random.rand(4, N, dim) - z = [random.randint(0, N - 1) for _ in range(N)] + # z = [random.randint(0, N - 1) for _ in range(N)] + z = bm.random.randint(0, N - 1, (N,)) gbest = gbest.reshape(1, dim) From d4f5975cd810a4f73666e8f16d0f6a44a747ac29 Mon Sep 17 00:00:00 2001 From: Flantori Date: Tue, 26 Nov 2024 11:05:25 +0800 Subject: [PATCH 40/63] fix(mesh): undefined variable in --- fealpy/mesh/triangle_mesh.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fealpy/mesh/triangle_mesh.py b/fealpy/mesh/triangle_mesh.py index 17cf3b4a0..e1d1fe50b 100644 --- a/fealpy/mesh/triangle_mesh.py +++ b/fealpy/mesh/triangle_mesh.py @@ -295,8 +295,6 @@ def uniform_refine(self, n=1, surface=None, interface=None, returnim=False): """ if returnim is True: IM = [] - if returnrm is True: - RM = [] for i in range(n): NN = self.number_of_nodes() @@ -317,10 +315,8 @@ def uniform_refine(self, n=1, surface=None, interface=None, returnim=False): shape=(NN + NE, NN)) A += coo_matrix((0.5 * bm.ones(NE, dtype=self.ftype), (bm.arange(NN, NN + NE), edge[:, 1])), shape=(NN + NE, NN)) - - IM.append(A.tocsr()) - + IM.append(A.tocsr()) self.node = bm.concatenate((node, newNode), axis=0) p = bm.concatenate((cell, edge2newNode[cell2edge]), axis=1) From dba8c080b95f7a3f6c700eb03e8346ec0f30308b Mon Sep 17 00:00:00 2001 From: chaos <2250213115@qq.com> Date: Tue, 26 Nov 2024 14:14:47 +0800 Subject: [PATCH 41/63] update --- app/lafem-ims/functional.py | 4 +- .../near_field_data_generator.py | 2 +- .../default_data/reciever_points_50.npy | Bin 928 -> 0 bytes .../test/test_near_field_data_generator_1.py | 105 +++++++++++++----- .../test/test_near_field_data_generator_2.py | 97 +++++++++++----- 5 files changed, 152 insertions(+), 56 deletions(-) delete mode 100644 app/lafem-ims/lafemims/default_data/reciever_points_50.npy diff --git a/app/lafem-ims/functional.py b/app/lafem-ims/functional.py index a68703118..e47c654c5 100644 --- a/app/lafem-ims/functional.py +++ b/app/lafem-ims/functional.py @@ -72,7 +72,7 @@ def levelset_fourier(points: NDArray, complexity: int, coefficient: NDArray, ori ################################################################################################################################# -def genarate_scatterers_circles(num_of_scatterers: int, seed: int) ->TensorLike: +def generate_scatterers_circles(num_of_scatterers: int, seed: int) ->TensorLike: """ 参数: - num_of_scatterers: int, 散射体的数量。 @@ -90,7 +90,7 @@ def genarate_scatterers_circles(num_of_scatterers: int, seed: int) ->TensorLike: rads = bm.astype(rads, bm.float64) return ctrs, rads -def genarate_scatterers_fourier(complexity: int, num_of_scatterers: int, seed: int) ->TensorLike: +def generate_scatterers_fourier(complexity: int, num_of_scatterers: int, seed: int) ->TensorLike: """ 参数: - complexity: int, 水平集函数的复杂度。 diff --git a/app/lafem-ims/lafemims/data_generator/near_field_data_generator.py b/app/lafem-ims/lafemims/data_generator/near_field_data_generator.py index 19bbb4b13..0c7ac2bea 100644 --- a/app/lafem-ims/lafemims/data_generator/near_field_data_generator.py +++ b/app/lafem-ims/lafemims/data_generator/near_field_data_generator.py @@ -94,7 +94,7 @@ def get_nearfield_data(self, k: float, d: Sequence[float]): space = LagrangeFESpace(self.mesh, p=self.p) - # 定义积分器 + # 定义积分子 D = ScalarDiffusionIntegrator(pde.diffusion_coefficient, q=self.q) C = ScalarConvectionIntegrator(pde.convection_coefficient, q=self.q) M = ScalarMassIntegrator(pde.reaction_coefficient, q=self.q) diff --git a/app/lafem-ims/lafemims/default_data/reciever_points_50.npy b/app/lafem-ims/lafemims/default_data/reciever_points_50.npy deleted file mode 100644 index 8e1b75c0a39dd560e56f7f694dbd9d10e7629164..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 928 zcmbR27wQ`j$;eQ~P_3SlTAW;@Zl$1ZlV+i=qoAIaUsO_*m=~X4l#&V(cT3DEP6dh= zXCxM+0{I%I2098xnmP)#3giMV1~3qDfY1yKi`p+tS#Vw0;l?IT(`CF*?0tl_f>h&$ z9rm>Z+Gs!hY=3UX)VgdE~8U%F*b{MX(>LU7}THbIBWC%;E(xH3Du{9$)cOHaVz z+lEO;UpKKk$fO-_S**e9Aej-WbvTjRLCYy=pG7^V!xJ|n8@@|?4k~7Hr|;&mI*81j zSO4{qfCE$E_Pf8>867HmIbUs)5OP@Z_&3Yy9Y5^lw{o4yU=?-{igc+{{{7b8UTu1} z|59Oxz#TUhoe{rcU%%?z^rv5i9ikxo{V=`)ME!o4`ehLF_QTAB+IOHFYM%zgJqLuL z?$L(0_W;bjvJn3q5QF;X1;oDxzC-;B4UYqtpy6>I5?%+Qpy34#&jYaVgogKl3()X} z#>asgH(>>hA9wb^^2gFgs_kag#_#Y@kjh_R0sPT7TJ~Vz6 zAn|*E5jFk~s6o>QG(HaKLDSD8sC(t1=}QC>9|vAT)8Bijf6>zC0cd Date: Tue, 26 Nov 2024 14:16:22 +0800 Subject: [PATCH 42/63] . --- app/lafem-ims/test/test_near_field_data_generator_1.py | 4 +--- app/lafem-ims/test/test_near_field_data_generator_2.py | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/app/lafem-ims/test/test_near_field_data_generator_1.py b/app/lafem-ims/test/test_near_field_data_generator_1.py index 6ecd40d07..8007305ce 100644 --- a/app/lafem-ims/test/test_near_field_data_generator_1.py +++ b/app/lafem-ims/test/test_near_field_data_generator_1.py @@ -66,7 +66,7 @@ def main(idx: int, ctr: TensorLike, rad:TensorLike, save_path: str, test_type: s from multiprocessing import Pool from tqdm import tqdm import time - + NUM_OF_POOL = 8 # 并行进程数 AMOUNT_OF_DATA = 100 # 生成数据数量 @@ -98,5 +98,3 @@ def update_progress_bar(*_): IDX = 88 # 选择要可视化的数据索引 generator = main(IDX, centers[IDX:IDX+1, ...], radius[IDX:IDX+1, ...], SAVE_PATH, TEST_TYPE) generator.visualization_of_nearfield_data(k=k[-1], d=d[0]) - - diff --git a/app/lafem-ims/test/test_near_field_data_generator_2.py b/app/lafem-ims/test/test_near_field_data_generator_2.py index 4e5106749..203194883 100644 --- a/app/lafem-ims/test/test_near_field_data_generator_2.py +++ b/app/lafem-ims/test/test_near_field_data_generator_2.py @@ -68,7 +68,7 @@ def main(idx: int, complexity: int, coefficient: TensorLike, origin:TensorLike, from multiprocessing import Pool from tqdm import tqdm import time - + NUM_OF_POOL = 8 # 并行进程数 AMOUNT_OF_DATA = 100 # 生成数据数量 @@ -96,4 +96,3 @@ def update_progress_bar(*_): IDX = 99 # 选择要可视化的数据索引 generator = main(IDX, M, coefficients[IDX, ...], origin, SAVE_PATH, TEST_TYPE) generator.visualization_of_nearfield_data(k=k[-1], d=d[0]) - From 91fb24b0d75d91c8437ab727192465dd6334f12f Mon Sep 17 00:00:00 2001 From: chenchunyu Date: Wed, 27 Nov 2024 09:34:19 +0800 Subject: [PATCH 43/63] update --- app/fracturex/fracturex/cases/phase_field/model3d.py | 4 ++-- .../cases/phase_field/square_domian_with_fracture.py | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/fracturex/fracturex/cases/phase_field/model3d.py b/app/fracturex/fracturex/cases/phase_field/model3d.py index 0f71fb8be..17321157c 100644 --- a/app/fracturex/fracturex/cases/phase_field/model3d.py +++ b/app/fracturex/fracturex/cases/phase_field/model3d.py @@ -166,8 +166,8 @@ def is_dirchlet_boundary(self, p): tmr.send(None) end = time.time() -force = ms.Rforce -disp = ms.force_value +force = ms.get_residual_force() +disp = model.is_force() ftname = 'force_'+args.mesh_type + '_p' + str(p) + '_' + 'model3d_disp.pt' diff --git a/app/fracturex/fracturex/cases/phase_field/square_domian_with_fracture.py b/app/fracturex/fracturex/cases/phase_field/square_domian_with_fracture.py index a67855700..761b74484 100644 --- a/app/fracturex/fracturex/cases/phase_field/square_domian_with_fracture.py +++ b/app/fracturex/fracturex/cases/phase_field/square_domian_with_fracture.py @@ -205,7 +205,7 @@ def adaptive_mesh(self, mesh, d0=0.49, d1=1.01, h=0.005): end = time.time() force = ms.get_residual_force() -disp = model.is_force() + ftname = 'force_'+args.mesh_type + '_p' + str(p) + '_' + 'model1_disp.pt' @@ -214,6 +214,12 @@ def adaptive_mesh(self, mesh, d0=0.49, d1=1.01, h=0.005): tname = 'params_'+args.mesh_type + '_p' + str(p) + '_' + 'model1_disp.txt' with open(tname, 'w') as file: file.write(f'\n time: {end-start},\n degree:{p},\n, backend:{backend},\n, model_type:{model_type},\n, enable_adaptive:{enable_adaptive},\n, marking_strategy:{marking_strategy},\n, refine_method:{refine_method},\n, n:{n},\n, maxit:{maxit},\n, vtkname:{vtkname}\n') + +if force_type == 'y': + disp = model.is_y_force() +elif force_type == 'x': + disp = model.is_x_force() + fig, axs = plt.subplots() plt.plot(disp, force, label='Force') plt.xlabel('Displacement Increment') From c658add5ee1ae1bb6917881e714fa3a42469e8b6 Mon Sep 17 00:00:00 2001 From: chenchunyu Date: Wed, 27 Nov 2024 10:44:38 +0800 Subject: [PATCH 44/63] update --- fealpy/mesh/lagrange_quadrangle_mesh.py | 285 ++++++++++++++++++--- test/mesh/test_lagrange_quadrangle_mesh.py | 7 +- 2 files changed, 250 insertions(+), 42 deletions(-) diff --git a/fealpy/mesh/lagrange_quadrangle_mesh.py b/fealpy/mesh/lagrange_quadrangle_mesh.py index 7a5cfd77a..aa0df3f7e 100644 --- a/fealpy/mesh/lagrange_quadrangle_mesh.py +++ b/fealpy/mesh/lagrange_quadrangle_mesh.py @@ -19,24 +19,14 @@ def __init__(self, node: TensorLike, cell: TensorLike, p=1, surface=None, self.cell = cell self.surface = surface - if p == 1: - self.node = node - else: - NN = self.number_of_nodes() - self.node = bm.zeros((NN, self.GD), dtype=bm.float64) - bc = bm.multi_index_matrix(p, TD)/p - bc = bm.einsum('im, jn -> ijmn', bc, bc).reshape(-1, 4) - self.node[self.cell] = bm.einsum('ijn, kj -> ikn', node[cell], bc) - - #self.localEdge = self.generate_local_lagrange_edges(p) - #self.localFace = self.localEdge + self.node = node self.ccw = bm.tensor([0, 2, 3, 1], **kwargs) if construct: self.construct() self.meshtype = 'lquad' - self.qmesh = None # 网格的顶点必须在球面上 + self.linearmesh = None # 网格的顶点必须在球面上 self.nodedata = {} self.edgedata = {} @@ -46,19 +36,9 @@ def __init__(self, node: TensorLike, cell: TensorLike, p=1, surface=None, def reference_cell_measure(self): return 1 - def generate_local_lagrange_edges(self, p: int) -> TensorLike: - """ - Generate the local edges for Lagrange elements of order p. - """ - multiIndex = self.multi_index_matrix(p, 1, dtype=self.ftype, device=bm.get_device(cell)) - #multiIndex = bm.multi_index_matrix(p, TD) - - localEdge = bm.zeros((4, p+1), dtype=bm.int32) - - return localEdge - def interpolation_points(self, p: int, index: Index=_S): """Fetch all p-order interpolation points on the quadrangle mesh.""" + #TODO pass @classmethod @@ -70,8 +50,8 @@ def from_quadrangle_mesh(cls, mesh, p: int, surface=None): bnode[:],_ = surface.project(bnode) node,_ = surface.project(node) - lmesh = cls(node, cell, p=p, construct=True) - lmesh.qmesh = mesh + lmesh = cls(node, cell, p=p, construct=False) + lmesh.linearmesh = mesh lmesh.edge2cell = mesh.edge2cell # (NF, 4) lmesh.cell2edge = mesh.cell_to_edge() @@ -92,6 +72,7 @@ def quadrature_formula(self, q, etype: Union[int, str] = 'cell'): raise ValueError(f"entity type: {etype} is wrong!") def bc_to_point(self, bc: TensorLike, index: Index=_S, etype='cell'): + #TODO 复制粘贴 LagrangeTriangleMesh 的代码 node = self.node TD = len(bc) phi = self.shape_function(bc) @@ -106,32 +87,28 @@ def shape_function(self, bc: TensorLike, p=None): bc[i] 是一个一维积分公式的重心坐标数组 假设 bc[0]==bc[1]== ... ==bc[TD-1] """ - p = self.p if p is None else p - TD = len(bc) - phi = bm.simplex_shape_function(bc[0], p) - if TD == 2: - phi = bm.einsum('im, jn -> ijmn', phi, phi) #TODo - shape = phi.reshape[:-2] + (-1,) - phi = phi.reshape(shape) # 展平自由度 - shape = (-1, 1) + phi.shape[-1:] # 增加一个单元轴,方便广播运算 - phi = phi.reshape(shape) # 展平积分点 - return phi + #TODO test + return self.linearmesh.shape_function(bc, p) def grad_shape_function(self, bc: TensorLike, p=None, index: Index=_S, variables='x'): - pass + return self.linearmesh.grad_shape_function(bc, p, index, variables) def vtk_cell_type(self, etype='cell'): """ - @berif 返回网格单元对应的 vtk类型。 + + Notes + ----- + 返回网格单元对应的 vtk 类型。 """ if etype in {'cell', 2}: - VTK_LAGRANGE_TRIANGLE = 69 - return VTK_LAGRANGE_TRIANGLE + VTK_LAGRANGE_QUADRILATERAL = 70 + return VTK_LAGRANGE_QUADRILATERAL elif etype in {'face', 'edge', 1}: VTK_LAGRANGE_CURVE = 68 return VTK_LAGRANGE_CURVE + def to_vtk(self, etype='cell', index: Index=_S, fname=None): """ Parameters @@ -163,3 +140,233 @@ def to_vtk(self, etype='cell', index: Index=_S, fname=None): write_to_vtu(fname, node, NC, cellType, cell.flatten(), nodedata=self.nodedata, celldata=self.celldata) + + # ipoint --> copy TriangleMesh + def number_of_local_ipoints(self, p:int, iptype:Union[int, str]='cell'): + """ + @berif 每个ltri单元上插值点的个数 + """ + #TODO + pass + + def number_of_global_ipoints(self, p:int): + """ + @berif ltri网格上插值点总数 + """ + # TODO + pass + + def cell_to_ipoint(self, p:int, index:Index=_S): + """ + @berif 获取单元与插值点的对应关系 + """ + #TODO 复制粘贴 QuadrangleMesh 的代码并测试 + pass + + def face_to_ipoint(self, p: int, index: Index=_S): + #TODO test + return self.edge_to_ipoint(p, index) + + def entity_measure(self, etype=2, index:Index=_S): + if etype in {'cell', 2}: + return self.cell_area(index=index) + elif etype in {'edge', 1}: + return self.edge_length(index=index) + elif etype in {'node', 0}: + return bm.zeros(1, dtype=bm.float64) + else: + raise ValueError(f"entity type:{etype} is erong!") + + def cell_area(self, q=None, index: Index=_S): + """ + Calculate the area of a cell. + """ + #TODO Test + p = self.p + q = p if q is None else q + GD = self.geo_dimension() + + qf = self.quadrature_formula(q, etype='cell') + bcs, ws = qf.get_quadrature_points_and_weights() + J = self.jacobi_matrix(bcs, index=index) + n = bm.cross(J[..., 0], J[..., 1], axis=-1) + if GD == 3: + n = bm.sqrt(bm.sum(n**2, axis=-1)) # (NC, NQ) + a = bm.einsum('i, ji -> j', ws, n)/2.0 + return a + + def edge_length(self, q=None, index: Index=_S): + """ + Calculate the length of the side. + """ + #TODO Test + p = self.p + q = p if q is None else q + qf = self.quadrature_formula(q, etype='edge') + bcs, ws = qf.get_quadrature_points_and_weights() + + J = self.jacobi_matrix(bcs, index=index) + a = bm.sqrt(bm.sum(J**2, axis=(-1, -2))) + l = bm.einsum('i, ij -> j', ws, a) + return l + + def cell_unit_normal(self, bc: TensorLike, index: Index=_S): + """ + When calculating the surface,the direction of the unit normal at the integration point. + """ + #TODO Test + J = self.jacobi_matrix(bc, index=index) + n = bm.cross(J[..., 0], J[..., 1], axis=-1) + if self.GD == 3: + l = bm.sqrt(bm.sum(n**2, axis=-1, keepdims=True)) + n /= l + return n + + def jacobi_matrix(self, bc: TensorLike, p=None, index: Index=_S, return_grad=False): + """ + @berif 计算参考单元 (xi, eta) 到实际 Lagrange 三角形(x) 之间映射的 Jacobi 矩阵。 + + x(xi, eta) = phi_0 x_0 + phi_1 x_1 + ... + phi_{ldof-1} x_{ldof-1} + """ + #TODO 测试,如果不对,那就复制粘贴 TensorMesh 的代码 + TD = bc.shape[-1] - 1 + entity = self.entity(TD, index) + gphi = self.grad_shape_function(bc, p=p, variables='u') + J = bm.einsum( + 'cin, cqim -> cqnm', + self.node[entity[index], :], gphi) #(NC,ldof,GD),(NC,NQ,ldof,TD) + if return_grad is False: + return J #(NC,NQ,GD,TD) + else: + return J, gphi + + # fundamental form + def first_fundamental_form(self, bc: Union[TensorLike, Tuple[TensorLike]], + index: Index=_S, return_jacobi=False, return_grad=False): + """ + Compute the first fundamental form of a mesh surface at integration points. + """ + #TODO 测试 + TD = bc.shape[-1] - 1 + + J = self.jacobi_matrix(bc, index=index, + return_grad=return_grad) + + if return_grad: + J, gphi = J + + shape = J.shape[0:-2] + (TD, TD) + G = bm.zeros(shape, dtype=self.ftype) + for i in range(TD): + G[..., i, i] = bm.sum(J[..., i]**2, axis=-1) + for j in range(i+1, TD): + G[..., i, j] = bm.sum(J[..., i]*J[..., j], axis=-1) + G[..., j, i] = G[..., i, j] + if (return_jacobi is False) & (return_grad is False): + return G + elif (return_jacobi is True) & (return_grad is False): + return G, J + elif (return_jacobi is False) & (return_grad is True): + return G, gphi + else: + return G, J, gphi + + def second_fundamental_form(self, bc: Union[TensorLike, Tuple[TensorLike]], + index: Index=_S, return_jacobi=False, return_grad=False): + """ + Compute the second fundamental form of a mesh surface at integration points. + """ + #TODO + pass + + # tools + def integral(self, f, q=3, celltype=False) -> TensorLike: + """ + @brief 在网格中数值积分一个函数 + """ + #TODO 测试 + GD = self.geo_dimension() + qf = self.integrator(q, etype='cell') + bcs, ws = qf.get_quadrature_points_and_weights() + ps = self.bc_to_point(bcs) + + rm = self.reference_cell_measure() + G = self.first_fundamental_form(bcs) + d = bm.sqrt(bm.linalg.det(G)) # 第一基本形式开方 + + if callable(f): + if getattr(f, 'coordtype', None) == 'barycentric': + f = f(bcs) + else: + f = f(ps) + + cm = self.entity_measure('cell') + + if isinstance(f, (int, float)): # u 为标量常函数 + e = f*cm + elif bm.is_tensor(f): + if f.shape == (GD, ): # 常向量函数 + e = cm[:, None]*f + elif f.shape == (GD, GD): + e = cm[:, None, None]*f + else: + e = bm.einsum('q, cq..., cq -> c...', ws*rm, f, d) + else: + raise ValueError(f"Unsupported type of return value: {f.__class__.__name__}.") + + if celltype: + return e + else: + return bm.sum(e) + + def error(self, u, v, q=3, power=2, celltype=False) -> TensorLike: + """ + @brief Calculate the error between two functions. + """ + #TODO 测试 + GD = self.geo_dimension() + qf = self.quadrature_formula(q, etype='cell') + bcs, ws = qf.get_quadrature_points_and_weights() + ps = self.bc_to_point(bcs) + + rm = self.reference_cell_measure() + G = self.first_fundamental_form(bcs) + d = bm.sqrt(bm.linalg.det(G)) # 第一基本形式开方 + + if callable(u): + if getattr(u, 'coordtype', None) == 'barycentric': + u = u(bcs) + else: + u = u(ps) + + if callable(v): + if getattr(v, 'coordtype', None) == 'barycentric': + v = v(bcs) + else: + v = v(ps) + cm = self.entity_measure('cell') + NC = self.number_of_cells() + if v.shape[-1] == NC: + v = bm.swapaxes(v, 0, -1) + #f = bm.power(bm.abs(u - v), power) + f = bm.abs(u - v)**power + if len(f.shape) == 1: + f = f[:, None] + + if isinstance(f, (int, float)): # f为标量常函数 + e = f*cm + elif bm.is_tensor(f): + if f.shape == (GD, ): # 常向量函数 + e = cm[:, None]*f + elif f.shape == (GD, GD): + e = cm[:, None, None]*f + else: + e = bm.einsum('q, cq..., cq -> c...', ws*rm, f, d) + + if celltype is False: + #e = bm.power(bm.sum(e), 1/power) + e = bm.sum(e)**(1/power) + else: + e = bm.power(bm.sum(e, axis=tuple(range(1, len(e.shape)))), 1/power) + return e # float or (NC, ) + diff --git a/test/mesh/test_lagrange_quadrangle_mesh.py b/test/mesh/test_lagrange_quadrangle_mesh.py index a14a52b6b..2ceebc577 100644 --- a/test/mesh/test_lagrange_quadrangle_mesh.py +++ b/test/mesh/test_lagrange_quadrangle_mesh.py @@ -21,11 +21,13 @@ def test_surface_mesh(self, backend): surface = SphereSurface() mesh = QuadrangleMesh.from_unit_sphere_surface() - lmesh = LagrangeQuadrangleMesh.from_quadrangle_mesh(mesh, p=1, surface=surface) + fname = f"sphere_qtest_quad.vtu" + mesh.to_vtk(fname=fname) + + lmesh = LagrangeQuadrangleMesh.from_quadrangle_mesh(mesh, p=3, surface=surface) fname = f"sphere_qtest.vtu" lmesh.to_vtk(fname=fname) - """ @pytest.mark.parametrize("backend", ['numpy']) @pytest.mark.parametrize("data", from_quadrangle_mesh_data) def test_from_quadranglemesh(self, data, backend): @@ -44,7 +46,6 @@ def test_from_quadranglemesh(self, data, backend): cell = lmesh.entity('cell') np.testing.assert_allclose(bm.to_numpy(cell), data["cell"], atol=1e-14) - """ if __name__ == "__main__": From 36a404b4379359e7836aa30da54993ca8fb20454 Mon Sep 17 00:00:00 2001 From: BenHBLiu Date: Wed, 27 Nov 2024 10:48:50 +0800 Subject: [PATCH 45/63] update --- test/opt/test_iopt_alg.py | 196 ++++---------------------------------- 1 file changed, 20 insertions(+), 176 deletions(-) diff --git a/test/opt/test_iopt_alg.py b/test/opt/test_iopt_alg.py index 12b461e34..ceda3615e 100644 --- a/test/opt/test_iopt_alg.py +++ b/test/opt/test_iopt_alg.py @@ -1,195 +1,39 @@ import pytest from fealpy.backend import backend_manager as bm -from fealpy.opt import CrayfishOptAlg -from fealpy.opt import HoneybadgerAlg -from fealpy.opt import QuantumParticleSwarmOpt, LevyQuantumParticleSwarmOpt -from fealpy.opt import SnowAblationOpt -from fealpy.opt import GreyWolfOptimizer -from fealpy.opt import HippopotamusOptAlg -from fealpy.opt import CrestedPorcupineOpt -from fealpy.opt import BlackwingedKiteAlg -from fealpy.opt import CuckooSearchOpt -from fealpy.opt import ParticleSwarmOpt -from fealpy.opt import ButterflyOptAlg -from fealpy.opt import ExponentialTrigonometricOptAlg -from fealpy.opt import DifferentialEvolution -from fealpy.opt import DifferentialtedCreativeSearch -from fealpy.opt import initialize +from fealpy.opt import * from fealpy.opt.optimizer_base import Optimizer, opt_alg_options from iopt_data import iopt_data class TestIOptInterfaces: - - @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) - @pytest.mark.parametrize("data", iopt_data) - @pytest.mark.parametrize("NP", [100]) - def test_crayfish_opt_alg(self, backend, data, NP): - bm.set_backend(backend) - lb, ub = data['domain'] - x0 = initialize(NP, data['ndim'], ub, lb) - option = opt_alg_options(x0, data['objective'], data['domain'], NP) - optimizer = CrayfishOptAlg(option) - optimizer.run() - - @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) - @pytest.mark.parametrize("data", iopt_data) - @pytest.mark.parametrize("NP", [100]) - def test_honeybadger_opt_alg(self, backend, data, NP): - bm.set_backend(backend) - lb, ub = data['domain'] - x0 = initialize(NP, data['ndim'], ub, lb) - option = opt_alg_options(x0, data['objective'], data['domain'], NP) - optimizer = HoneybadgerAlg(option) - gbest, gbest_f = optimizer.run() - - @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) - @pytest.mark.parametrize("data", iopt_data) - @pytest.mark.parametrize("NP", [100]) - def test_quantumparticleswarm_opt_alg(self, backend, data, NP): - bm.set_backend(backend) - lb, ub = data['domain'] - x0 = initialize(NP, data['ndim'], ub, lb) - option = opt_alg_options(x0, data['objective'], data['domain'], NP) - optimizer = QuantumParticleSwarmOpt(option) - optimizer = LevyQuantumParticleSwarmOpt(option) - gbest, gbest_f = optimizer.run() - - @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) - @pytest.mark.parametrize("data", iopt_data) - @pytest.mark.parametrize("NP", [100]) - def test_snowmelt_opt_alg(self, backend, data, NP): - bm.set_backend(backend) - lb, ub = data['domain'] - x0 = initialize(NP, data['ndim'], ub, lb) - option = opt_alg_options(x0, data['objective'], data['domain'], NP) - optimizer = SnowAblationOpt(option) - gbest, gbest_f = optimizer.run() - - @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) - @pytest.mark.parametrize("data", iopt_data) - @pytest.mark.parametrize("NP", [100]) - def test_hippopotamus_optimizer(self, backend, data, NP): - bm.set_backend(backend) - lb, ub = data['domain'] - x0 = initialize(NP, data['ndim'], ub, lb) - option = opt_alg_options(x0, data['objective'], data['domain'], NP) - optimizer = HippopotamusOptAlg(option) - optimizer.run() - - @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) - @pytest.mark.parametrize("data", iopt_data) - @pytest.mark.parametrize("NP", [100]) - def test_grey_wolf_optimizer(self, backend, data, NP): - bm.set_backend(backend) - lb, ub = data['domain'] - x0 = initialize(NP, data['ndim'], ub, lb) - option = opt_alg_options(x0, data['objective'], data['domain'], NP) - optimizer = GreyWolfOptimizer(option) - gbest, gbest_f = optimizer.run() - - @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) - @pytest.mark.parametrize("data", iopt_data) - @pytest.mark.parametrize("NP", [130]) - def test_crested_porcupine_opt(self, backend, data, NP): - bm.set_backend(backend) - lb, ub = data['domain'] - x0 = initialize(NP, data['ndim'], ub, lb) - option = opt_alg_options(x0, data['objective'], data['domain'], NP) - optimizer = CrestedPorcupineOpt(option) - gbest, gbest_f = optimizer.run() - - @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) - @pytest.mark.parametrize("data", iopt_data) - @pytest.mark.parametrize("NP", [130]) - def test_black_winged_kite_alg(self, backend, data, NP): - bm.set_backend(backend) - lb, ub = data['domain'] - x0 = initialize(NP, data['ndim'], ub, lb) - option = opt_alg_options(x0, data['objective'], data['domain'], NP) - optimizer = BlackwingedKiteAlg(option) - gbest, gbest_f = optimizer.run() - - @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) - @pytest.mark.parametrize("data", iopt_data) - @pytest.mark.parametrize("NP", [130]) - def test_cuckoo_search_opt(self, backend, data, NP): - bm.set_backend(backend) - lb, ub = data['domain'] - x0 = initialize(NP, data['ndim'], ub, lb) - option = opt_alg_options(x0, data['objective'], data['domain'], NP) - optimizer = CuckooSearchOpt(option) - gbest, gbest_f = optimizer.run() - - @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) - @pytest.mark.parametrize("data", iopt_data) - @pytest.mark.parametrize("NP", [130]) - def test_particle_swarm_opt(self, backend, data, NP): - bm.set_backend(backend) - lb, ub = data['domain'] - x0 = initialize(NP, data['ndim'], ub, lb) - option = opt_alg_options(x0, data['objective'], data['domain'], NP) - optimizer = ParticleSwarmOpt(option) - gbest, gbest_f = optimizer.run() - - @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) - @pytest.mark.parametrize("data", iopt_data) - @pytest.mark.parametrize("NP", [130]) - def test_butterfly_opt_alg(self, backend, data, NP): - bm.set_backend(backend) - lb, ub = data['domain'] - x0 = initialize(NP, data['ndim'], ub, lb) - option = opt_alg_options(x0, data['objective'], data['domain'], NP) - optimizer = ButterflyOptAlg(option) - gbest, gbest_f = optimizer.run() - - @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) - @pytest.mark.parametrize("data", iopt_data) - @pytest.mark.parametrize("NP", [130]) - def test_exponential_trigonometric_opt_alg(self, backend, data, NP): - bm.set_backend(backend) - lb, ub = data['domain'] - x0 = initialize(NP, data['ndim'], ub, lb) - option = opt_alg_options(x0, data['objective'], data['domain'], NP) - optimizer = ExponentialTrigonometricOptAlg(option) - gbest, gbest_f = optimizer.run() - - @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) - @pytest.mark.parametrize("data", iopt_data) - @pytest.mark.parametrize("NP", [130]) - def test_differential_evolution(self, backend, data, NP): - bm.set_backend(backend) - lb, ub = data['domain'] - x0 = initialize(NP, data['ndim'], ub, lb) - option = opt_alg_options(x0, data['objective'], data['domain'], NP) - optimizer = DifferentialEvolution(option) - gbest, gbest_f = optimizer.run() - @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) @pytest.mark.parametrize("data", iopt_data) @pytest.mark.parametrize("NP", [100]) - def test_differentialted_creative_search(self, backend, data, NP): + def test_opt_alg(self, backend, data, NP): bm.set_backend(backend) lb, ub = data['domain'] x0 = initialize(NP, data['ndim'], ub, lb) option = opt_alg_options(x0, data['objective'], data['domain'], NP) - optimizer = DifferentialtedCreativeSearch(option) + # optimizer = HoneybadgerAlg(option) + # optimizer = CrayfishOptAlg(option) + # optimizer = QuantumParticleSwarmOpt(option) + # optimizer = LevyQuantumParticleSwarmOpt(option) + # optimizer = SnowAblationOpt(option) + # optimizer = HippopotamusOptAlg(option) + # optimizer = GreyWolfOptimizer(option) + # optimizer = CrestedPorcupineOpt(option) + # optimizer = BlackwingedKiteAlg(option) + # optimizer = CuckooSearchOpt(option) + # optimizer = ParticleSwarmOpt(option) + # optimizer = ButterflyOptAlg(option) + # optimizer = ExponentialTrigonometricOptAlg(option) + # optimizer = DifferentialEvolution(option) + # optimizer = DifferentialtedCreativeSearch(option) + optimizer = CuckooQuantumParticleSwarmOpt(option) gbest, gbest_f = optimizer.run() if __name__ == "__main__": - # pytest.main(["./test_iopt_alg.py", "-k", "test_honeybadger_opt_alg"]) - # pytest.main(["./test_iopt_alg.py", "-k", "test_crayfish_opt_alg"]) - # pytest.main(["./test_iopt_alg.py", "-k", "test_quantumparticleswarm_opt_alg"]) - # pytest.main(["./test_iopt_alg.py", "-k", "test_grey_wolf_optimizer"]) - # pytest.main(["./test_iopt_alg.py", "-k", "test_snowmelt_opt_alg"]) - # pytest.main(["./test_iopt_alg.py", "-k", "test_hippopotamus_optimizer"]) - # pytest.main(["./test_iopt_alg.py", "-k", "test_crested_porcupine_opt"]) - # pytest.main(["./test_iopt_alg.py", "-k", "test_black_winged_kite_alg"]) - # pytest.main(["./test_iopt_alg.py", "-k", "test_cuckoo_search_opt"]) - # pytest.main(["./test_iopt_alg.py", "-k", "test_particle_swarm_opt"]) - # pytest.main(["./test_iopt_alg.py", "-k", "test_butterfly_opt_alg"]) - # pytest.main(["./test_iopt_alg.py", "-k", "test_differential_evolution"]) - # pytest.main(["./test_iopt_alg.py", "-k", "test_exponential_trigonometric_opt_alg"]) - pytest.main(["./test_iopt_alg.py", "-k", "test_differentialted_creative_search"]) \ No newline at end of file + pytest.main(["./test_iopt_alg.py", "-k", "test_opt_alg"]) + \ No newline at end of file From 1df76839c4bdf07bfb5df407071111db42cce6be Mon Sep 17 00:00:00 2001 From: BenHBLiu Date: Wed, 27 Nov 2024 10:49:45 +0800 Subject: [PATCH 46/63] add C-QPSO --- .../opt/cuckoo_quantum_particle_swarm_opt.py | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 fealpy/opt/cuckoo_quantum_particle_swarm_opt.py diff --git a/fealpy/opt/cuckoo_quantum_particle_swarm_opt.py b/fealpy/opt/cuckoo_quantum_particle_swarm_opt.py new file mode 100644 index 000000000..270e2aaa4 --- /dev/null +++ b/fealpy/opt/cuckoo_quantum_particle_swarm_opt.py @@ -0,0 +1,99 @@ + +from ..backend import backend_manager as bm +from ..typing import TensorLike, Index, _S +from .. import logger +from .opt_function import levy +from .optimizer_base import Optimizer + +""" +Cuckoo Quantum Particle Swarm Optimization + +Reference: +~~~~~~~~~~ +Xiongfa Mai, Han-Bin Liu, Li-Bin Liu. +A New Hybrid Cuckoo Quantum-Behavior Particle Swarm Optimization Algorithm and its Application in Muskingum Model. +Neural Processing Letters, 2023, 55: 8309-8337. +""" +class CuckooQuantumParticleSwarmOpt(Optimizer): + def __init__(self, option) -> None: + super().__init__(option) + + + def run(self): + options = self.options + a = options["x0"] + N = options["NP"] + fit = self.fun(a)[:, None] + MaxIT = options["MaxIters"] + dim = options["ndim"] + lb, ub = options["domain"] + pbest = bm.copy(a) + pbest_f = bm.copy(fit) + gbest_index = bm.argmin(pbest_f) + gbest = pbest[gbest_index] + gbest_f = pbest_f[gbest_index] + + x = bm.random.rand(N, dim) * (ub - lb) + lb + fit_x = self.fun(x)[:, None] + z = bm.random.rand(N, dim) * (ub - lb) + lb + fit_z = self.fun(z)[:, None] + NS = 0 + best = bm.zeros((1, MaxIT)) + NN = int(N/2) + for it in range(0, MaxIT): + alpha = 1 - 0.5 * bm.log(bm.array(it + 1)) / bm.log(bm.array(MaxIT)) # Rapid Decreasing Contraction-Expansion Coefficient + delta = bm.abs((fit - bm.max(fit)) / (bm.min(fit) - bm.max(fit) + 1e-10)) + mbest = bm.sum(delta * pbest, axis=0) / N # The Weighted Mean Best Position + phi = bm.random.rand(N, 1) + p = phi * pbest + (1 - phi) * gbest # local attractor + rand = bm.random.rand(N, 1) + a = p + alpha * bm.abs(mbest - a) * bm.log(1 / bm.random.rand(N, 1)) * (1 - 2 * (rand >= 0.5)) # update + a = a + (lb - a) * (a < lb) + (ub - a) * (a > ub) + fit = self.fun(a)[:, None] + + Pa = 0.85 + 1.3 * ((it + 1) / MaxIT) ** 3 - 0.5 * (2 * (it + 1) / MaxIT) ** 2 + alpha_x = 1e-4 * MaxIT * bm.exp(bm.array((-4 * (it + 1)) / MaxIT)) + omega = bm.cos(bm.array(bm.pi * it / MaxIT) + 0.5 * bm.pi) + 1 + x_new = x + alpha_x * levy(N, dim, 1.5) * (x - gbest) + x_new = x_new + (lb - x_new) * (x_new < lb) + (ub - x_new) * (x_new > ub) + fit_new = self.fun(x_new)[:, None] + mask = fit_new < fit_x + x, fit_x = bm.where(mask, x_new, x), bm.where(mask, fit_new, fit_x) + x_new = omega * x + bm.random.rand(N, 1) * bm.where((bm.random.rand(N, dim) - Pa) < 0, 0, 1) * (x[bm.random.randint(0, N, (N,))] - x[bm.random.randint(0, N, (N,))]) + x_new = x_new + (lb - x_new) * (x_new < lb) + (ub - x_new) * (x_new > ub) + fit_new = self.fun(x_new)[:, None] + mask = fit_new < fit_x + x, fit_x = bm.where(mask, x_new, x), bm.where(mask, fit_new, fit_x) + + d_rand = bm.random.rand(N, 1) + z_new = d_rand * x + (1 - d_rand) * a + fit_new = self.fun(z_new)[:, None] + mask = fit_new < fit_z + z, fit_z = bm.where(mask, z_new, z), bm.where(mask, fit_new, fit_z) + + mask = fit_x < fit_z + x, fit_x = bm.where(mask, z, x), bm.where(mask, fit_z, fit_x) + index = bm.argmin(fit_x) + (gbest_f, gbest) = (fit_x[index], x[index]) if fit_x[index] < gbest_f else (gbest_f, gbest) + + maks = fit < fit_z + a, fit = bm.where(maks, z, a), bm.where(maks, fit_z, fit) + mask = fit < pbest_f + pbest, pbest_f = bm.where(mask, a, pbest), bm.where(fit < pbest_f, fit, pbest_f) + gbest_idx = bm.argmin(pbest_f) + (gbest_f, gbest) = (pbest_f[gbest_idx], pbest[gbest_idx]) if pbest_f[gbest_idx] < gbest_f else (gbest_f, gbest) + + best[0, it] = gbest_f[0] + if it > 0: + if best[0, it] < best[0, it - 1]: + NS = NS + 1 + if NS == 5: + index_a = bm.argsort(fit, axis=0) + index_x = bm.argsort(fit_x) + a[index_a[NN:N][:, 0]] = bm.random.rand(NN, dim) * (ub - lb) + lb + x[index_x[NN:N][:, 0]] = bm.random.rand(NN, dim) * (ub - lb) + lb + fit = self.fun(a)[:, None] + fit_x = self.fun(x)[:, None] + NS = 0 + + return gbest, gbest_f \ No newline at end of file From 29f4b80701d54d1b4becc7bbabf99fab6df0be45 Mon Sep 17 00:00:00 2001 From: "brighthe98@gmail.com" Date: Wed, 27 Nov 2024 14:19:15 +0800 Subject: [PATCH 47/63] update --- ...p_detail_linear_element_hexahedron_mesh.py | 2 +- app/soptx/linear_elasticity/test_abaqus.py | 14 +- app/soptx/soptx/opt/compliance.py | 170 ++++++++++-- app/soptx/soptx/solver/elastic_fem_solver.py | 2 +- app/soptx/soptx/tests/test_ad.py | 252 ++++++++++++++++++ app/soptx/soptx/tests/test_compliance.py | 20 +- 6 files changed, 416 insertions(+), 44 deletions(-) create mode 100644 app/soptx/soptx/tests/test_ad.py diff --git a/app/soptx/linear_elasticity/exp_detail_linear_element_hexahedron_mesh.py b/app/soptx/linear_elasticity/exp_detail_linear_element_hexahedron_mesh.py index 760b34bd2..fa774a905 100644 --- a/app/soptx/linear_elasticity/exp_detail_linear_element_hexahedron_mesh.py +++ b/app/soptx/linear_elasticity/exp_detail_linear_element_hexahedron_mesh.py @@ -59,7 +59,7 @@ def dirichlet(self, points: TensorLike) -> TensorLike: bm.set_backend('numpy') -nx, ny, nz = 2, 2, 2 +nx, ny, nz = 3, 3, 3 mesh = HexahedronMesh.from_box(box=[0, 1, 0, 1, 0, 1], nx=nx, ny=ny, nz=nz, device=bm.get_device('cpu')) diff --git a/app/soptx/linear_elasticity/test_abaqus.py b/app/soptx/linear_elasticity/test_abaqus.py index 32cdc9013..ed0fd667d 100644 --- a/app/soptx/linear_elasticity/test_abaqus.py +++ b/app/soptx/linear_elasticity/test_abaqus.py @@ -64,7 +64,7 @@ def dirichlet(self, points: TensorLike) -> TensorLike: nx, ny, nz = 1, 1, 1 mesh = HexahedronMesh.from_box(box=[0, 1, 0, 1, 0, 1], nx=nx, ny=ny, nz=nz, device=bm.get_device('cpu')) - +ip2 = mesh.interpolation_points(3) NC = mesh.number_of_cells() cm = mesh.cell_volume() node = mesh.entity('node') @@ -78,6 +78,9 @@ def dirichlet(self, points: TensorLike) -> TensorLike: phi = space.basis(bcs) # (1, NQ, ldof) gphi = space.grad_basis(bc=bcs) # (NC, NQ, ldof, GD) +J = mesh.jacobi_matrix(bcs) +detJ = bm.linalg.det(J) + tensor_space = TensorFunctionSpace(space, shape=(3, -1)) tgdof = tensor_space.number_of_global_dofs() phi_tensor = tensor_space.basis(bcs) # (1, NQ, tldof, GD) @@ -95,11 +98,16 @@ def dirichlet(self, points: TensorLike) -> TensorLike: gphi=gphi, shear_order=['xy', 'yz', 'zx']) D = linear_elastic_material.elastic_matrix(bcs) KE = bm.einsum('q, c, cqki, cqkl, cqlj -> cij', ws, cm, B, D, B) +KE = bm.einsum('c, cqki, cqkl, cqlj -> cij', cm, B, D, B) +KE2 = bm.einsum('q, cq, cqki, cqkl, cqlj -> cij', ws, detJ, B, D, B) + +error = bm.max(bm.abs(KE[0] - KE2[0])) -integrator_K = LinearElasticIntegrator(material=linear_elastic_material, q=tensor_space.p+3) +integrator_K = LinearElasticIntegrator(material=linear_elastic_material, q=q) KE_maual = integrator_K.assembly(space=tensor_space) KE0 = KE[0] +KE_maual[0] = KE_maual[0] I = bm.broadcast_to(cell2tdof[:, :, None], shape=KE.shape) J = bm.broadcast_to(cell2tdof[:, None, :], shape=KE.shape) @@ -128,7 +136,7 @@ def dirichlet(self, points: TensorLike) -> TensorLike: fixed_node_index = bm.tensor([0]) export_to_inp(filename='/home/heliang/FEALPy_Development/fealpy/app/soptx/linear_elasticity/local_stiffness_matrix.inp', nodes=node, elements=cell, fixed_nodes=fixed_node_index, load_nodes=load_node_indices, loads=F_load_nodes, - young_modulus=206e3, poisson_ratio=0.3, density=7850.0) + young_modulus=206e3, poisson_ratio=0.3, density=7.85e-9) diff --git a/app/soptx/soptx/opt/compliance.py b/app/soptx/soptx/opt/compliance.py index 4189f71e3..da25b7453 100644 --- a/app/soptx/soptx/opt/compliance.py +++ b/app/soptx/soptx/opt/compliance.py @@ -95,13 +95,84 @@ def _compute_element_compliance(self, u: TensorLike) -> TensorLike: # 更新缓存 self._element_compliance = bm.einsum('ci, cik, ck -> c', ue, ke0, ue) + return self._element_compliance + def _compute_gradient_manual(self, + rho: TensorLike, + u: Optional[TensorLike] = None) -> TensorLike: + """使用解析方法计算梯度""" + if u is None: + u = self._update_u(rho) + + ce = (self.get_element_compliance() + if self._element_compliance is not None + else self._compute_element_compliance(u)) + + dE = self.material_properties.calculate_elastic_modulus_derivative(rho) + dc = -bm.einsum('c, c -> c', dE, ce) + + return dc + + def _compute_gradient_auto(self, + rho: TensorLike, + u: Optional[TensorLike] = None) -> TensorLike: + """使用自动微分计算梯度""" + # 首先获取位移场(只需要计算一次, 在自动微分之外完成) + if u is None: + u = self._update_u(rho) + + # 获取基础刚度矩阵和单元自由度映射 + ke0 = self.solver.get_base_local_stiffness_matrix() + cell2dof = self.solver.tensor_space.cell_to_dof() + ue = u[cell2dof] # 获取单元位移 + + def compliance_contribution(rho_i: float, ue_i: TensorLike, ke0_i: TensorLike) -> float: + """计算单个单元的柔顺度贡献 + + Parameters + ---------- + rho_i : 单个单元的密度值 + ue_i : (tldof, ), 单个单元的位移向量 + ke0_i : (tldof, tldof), 单个单元的基础刚度矩阵 + """ + # 计算该单元的材料属性 + E = self.material_properties.calculate_elastic_modulus(rho_i) + + # 计算单元柔顺度并取负值 : -(E * u^T * K * u) + return -E * bm.einsum('i, ij, j', ue_i, ke0_i, ue_i) + + # 创建向量化的梯度计算函数 + # 最内层:lambda x: compliance_contribution(x, u, k) + # 这创建了一个关于单个密度值的函数 + # x 是将要求导的变量(单元密度) + # u 和 k 是固定的参数(位移和刚度矩阵) + + # 中间层:bm.grad(...)(r) + # grad 计算上述函数关于 x 的导数 + # (r) 表示在点 r 处求值 + + # 外层:bm.vmap(lambda r, u, k: ...) + # vmap 将这个操作向量化,使其可以并行处理所有单元 + vmap_grad = bm.vmap(lambda r, u, k: bm.grad( + lambda x: compliance_contribution(x, u, k) + )(r)) + + # 直接对所有单元进行并行计算 + # 这一步同时处理所有单元,对应关系是: + + # rho 对应每个单元的密度值 + # ue 对应每个单元的位移向量 + # ke0 对应每个单元的基础刚度矩阵 + return vmap_grad(rho, ue, ke0) + def get_element_compliance(self) -> TensorLike: """获取单元柔顺度""" if self._element_compliance is None: raise ValueError("必须先调用 fun() 计算柔顺度") + return self._element_compliance + #--------------------------------------------------------------------------- # 优化相关方法 @@ -132,11 +203,12 @@ def fun(self, c = bm.einsum('c, c -> ', E, ce) return c - + def jac(self, rho: TensorLike, u: Optional[TensorLike] = None, - filter_params: Optional[Dict[str, Any]] = None) -> TensorLike: + filter_params: Optional[Dict[str, Any]] = None, + diff_mode: Literal["auto", "manual"] = "manual") -> TensorLike: """计算目标函数梯度 Parameters @@ -144,48 +216,88 @@ def jac(self, rho : 密度场 u : 可选的位移场,如果为 None 则自动计算或使用缓存的位移场 filter_params : 滤波器参数 + diff_mode : 梯度计算方式 + - "manual": 使用解析推导的梯度公式(默认) + - "auto": 使用自动微分技术 + Returns ------- dc : 目标函数对密度的梯度 """ + # 创建计时器 - t = timer("Grad Timing") - next(t) # 启动计时器 + t = timer(f"Gradient Computation ({diff_mode} mode)") + next(t) - # 获取位移场 - dc_func = bm.vmap(bm.jacfwd(func=self.fun)) # 输入比输出少 - dc_value = dc_func(rho) - t.send('auto grad') - print("dc_func:", dc_func) - print("dc_auto:", dc_value) - # dc = bm.jacrev(func=self.fun) # 输入比输出多 + # 选择计算方法 + if diff_mode == "manual": + dc = self._compute_gradient_manual(rho, u) + t.send('Manual gradient computed') + elif diff_mode == "auto": + dc = self._compute_gradient_auto(rho) + t.send('Automatic gradient computed') - if u is None: - u = self._update_u(rho) - - # 获取单元柔度 - ce = (self.get_element_compliance() - if self._element_compliance is not None - else self._compute_element_compliance(u)) - - # 计算梯度 - dE = self.material_properties.calculate_elastic_modulus_derivative(rho) - dc = -bm.einsum('c, c -> c', dE, ce) - print("dc_manual:", dc) - t.send('manual grad') + # 应用滤波(如果需要) + if self.filter is not None: + dc = self.filter.filter_sensitivity(dc, rho, 'objective', filter_params) + t.send("Sensitivity filter applied") # 结束计时 t.send(None) + + return dc + + # def jac(self, + # rho: TensorLike, + # u: Optional[TensorLike] = None, + # filter_params: Optional[Dict[str, Any]] = None) -> TensorLike: + # """计算目标函数梯度 + + # Parameters + # ---------- + # rho : 密度场 + # u : 可选的位移场,如果为 None 则自动计算或使用缓存的位移场 + # filter_params : 滤波器参数 + + # Returns + # ------- + # dc : 目标函数对密度的梯度 + # """ + # # 创建计时器 + # t = timer("Grad Timing") + # next(t) # 启动计时器 + # # 获取位移场 + # dc_func = bm.vmap(bm.jacfwd(func=self.fun)) # 输入比输出少 + # dc_value = dc_func(rho) + # t.send('auto grad') + # print("dc_func:", dc_func) + # print("dc_auto:", dc_value) + # # dc = bm.jacrev(func=self.fun) # 输入比输出多 + # if u is None: + # u = self._update_u(rho) + + # # 获取单元柔度 + # ce = (self.get_element_compliance() + # if self._element_compliance is not None + # else self._compute_element_compliance(u)) + # # 计算梯度 + # dE = self.material_properties.calculate_elastic_modulus_derivative(rho) + # dc = -bm.einsum('c, c -> c', dE, ce) + # print("dc_manual:", dc) + # t.send('manual grad') + + # # 结束计时 + # t.send(None) - # 应用滤波 - if self.filter is None: - return dc + # # 应用滤波 + # if self.filter is None: + # return dc - # 明确指定这是目标函数的梯度 - return self.filter.filter_sensitivity(dc, rho, 'objective', filter_params) + # # 明确指定这是目标函数的梯度 + # return self.filter.filter_sensitivity(dc, rho, 'objective', filter_params) def hess(self, rho: TensorLike, lambda_: dict) -> TensorLike: """计算目标函数 Hessian 矩阵(未实现)""" diff --git a/app/soptx/soptx/solver/elastic_fem_solver.py b/app/soptx/soptx/solver/elastic_fem_solver.py index e0e343eb5..2b39b197a 100644 --- a/app/soptx/soptx/solver/elastic_fem_solver.py +++ b/app/soptx/soptx/solver/elastic_fem_solver.py @@ -254,7 +254,7 @@ def _apply_boundary_conditions(self, K: CSRTensor, F: TensorLike) -> tuple[CSRTe isBdDof = self.tensor_space.is_boundary_dof(threshold=threshold, method='interp') - F = F - K.matmul(uh_bd) + F = F - K.matmul(uh_bd) # in-place operations F[isBdDof] = uh_bd[isBdDof] dbc = DirichletBC(space=self.tensor_space, gd=dirichlet, diff --git a/app/soptx/soptx/tests/test_ad.py b/app/soptx/soptx/tests/test_ad.py new file mode 100644 index 000000000..04f73e5b7 --- /dev/null +++ b/app/soptx/soptx/tests/test_ad.py @@ -0,0 +1,252 @@ +"""Test cases for compliance objective and volume constraint.""" + +from dataclasses import dataclass +import dataclasses +from typing import Literal, Optional, Union, Dict, Any + +from fealpy.backend import backend_manager as bm +from fealpy.mesh import UniformMesh2d, UniformMesh3d, TriangleMesh +from fealpy.functionspace import LagrangeFESpace, TensorFunctionSpace + +from soptx.material import ( + ElasticMaterialConfig, + ElasticMaterialProperties, + SIMPInterpolation +) +from soptx.pde import MBBBeam2dData1, Cantilever2dData1, Cantilever3dData1 +from soptx.solver import ElasticFEMSolver, AssemblyMethod, AssemblyConfig +from soptx.opt import ComplianceObjective, VolumeConstraint +from soptx.filter import Filter, FilterConfig + +bm.set_backend('pytorch') + +mesh = TriangleMesh.from_box(box=[0, 1, 0, 1], nx=10, ny=10) +@dataclass +class TestConfig: + """Configuration for topology optimization test cases.""" + # Required parameters (no default values) + problem_type: Literal['mbb_2d', 'cantilever_2d', 'cantilever_3d'] + nx: int + ny: int + volume_fraction: float + filter_radius: float + filter_type: Literal['sensitivity', 'density', 'heaviside'] + + # Optional parameters (with default values) + nz: Optional[int] = None # Only for 3D problems + elastic_modulus: float = 1.0 + poisson_ratio: float = 0.3 + minimal_modulus: float = 1e-9 + penalty_factor: float = 3.0 + projection_beta: Optional[float] = None # Only for Heaviside projection + move_limit: float = 0.2 + tolerance: float = 0.01 + initial_lambda: float = 1e9 + bisection_tol: float = 1e-3 + + assembly_method: AssemblyMethod = AssemblyMethod.STANDARD # 矩阵组装方法 + quadrature_degree_increase: int = 3 # 积分阶数增量 + solver_type: Literal['cg', 'direct'] = 'cg' # 求解器类型 + solver_params: Optional[Dict[str, Any]] = None # 求解器参数 + +def create_base_components(config: TestConfig): + """Create basic components needed for topology optimization based on configuration.""" + # Create mesh and determine dimensionality + if config.problem_type == 'cantilever_3d': + extent = [0, config.nx, 0, config.ny, 0, config.nz] + h = [1.0, 1.0, 1.0] + origin = [0.0, 0.0, 0.0] + mesh = UniformMesh3d( + extent=extent, h=h, origin=origin, + ipoints_ordering='zyx', flip_direction='y', + device='cpu' + ) + dimension = 3 + else: + extent = [0, config.nx, 0, config.ny] + h = [1.0, 1.0] + origin = [0.0, 0.0] + mesh = UniformMesh2d( + extent=extent, h=h, origin=origin, + ipoints_ordering='yx', flip_direction='y', + device='cpu' + ) + dimension = 2 + + # Create function spaces + p = 1 + space_C = LagrangeFESpace(mesh=mesh, p=p, ctype='C') + tensor_space_C = TensorFunctionSpace(space_C, (-1, dimension)) + space_D = LagrangeFESpace(mesh=mesh, p=p-1, ctype='D') + + # Create material properties + material_config = ElasticMaterialConfig( + elastic_modulus=config.elastic_modulus, + minimal_modulus=config.minimal_modulus, + poisson_ratio=config.poisson_ratio, + plane_assumption="3D" if dimension == 3 else "plane_stress" + ) + interpolation_model = SIMPInterpolation(penalty_factor=config.penalty_factor) + material_properties = ElasticMaterialProperties( + config=material_config, + interpolation_model=interpolation_model + ) + + # Create PDE problem based on problem type + if config.problem_type == 'mbb_2d': + pde = MBBBeam2dData1( + xmin=0, xmax=config.nx*h[0], + ymin=0, ymax=config.ny*h[1] + ) + elif config.problem_type == 'cantilever_2d': + pde = Cantilever2dData1( + xmin=0, xmax=config.nx*h[0], + ymin=0, ymax=config.ny*h[1] + ) + elif config.problem_type == 'cantilever_3d': + pde = Cantilever3dData1( + xmin=0, xmax=config.nx*h[0], + ymin=0, ymax=config.ny*h[1], + zmin=0, zmax=config.nz*h[2] + ) + + assembly_config = AssemblyConfig( + method=config.assembly_method, + quadrature_degree_increase=config.quadrature_degree_increase + ) + + # 设置默认的求解器参数 + default_solver_params = { + 'cg': {'maxiter': 5000, 'atol': 1e-12, 'rtol': 1e-12}, + 'direct': {'solver_type': 'mumps'} + } + solver_params = config.solver_params or default_solver_params[config.solver_type] + + solver = ElasticFEMSolver( + material_properties=material_properties, + tensor_space=tensor_space_C, + pde=pde, + assembly_config=assembly_config, + solver_type=config.solver_type, # 添加默认求解器类型 + solver_params=solver_params # 添加求解器参数 + ) + + # Initialize density field + array = config.volume_fraction * bm.ones(mesh.number_of_cells(), dtype=bm.float64) + rho = space_D.function(array) + + return mesh, space_D, material_properties, solver, rho + + +def test_compliance_objective(config: TestConfig): + """Test compliance objective computation and sensitivity analysis.""" + print(f"\n=== Testing Compliance Objective with {config.filter_type} filter ===") + + # Create base components + mesh, space_D, material_properties, solver, rho = create_base_components(config) + + # Test solver + solver.update_density(rho[:]) + solver_result = solver.solve_cg() + displacement = solver_result.displacement + print(f"\nSolver information:") + print(f"- Displacement shape: {displacement.shape}:\n {displacement[:]}") + + # Create filter + filter_config = FilterConfig( + filter_type={'sensitivity': 0, 'density': 1, 'heaviside': 2}[config.filter_type], + filter_radius=config.filter_radius + ) + filter_obj = Filter(filter_config) + filter_obj.initialize(mesh) + + # Create objective and constraint + objective = ComplianceObjective( + material_properties=material_properties, + solver=solver, + filter=filter_obj + ) + constraint = VolumeConstraint( + mesh=mesh, + volume_fraction=config.volume_fraction, + filter=filter_obj + ) + + # Test objective function + obj_value = objective.fun(rho=rho[:], u=displacement) + print(f"Objective function value: {obj_value:.6e}") + + # Test element compliance + ce = objective.get_element_compliance() + print(f"\nElement compliance information:") + print(f"- Shape: {ce.shape}:\n {ce}") + print(f"- Min: {bm.min(ce):.6e}") + print(f"- Max: {bm.max(ce):.6e}") + print(f"- Mean: {bm.mean(ce):.6e}") + + # Test sensitivity + dce = objective.jac(rho=rho[:], u=displacement, diff_mode="manual") + print(f"\nElement sensitivity information (manual):") + print(f"- Shape: {dce.shape}:\n, {dce}") + print(f"- Min: {bm.min(dce):.6e}") + print(f"- Max: {bm.max(dce):.6e}") + print(f"- Mean: {bm.mean(dce):.6e}") + + dce_auto = objective.jac(rho=rho[:], u=displacement, diff_mode='auto') + print(f"\nElement sensitivity information (auto):") + print(f"- Shape: {dce_auto.shape}:\n, {dce_auto}") + print(f"- Min: {bm.min(dce_auto):.6e}") + print(f"- Max: {bm.max(dce_auto):.6e}") + print(f"- Mean: {bm.mean(dce_auto):.6e}") + + # Test constraint + constraint_value = constraint.fun(rho=rho[:]) + print(f"\nConstraint function value: {constraint_value:.6e}") + + gradient = constraint.jac(rho=rho[:]) + print(f"\nConstraint gradient information:") + print(f"- Shape: {gradient.shape}") + print(f"- Min: {bm.min(gradient):.6e}") + print(f"- Max: {bm.max(gradient):.6e}") + print(f"- Mean: {bm.mean(gradient):.6e}") + + return { + 'objective_value': obj_value, + 'element_compliance': ce, + 'sensitivity': dce, + 'constraint_value': constraint_value, + 'constraint_gradient': gradient + } + +if __name__ == "__main__": + # Test 3D case with density filter + config_3d = TestConfig( + problem_type='cantilever_3d', + nx=60, ny=20, nz=4, + volume_fraction=0.3, + filter_radius=1.5, + filter_type='density' + ) + # results_3d = test_compliance_objective(config_3d) + + # Test 2D case with sensitivity filter + config_2d = TestConfig( + problem_type='mbb_2d', + nx=60, ny=20, + volume_fraction=0.5, + filter_radius=2.4, + filter_type='sensitivity' + ) + # results_2d = test_compliance_objective(config_2d) + + # Test 2D case with sensitivity filter + config_cantilever_2d = TestConfig( + problem_type='cantilever_2d', + nx=160, ny=100, + # nx=8, ny=5, + volume_fraction=0.4, + filter_radius=6, + filter_type='sensitivity' + ) + + result = test_compliance_objective(config_cantilever_2d) \ No newline at end of file diff --git a/app/soptx/soptx/tests/test_compliance.py b/app/soptx/soptx/tests/test_compliance.py index 888a0b23a..2d27c9df2 100644 --- a/app/soptx/soptx/tests/test_compliance.py +++ b/app/soptx/soptx/tests/test_compliance.py @@ -202,17 +202,17 @@ def test_compliance_objective(config: TestConfig): filter=filter_obj ) - # # Test objective function - # obj_value = objective.fun(rho=rho[:], u=displacement) - # print(f"Objective function value: {obj_value:.6e}") + # Test objective function + obj_value = objective.fun(rho=rho[:], u=displacement) + print(f"Objective function value: {obj_value:.6e}") - # # Test element compliance - # ce = objective.get_element_compliance() - # print(f"\nElement compliance information:") - # print(f"- Shape: {ce.shape}:\n {ce}") - # print(f"- Min: {bm.min(ce):.6e}") - # print(f"- Max: {bm.max(ce):.6e}") - # print(f"- Mean: {bm.mean(ce):.6e}") + # Test element compliance + ce = objective.get_element_compliance() + print(f"\nElement compliance information:") + print(f"- Shape: {ce.shape}:\n {ce}") + print(f"- Min: {bm.min(ce):.6e}") + print(f"- Max: {bm.max(ce):.6e}") + print(f"- Mean: {bm.mean(ce):.6e}") # Test sensitivity dce = objective.jac(rho=rho[:], u=displacement) From f94bee59970c85522d664e991724af8e04e8e19e Mon Sep 17 00:00:00 2001 From: chaos <2250213115@qq.com> Date: Wed, 27 Nov 2024 14:50:05 +0800 Subject: [PATCH 48/63] update --- .../near_field_data_generator.py | 5 +- app/lafem-ims/lafemims/model/ims.py | 927 ++++++++++++++++++ app/lafem-ims/lafemims/model/unet.py | 196 ++++ app/lafem-ims/lafemims/model/ut.py | 912 +++++++++++++++++ .../ml/generator/near_field_data_generator.py | 7 +- 5 files changed, 2042 insertions(+), 5 deletions(-) create mode 100644 app/lafem-ims/lafemims/model/ims.py create mode 100644 app/lafem-ims/lafemims/model/unet.py create mode 100644 app/lafem-ims/lafemims/model/ut.py diff --git a/app/lafem-ims/lafemims/data_generator/near_field_data_generator.py b/app/lafem-ims/lafemims/data_generator/near_field_data_generator.py index 0c7ac2bea..7f447f482 100644 --- a/app/lafem-ims/lafemims/data_generator/near_field_data_generator.py +++ b/app/lafem-ims/lafemims/data_generator/near_field_data_generator.py @@ -2,9 +2,10 @@ import os import matplotlib.pyplot as plt from numpy.typing import NDArray -import numpy as bm +import numpy as np from typing import Sequence, Callable +from fealpy.backend import backend_manager as bm from fealpy.mesh import TriangleMesh, QuadrangleMesh, UniformMesh2d from fealpy.pde.pml_2d import PMLPDEModel2d from fealpy.functionspace import LagrangeFESpace @@ -200,7 +201,7 @@ def save(self, save_path: str, scatterer_index: int): name = f"{k_name}, d={d_name}" data_dict[name] = self.data_for_dsm(k=k_values[i], d=d_values[j]) filename = os.path.join(save_path, f"data_for_dsm_{scatterer_index}.npz") - bm.savez(filename, **data_dict) + np.savez(filename, **data_dict) def visualization_of_nearfield_data(self, k: float, d: Sequence[float]): """ diff --git a/app/lafem-ims/lafemims/model/ims.py b/app/lafem-ims/lafemims/model/ims.py new file mode 100644 index 000000000..6a443bf22 --- /dev/null +++ b/app/lafem-ims/lafemims/model/ims.py @@ -0,0 +1,927 @@ +import math +import numbers +import os +import sys +from collections import OrderedDict +from datetime import date + +import h5py +import numpy as np +import torch +import random + + +from scipy.io import loadmat +from scipy.sparse import csr_matrix, diags +from sklearn.model_selection import KFold +from torch import nn +import torch.nn.functional as F +from torch.utils.data import Dataset, DataLoader +from torch.optim.lr_scheduler import OneCycleLR, _LRScheduler +from torch.utils.data import DataLoader +from tqdm.auto import tqdm +from typing import Callable, Dict +from torch.utils.tensorboard import SummaryWriter +try: + from libs.utils import * +except: + from utils import * + +current_path = os.path.dirname(os.path.abspath(__file__)) +SRC_ROOT = os.path.dirname(os.path.dirname(current_path)) +MODEL_PATH = default(os.environ.get('MODEL_PATH'), + os.path.join(SRC_ROOT, 'models')) +DATA_PATH = default(os.environ.get('DATA_PATH'), + os.path.join(SRC_ROOT, 'data')) +FIG_PATH = default(os.environ.get('FIG_PATH'), + os.path.join(os.path.dirname(SRC_ROOT), 'figures')) +EPOCH_SCHEDULERS = ['ReduceLROnPlateau', 'StepLR', 'MultiplicativeLR', + 'MultiStepLR', 'ExponentialLR', 'LambdaLR'] +PI = math.pi +SEED = default(os.environ.get('SEED'), 1127802) +PHI_NAME = "phi_bm10bp1" +COEFF_NAME = "I_true_num4" + +def pooling_2d(mat, kernel_size: tuple = (2, 2), method='mean', padding=False): + '''Non-overlapping pooling on 2D data (or 2D data stacked as 3D array). + + mat: ndarray, input array to pool. (m, n) or (bsz, m, n) + kernel_size: tuple of 2, kernel size in (ky, kx). + method: str, 'max for max-pooling, + 'mean' for mean-pooling. + pad: bool, pad or not. If no pad, output has size + n//f, n being size, f being kernel size. + if pad, output has size ceil(n/f), padding is nan + so when computing mean the 0 is counted + + Return : pooled matrix. + + Modified from https://stackoverflow.com/a/49317610/622119 + to handle the case of batch edge matrices + CC BY-SA 3.0 + ''' + + m, n = mat.shape[-2:] + ky, kx = kernel_size + + def _ceil(x, y): return int(np.ceil(x/float(y))) + + if padding: + ny = _ceil(m, ky) + nx = _ceil(n, kx) + size = mat.shape[:-2] + (ny*ky, nx*kx) + sy = (ny*ky - m)//2 + sx = (nx*kx - n)//2 + _sy = ny*ky - m - sy + _sx = nx*kx - n - sx + + mat_pad = np.full(size, np.nan) + mat_pad[..., sy:-_sy, sx:-_sx] = mat + else: + ny = m//ky + nx = n//kx + mat_pad = mat[..., :ny*ky, :nx*kx] + + new_shape = mat.shape[:-2] + (ny, ky, nx, kx) + + if method == 'max': + result = np.nanmax(mat_pad.reshape(new_shape), axis=(-3, -1)) + elif method == 'mean': + result = np.nanmean(mat_pad.reshape(new_shape), axis=(-3, -1)) + else: + raise NotImplementedError("pooling method not implemented.") + + return result + + +def train_batch_eit(model, loss_func, data, optimizer, lr_scheduler, device, grad_clip=0.99): + optimizer.zero_grad() + x, gradx = data["phi"].to(device), data["gradphi"].to(device) + grid_c, grid = data['grid_c'].to(device), data['grid'].to(device) + targets = data["targets"].to(device) + + # pos is for attention, grid is the finest grid + out_ = model(x, gradx, pos=grid_c, grid=grid) + out = out_['preds'] + + # out is (b, n, n, 1) + loss = loss_func(out, targets) + + loss.backward() + optimizer.step() + if lr_scheduler: + lr_scheduler.step() + + return loss.item() + +# def train_batch_ims(model, loss_func, data, optimizer, optimizer_, lr_scheduler, lr_scheduler_, device, grad_clip=0.99): +def train_batch_ims(model, loss_func, data, optimizer, lr_scheduler, device, grad_clip=0.99): + + optimizer.zero_grad() + u_s_real = data["u_s_real"].to(device) + u_s_imag = data["u_s_imag"].to(device) + u_s = torch.stack([u_s_real, u_s_imag], dim=0) + targets = data["targets"].to(device) + + out_ = model(u_s) + out = out_['preds'] + + loss_1 = loss_func(out, targets) + + loss_1.backward() + optimizer.step() + + if lr_scheduler: + lr_scheduler.step() + + + for param in model.parameters(): + param.requires_grad = False + + # model[0].s[0].requires_grad = False + # model[0].s[1].requires_grad = False + # model[0].s[2].requires_grad = False + + for param in model[2].out.parameters(): + param.requires_grad = True + + optimizer.zero_grad() + u_s_real = data["u_s_real"].to(device) + u_s_imag = data["u_s_imag"].to(device) + u_s = torch.stack([u_s_real, u_s_imag], dim=0) + targets = data["targets"].to(device) + + out_ = model(u_s) + out = out_['preds'] + + loss_2 = loss_func(out, targets) + + loss_2.backward() + optimizer.step() + if lr_scheduler: + lr_scheduler.step() + + for param in model.parameters(): + param.requires_grad = True + + if hasattr(model[0], 's') and isinstance(model[0].s, torch.nn.Parameter): + model[0].s.requires_grad = False # 设置model[0].s不需要计算梯度 + + + return loss_2.item() + +def train_batch_ims_LBFGS(model, loss_func, data, optimizer, lr_scheduler, device, grad_clip=0.99): + def closure(): + optimizer.zero_grad() + + u_s_real = data["u_s_real"].to(device) + u_s_imag = data["u_s_imag"].to(device) + u_s = torch.stack([u_s_real, u_s_imag], dim=0) + targets = data["targets"].to(device) + + out_ = model(u_s) + out = out_['preds'] + loss = loss_func(out, targets) + loss.backward() + return loss + + optimizer.step(closure) + # for name, param in model[2].out.named_parameters(): + # print(f"Parameter name: {name}") + # print(f"Parameter value:\n{param.data.cpu().numpy()}\n") + + # 返回损失值 + return closure().item() + + +# 进行一次优化步骤 + + +# def validate_epoch_eit(model, metric_funcs, valid_loader, device): +# model.eval() + +# if isinstance(metric_funcs, Callable): +# metric_val = [] +# for _, data in enumerate(valid_loader): +# with torch.no_grad(): +# x, g = data["phi"].to(device), data["gradphi"].to(device) +# grid_c, grid = data['grid_c'].to(device), data['grid'].to(device) +# targets = data["targets"].to(device) +# out_ = model(x, g, pos=grid_c, grid=grid) +# preds = out_['preds'] +# targets = data["targets"].to(device) +# weights = torch.ones_like(targets) +# metric = metric_funcs(preds, targets, weights=weights) + +# metric = metric.cpu().numpy() +# metric_val.append(metric) +# return dict(metric=np.mean(metric_val, axis=0)) + +# elif isinstance(metric_funcs, Dict): +# metric_names = metric_funcs.keys() +# metric_vals = {m: [] for m in metric_names} + +# for _, data in enumerate(valid_loader): +# with torch.no_grad(): +# x, g = data["phi"].to(device), data["gradphi"].to(device) +# grid_c, grid = data['grid_c'].to(device), data['grid'].to(device) +# targets = data["targets"].to(device) +# out_ = model(x, g, pos=grid_c, grid=grid) +# preds = out_['preds'] +# targets = data["targets"].to(device) +# weights = torch.ones_like(targets) + +# for m in metric_names: +# metric_f = metric_funcs[m] +# metric = metric_f(preds, targets, weights=weights) +# metric = metric.cpu().numpy() +# metric_vals[m].append(metric) + +# for m in metric_names: +# metric_vals[m] = np.mean(metric_vals[m], axis=0) + +# return metric_vals + +def validate_epoch_ims_flag(model, metric_funcs, valid_loader, flag, device): + model.eval() + + if isinstance(metric_funcs, Callable): + metric_val = [] + for _, data in enumerate(valid_loader): + with torch.no_grad(): + x_real= data["u_s_real"].to(device) + x_imag= data["u_s_imag"].to(device) + x = torch.stack([x_real, x_imag], dim=0) + targets = data["targets"].to(device) + out_ = model(x) + preds = out_['preds'] + targets = data["targets"].to(device) + weights = torch.ones_like(targets) + metric = metric_funcs(preds, targets, weights=weights) + + metric = metric.cpu().numpy() + metric_val.append(metric) + return dict(metric=np.mean(metric_val, axis=0)) + + elif isinstance(metric_funcs, Dict): + metric_names = metric_funcs.keys() + metric_vals = {m: [] for m in metric_names} + for i, data in enumerate(valid_loader): + with torch.no_grad(): + x_real = data["u_s_real"].to(device) + x_imag = data["u_s_imag"].to(device) + x = torch.stack([x_real, x_imag], dim=0) + out_ = model(x) + preds = out_['preds'] + targets = data["targets"].to(device) + for j in range(100): + flag_ = flag[j+i*100] + with torch.no_grad(): + pred = preds[j, ...] + target = targets[j, ...] + flag_pred = [] + flag_target = [] + for row, col in flag_: + pred_ = pred[row, col] + target_ = target[row, col] + flag_pred.append(pred_) + flag_target.append(target_) + flag_pred = torch.tensor(flag_pred).unsqueeze(-1).unsqueeze(-1).unsqueeze(-1) + flag_target = torch.tensor(flag_target).unsqueeze(-1).unsqueeze(-1).unsqueeze(-1) + weights = torch.ones_like(flag_target) + + for m in metric_names: + metric_f = metric_funcs[m] + metric = metric_f(flag_pred, flag_target, weights=weights) + metric = metric.cpu().numpy() + metric_vals[m].append(metric) + + for m in metric_names: + print(np.argmin(metric_vals[m])) + metric_vals[m] = np.mean(metric_vals[m], axis=0) + + return metric_vals + +def validate_epoch_ims(model, metric_funcs, valid_loader, device): + model.eval() + + if isinstance(metric_funcs, Callable): + metric_val = [] + for _, data in enumerate(valid_loader): + with torch.no_grad(): + x_real= data["u_s_real"].to(device) + x_imag= data["u_s_imag"].to(device) + x = torch.stack([x_real, x_imag], dim=0) + targets = data["targets"].to(device) + out_ = model(x) + preds = out_['preds'] + targets = data["targets"].to(device) + weights = torch.ones_like(targets) + metric = metric_funcs(preds, targets, weights=weights) + + metric = metric.cpu().numpy() + metric_val.append(metric) + return dict(metric=np.mean(metric_val, axis=0)) + + elif isinstance(metric_funcs, Dict): + metric_names = metric_funcs.keys() + metric_vals = {m: [] for m in metric_names} + for _, data in enumerate(valid_loader): + with torch.no_grad(): + x_real = data["u_s_real"].to(device) + x_imag = data["u_s_imag"].to(device) + x = torch.stack([x_real, x_imag], dim=0) + targets = data["targets"].to(device) + out_ = model(x) + preds = out_['preds'] + targets = data["targets"].to(device) + weights = torch.ones_like(targets) + + for m in metric_names: + metric_f = metric_funcs[m] + metric = metric_f(preds, targets, weights=weights) + metric = metric.cpu().numpy() + metric_vals[m].append(metric) + + for m in metric_names: + metric_vals[m] = np.mean(metric_vals[m], axis=0) + + return metric_vals + +def run_train(model, loss_func, metric_func, + train_loader, valid_loader, + # optimizer, optimizer_, lr_scheduler, lr_scheduler_, + optimizer, lr_scheduler, + train_batch=train_batch_ims, + validate_epoch=validate_epoch_ims, + epochs=10, + visualization_path_training='./', + visualization_path_validation='./', + visualization_path_parameter='./', + device="cuda", + mode='min', + tqdm_mode='batch', + patience=10, + grad_clip=0.999, + start_epoch: int = 0, + model_save_path=MODEL_PATH, + save_mode='state_dict', + model_name='model.pt', + result_name='result.pkl'): + loss_train = [] + loss_val = [] + loss_epoch = [] + lr_history = [] + # parameter_writers = {} + it = 0 + + if patience is None or patience == 0: + patience = epochs + start_epoch = start_epoch + end_epoch = start_epoch + epochs + best_val_metric = -np.inf if mode == 'max' else np.inf + best_val_epoch = None + save_mode = 'state_dict' if save_mode is None else save_mode + stop_counter = 0 + is_epoch_scheduler = any(s in str(lr_scheduler.__class__) + for s in EPOCH_SCHEDULERS) + tqdm_epoch = False if tqdm_mode == 'batch' else True + with tqdm(total=end_epoch-start_epoch, disable=not tqdm_epoch) as pbar_ep: + training_writer = SummaryWriter(log_dir=visualization_path_training) + validation_writer = SummaryWriter(log_dir=visualization_path_validation) + parameters_writer = SummaryWriter(log_dir=visualization_path_parameter) + global_step = 0 + epoch_step = 0 + for epoch in range(start_epoch, end_epoch): + # if epoch >= 0 and epoch < 10: + # if epoch > 0: + # with torch.no_grad(): + # lower_bound = max(-model[0].s, -0.999999) # 确保s_value + a >= 0 + # upper_bound = min(1 - model[0].s, 0.999999) # 确保s_value + a <= 1 + # a = random.uniform(lower_bound, upper_bound) + # new_s = model[0].s + a + # model[0].s.data = new_s + + + # lower_bounds = [max(-model[0].s[0], -0.999999), + # max(-model[0].s[1], -0.999999), + # max(-model[0].s[2], -0.999999)] + # max_lower_bound = max(lower_bounds) + # upper_bounds = [min(1 - model[0].s[0], 0.999999), + # min(1 - model[0].s[1], 0.999999), + # min(1 - model[0].s[2], 0.999999)] + # min_upper_bound = min(upper_bounds) + # a = random.uniform(max_lower_bound, min_upper_bound) + + # new_s_0 = model[0].s[0] + a + # new_s_1 = model[0].s[1] + a + # new_s_2 = model[0].s[2] + a + # model[0].s[0].data = new_s_0 + # model[0].s[1].data = new_s_1 + # model[0].s[2].data = new_s_2 + + # if epoch == 10: + # model[0].s[0].data = torch.tensor(1.0, device=device) + # model[0].s[1].data = torch.tensor(1.0, device=device) + # model[0].s[2].data = torch.tensor(1.0, device=device) + + model.train() + with tqdm(total=len(train_loader), disable=tqdm_epoch) as pbar_batch: + for batch in train_loader: + if is_epoch_scheduler: + loss = train_batch(model, loss_func, + batch, optimizer, + None, device, grad_clip=grad_clip) + else: + loss = train_batch(model, loss_func, + batch, + # optimizer, optimizer_, lr_scheduler, lr_scheduler_, + optimizer, lr_scheduler, + device, grad_clip=grad_clip) + + # training_writer.add_histogram(name, param, global_step=global_step) + # training_writer.add_histogram('s', param, global_step=global_step) + + for name, param in model.named_parameters(): + scalar_dict = {} + if '0.s' in name: + scalar_dict[name] = param + parameters_writer.add_scalars('s', scalar_dict, global_step=global_step) + loss = np.array(loss) + training_writer.add_scalar('train', loss, global_step=global_step) + global_step +=1 + + loss_epoch.append(loss) + it += 1 + lr = optimizer.param_groups[0]['lr'] + lr_history.append(lr) + desc = f"epoch: [{epoch+1}/{end_epoch}]" + if loss.ndim == 0: # 1 target loss + _loss_mean = np.mean(loss_epoch) + desc += f" loss: {_loss_mean:.3e}" + else: + _loss_mean = np.mean(loss_epoch, axis=0) + for j in range(len(_loss_mean)): + if _loss_mean[j] > 0: + desc += f" | loss {j}: {_loss_mean[j]:.3e}" + desc += f" | current lr: {lr:.3e}" + pbar_batch.set_description(desc) + pbar_batch.update() + + loss_train.append(_loss_mean) + # loss_train.append(loss_epoch) + loss_epoch = [] + + val_result = validate_epoch( + model, metric_func, valid_loader, device) + validation_writer.add_scalar('validate', val_result["metric"], global_step=epoch_step) + epoch_step +=1 + loss_val.append(val_result["metric"]) + val_metric = val_result["metric"].sum() + + if mode == 'max': + saving_criterion = (val_metric > best_val_metric) + elif mode == 'min': + saving_criterion = (val_metric < best_val_metric) + + if saving_criterion: + best_val_epoch = epoch + best_val_metric = val_metric + stop_counter = 0 + if save_mode == 'state_dict': + torch.save(model.state_dict(), os.path.join( + model_save_path, model_name)) + else: + torch.save(model, os.path.join( + model_save_path, model_name)) + best_model_state_dict = { + k: v.to('cpu') for k, v in model.state_dict().items()} + best_model_state_dict = OrderedDict(best_model_state_dict) + + else: + stop_counter += 1 + + if lr_scheduler and is_epoch_scheduler: + if 'ReduceLROnPlateau' in str(lr_scheduler.__class__): + lr_scheduler.step(val_metric) + else: + lr_scheduler.step() + + if stop_counter > patience: + print(f"Early stop at epoch {epoch}") + break + if val_result["metric"].ndim == 0: + desc = color( + f"| val metric: {val_metric:.3e} ", color=Colors.blue) + else: + desc = color(f"| ", color=Colors.blue) + for i, _m in enumerate(val_result["metric"]): + desc += color(f"val metric {i+1}: {_m:.3e} ", + color=Colors.blue) + + desc += color( + f"| best val: {best_val_metric:.3e} at epoch {best_val_epoch+1}", color=Colors.yellow) + desc += color(f" | early stop: {stop_counter} ", color=Colors.red) + desc += color(f" | current lr: {lr:.3e}", color=Colors.magenta) + if not tqdm_epoch: + tqdm.write("\n"+desc+"\n") + else: + desc_ep = color("", color=Colors.green) + if _loss_mean.ndim == 0: # 1 target loss + desc_ep += color(f"| loss: {_loss_mean:.3e} ", + color=Colors.green) + else: + for j in range(len(_loss_mean)): + if _loss_mean[j] > 0: + desc_ep += color( + f"| loss {j}: {_loss_mean[j]:.3e} ", color=Colors.green) + desc_ep += desc + pbar_ep.set_description(desc_ep) + pbar_ep.update() + # if epoch >= 0 and epoch < 10: + # if epoch > 0: + # with torch.no_grad(): + # new_s_ = model[0].s - a + # model[0].s.data = new_s_ + + # new_s_0_ = model[0].s[0] - a + # new_s_1_ = model[0].s[1] - a + # new_s_2_ = model[0].s[2] - a + # model[0].s[0].data = new_s_0_ + # model[0].s[1].data = new_s_1_ + # model[0].s[2].data = new_s_2_ + + # model[0].s =nn.Parameter(new_s_, requires_grad=True) + + result = dict( + best_val_epoch=best_val_epoch, + best_val_metric=best_val_metric, + loss_train=np.asarray(loss_train), + loss_val=np.asarray(loss_val), + lr_history=np.asarray(lr_history), + # best_model=best_model_state_dict, + optimizer_state=optimizer.state_dict() + ) + save_pickle(result, os.path.join(model_save_path, result_name)) + return result + +class UnitGaussianNormalizer: + def __init__(self, eps=1e-5): + super().__init__() + ''' + modified from utils3.py in + https://github.com/zongyi-li/fourier_neural_operator + Changes: + - .to() has a return to polymorph the torch behavior + - naming convention changed to sklearn scalers + ''' + self.eps = eps + + def fit_transform(self, x): + self.mean = x.mean(0) + self.std = x.std(0) + return (x - self.mean) / (self.std + self.eps) + + def transform(self, x): + return (x - self.mean) / (self.std + self.eps) + + def inverse_transform(self, x): + return (x * (self.std + self.eps)) + self.mean + + def to(self, device): + if torch.is_tensor(self.mean): + self.mean = self.mean.float().to(device) + self.std = self.std.float().to(device) + else: + self.mean = torch.from_numpy(self.mean).float().to(device) + self.std = torch.from_numpy(self.std).float().to(device) + return self + + def cuda(self, device=None): + assert torch.is_tensor(self.mean) + self.mean = self.mean.float().cuda(device) + self.std = self.std.float().cuda(device) + return self + + def cpu(self): + assert torch.is_tensor(self.mean) + self.mean = self.mean.float().cpu() + self.std = self.std.float().cpu() + return self + + +class EITDataset(Dataset): + def __init__(self, + data_path=DATA_PATH, + file_type='h5', + phi_name=PHI_NAME, + coeff_name=COEFF_NAME, + part_idx: list = [4], + index_channel = None, + shuffle_channel=False, + normalizer_x=None, + normalization=False, + subsample: int = 1, + subsample_attn: int = 4, + subsample_method='nearest', + channel=5, + train_data=True, + train_len=0.8, + valid_len=0.2, + debug=False, + online_grad=True, + return_grad=True, + return_boundary=True, + random_state=1127802): + ''' + EIT data from Fan-Bohorquez-Ying 2019, Guo-Jiang 2020 + ''' + assert max(part_idx) <= 6 + self.data_path = data_path + self.file_type = file_type + self.phi_name = phi_name + self.coeff_name = coeff_name + self.part_idx = part_idx + self.parts = ['part'+str(i) for i in part_idx] + self.shuffle_channel = shuffle_channel + self.index_channel = index_channel + self.n_grid_fine = 64 # finest resolution along x-, y- dim + self.subsample_attn = subsample_attn # subsampling for attn + self.subsample = subsample # subsampling for input and output + self.subsample_method = subsample_method # 'interp' or 'nearest' + self.channel = channel + self.n_grid = int(((self.n_grid_fine - 1)/self.subsample) + 1) + self.n_grid_coarse = int( + ((self.n_grid_fine - 1)/self.subsample_attn) + 1) + self.h = 1/self.n_grid_fine # grad 1st then subsample + self.train_data = train_data + self.train_len = train_len + self.valid_len = valid_len + self.normalization = normalization + self.normalizer_x = normalizer_x + self.random_state = random_state + self.return_grad = return_grad + self.online_grad = online_grad + self.return_boundary = return_boundary + self.eps = 1e-8 + self.debug = debug + if self.data_path is not None: + self._initialize() + + def __len__(self): + return self.n_samples + + def _initialize(self): + get_seed(self.random_state, printout=False) + with timer(f"Loading parts {'+'.join([str(i) for i in self.part_idx]) }"): + self._get_files() + phi, a = self.load_example() # (N, n, n) and (N, C, n, n) + gc.collect() + + self.n_samples = len(a) + + self.phi, self.gradphi, self.targets = self.preprocess(phi, a) + + self.grid_c = self.get_grid(self.n_grid_coarse) + self.grid = self.get_grid(self.n_grid_fine, + subsample=self.subsample, + return_boundary=self.return_boundary) + + if self.train_data and self.normalization: + self.normalizer_x = UnitGaussianNormalizer() + self.normalizer_y = UnitGaussianNormalizer() + self.phi = self.normalizer_x.fit_transform(self.phi) + + if self.return_boundary: + _ = self.normalizer_y.fit_transform(x=self.targets) + else: + _ = self.normalizer_y.fit_transform( + x=self.targets[:, 1:-1, 1:-1, :]) + elif self.normalization: + self.phi = self.normalizer_x.transform(self.phi) + + def _get_files(self): + data_files = find_files(self.phi_name, path=self.data_path) + target_files = find_files(self.coeff_name, path=self.data_path) + t = '.'+self.file_type + data_file = [] + target_file = [] + for p in self.parts: + data_file += [f for f in data_files if p in f and t in f] + target_file += [f for f in target_files if p in f and t in f] + self.data_file = sorted(data_file) + self.target_file = sorted(target_file) + + def load_example(self): + '''load example in mat files''' + data_file, target_file = self.data_file, self.target_file + + a = [] + for f in target_file: + if self.file_type == 'mat': + a_ = loadmat(f) + n = self.n_grid_fine + assert a_["I_true"].shape[-1] == n**2 + a_ = a_["I_true"].reshape(-1, n, n) + + elif self.file_type == 'h5': + a_ = h5py.File(f, mode="r") + a_ = a_.get('I_true') + # a.append(a_[()]) + a.append(a_[()].transpose(0, 2, 1)) + + a = np.concatenate(a, axis=0) + + if self.debug: + data_len = int(0.1*len(a)) + a = a[:data_len] + else: + data_len = self.get_data_len(len(a)) + if self.train_data: + a = a[:data_len] + else: + a = a[-data_len:] + + u = [] + for _, d in enumerate(data_file): + u_ = h5py.File(d, mode="r") + key = list(u_.keys())[0] + u_ = u_.get(key) + u.append(u_[()]) + + if self.shuffle_channel and self.index_channel is None: + self.index_channel = np.random.randint(u[0].shape[1], size=self.channel) + elif not self.shuffle_channel: + self.index_channel = np.arange(self.channel) + u = np.concatenate([x[:, self.index_channel, ...] for x in u], axis=0) + + if self.train_data: + u = u[:data_len] + else: + u = u[-data_len:] + + return u, a + + def get_data_len(self, len_data): + if self.train_data: + if self.train_len <= 1: + train_len = int(self.train_len*len_data) + elif 1 < self.train_len < len_data: + train_len = self.train_len + else: + train_len = int(0.8*len_data) + return train_len + else: + if self.valid_len <= 1: + valid_len = int(self.valid_len*len_data) + elif 1 < self.valid_len < len_data: + valid_len = self.valid_len + else: + valid_len = int(0.2*len_data) + return valid_len + + def preprocess(self, u, a): + # input is (N, C, 201, 201) + bsz = a.shape[0] + n_grid_fine = self.n_grid_fine + s = self.subsample + h = self.h + n = self.n_grid + + if s > 1 and self.subsample_method == 'nearest': + a = a[:, ::s, ::s] + elif s > 1 and self.subsample_method in ['interp', 'linear', 'average']: + a = pooling_2d(a, + kernel_size=(s, s), + padding=True) + a = a.reshape(bsz, n, n, 1) + + if self.return_grad and not self.online_grad: + gradu = self.get_grad(u, h) # u is (N, C, n, n) + gradu = gradu[..., ::s, ::s] + gradu = gradu.transpose((0, 2, 3, 1)) # (N, n, n, C) + else: + gradu = np.zeros((bsz, )) # placeholder + + u = u[..., ::s, ::s] + u = u.transpose((0, 2, 3, 1)) # (N, n, n, C) + return u, gradu, a + + @staticmethod + def get_grad(f, h): + ''' + h: mesh size + n: grid size + separate input for online grad generating + input f: (N, C, n, n) + ''' + bsz, N_C = f.shape[:2] + # n = int(((n - 1)/s) + 1) + + fx, fy = [], [] + for i in range(N_C): + '''smaller mem footprint''' + _fx, _fy = EITDataset.central_diff(f[:, i], h) + fx.append(_fx) + fy.append(_fy) + gradf = np.stack(fx+fy, axis=1) # (N, 2*C, n, n) + return gradf + + @staticmethod + def central_diff(f, h, mode='constant', padding=True): + """ + mode: see + https://numpy.org/doc/stable/reference/generated/numpy.pad.html + """ + # x: (batch, n, n) + # b = x.shape[0] + if padding: + f = np.pad(f, ((0, 0), (1, 1), (1, 1)), + mode=mode, constant_values=0) + d, s = 2, 1 # dilation and stride + grad_x = (f[:, d:, s:-s] - f[:, :-d, s:-s]) / \ + d # (N, S_x, S_y) + grad_y = (f[:, s:-s, d:] - f[:, s:-s, :-d]) / \ + d # (N, S_x, S_y) + + return grad_x/h, grad_y/h + + @staticmethod + def get_grid(n_grid, subsample=1, return_boundary=True): + x = np.linspace(0, 1, n_grid) + y = np.linspace(0, 1, n_grid) + x, y = np.meshgrid(x, y) + s = subsample + + if return_boundary: + x = x[::s, ::s] + y = y[::s, ::s] + else: + x = x[::s, ::s][1:-1, 1:-1] + y = y[::s, ::s][1:-1, 1:-1] + grid = np.stack([x, y], axis=-1) + # grid is DoF, excluding boundary (n, n, 2), or (n-2, n-2, 2) + return grid + + def __getitem__(self, index): + ''' + Outputs: + - grid: x-, y- coords + - grid_c: coarse grid + - a: diffusion coeff + - u: solution + - gradu: gradient of solution + ''' + pos_dim = 2 + # uniform grid for all samples (n_s*n_s, 2) + grid_c = self.grid_c.reshape(-1, pos_dim) + # uniform grid fine for all samples (n, n, 2) + if self.subsample_attn is None: + grid_c = torch.tensor([1.0]) # place holder + else: + grid_c = torch.from_numpy(grid_c) # (n_s*n_s, 2) + + grid = torch.from_numpy(self.grid) # fine grid (n, n, 2) + + phi_ = self.phi[index] + phi = torch.from_numpy(phi_) + targets = torch.from_numpy(self.targets[index]) + + if self.return_grad and self.online_grad: + phi_ = phi_[None, ...].transpose(0, 3, 1, 2) + # phi_ (1, C, n, n) + gradphi = self.get_grad(phi_, self.h) + gradphi = gradphi.squeeze().transpose(1, 2, 0) #(n, n, 2*C) + gradphi = torch.from_numpy(gradphi) + elif self.return_grad: + gradphi = torch.from_numpy(self.gradphi[index]) + else: + gradphi = torch.tensor(float('nan')) + + return dict(phi=phi.float(), + gradphi=gradphi.float(), + grid_c=grid_c.float(), + grid=grid.float(), + targets=targets.float(), + ) +################################################################################################################# + +if __name__ == '__main__': + dataset = EITDataset(part_idx=[1, 2], + file_type='h5', + subsample=1, + channel=3, + return_grad=True, + online_grad=False, + train_data=True, + debug=False) + loader = DataLoader(dataset, + batch_size=10, + shuffle=True, + drop_last=True, + pin_memory=True) + sample = next(iter(loader)) + phi = sample['phi'] + gradphi = sample['gradphi'] + targets = sample['targets'] + print(phi.size(), gradphi.size(), targets.size()) diff --git a/app/lafem-ims/lafemims/model/unet.py b/app/lafem-ims/lafemims/model/unet.py new file mode 100644 index 000000000..7ba8298de --- /dev/null +++ b/app/lafem-ims/lafemims/model/unet.py @@ -0,0 +1,196 @@ +""" Parts of the U-Net model and basic block for ResNet +https://github.com/milesial/Pytorch-UNet +https://github.com/pytorch/vision/blob/main/torchvision/models/resnet.py +https://github.com/Dootmaan/MT-UNet +""" + +import torch +import torch.nn as nn +import torch.nn.functional as F +import copy +from einops import rearrange +from torch.nn.init import xavier_uniform_, constant_, xavier_normal_, zeros_ + +class Identity(nn.Module): + ''' + a placeholder layer similar to tensorflow.no_op(): + https://github.com/pytorch/pytorch/issues/9160#issuecomment-483048684 + not used anymore as + https://pytorch.org/docs/stable/generated/torch.nn.Identity.html + edge and grid are dummy inputs + ''' + + def __init__(self, in_features=None, out_features=None, + *args, **kwargs): + super(Identity, self).__init__() + + if in_features is not None and out_features is not None: + self.id = nn.Linear(in_features, out_features) + else: + self.id = nn.Identity() + + def forward(self, x, edge=None, grid=None): + return self.id(x) + +class LayerNorm2d(nn.Module): + """ + a wrapper for GroupNorm + https://pytorch.org/docs/stable/generated/torch.nn.GroupNorm.html + input and output shapes: (bsz, C, H, W) + """ + def __init__(self, num_channels, + eps=1e-06, + elementwise_affine=True, + device=None, + dtype=None) -> None: + super(LayerNorm2d, self).__init__() + self.norm = nn.GroupNorm(1, num_channels, + eps=eps, + affine=elementwise_affine, + device=device, + dtype=dtype) + def forward(self, x): + return self.norm(x) + + +class DoubleConv(nn.Module): + """(convolution => BN or LN=> ReLU) * 2""" + + def __init__(self, in_channels, + out_channels, + mid_channels=None, + activation='relu', + batch_norm=True): + super().__init__() + if not mid_channels: + mid_channels = out_channels + + if batch_norm: + self.norm1 = nn.BatchNorm2d(mid_channels) + self.norm2 = nn.BatchNorm2d(out_channels) + else: + self.norm1 = LayerNorm2d(mid_channels) + self.norm2 = LayerNorm2d(out_channels) + + if activation == 'silu': + self.activation1 = nn.SiLU() + self.activation2 = nn.SiLU() + elif activation == 'gelu': + self.activation1 = nn.GELU() + self.activation2 = nn.GELU() + else: + self.activation1 = nn.ReLU() + self.activation2 = nn.ReLU() + + self.conv1 = nn.Conv2d(in_channels, mid_channels, + kernel_size=3, padding=1, bias=False) + self.conv2 = nn.Conv2d(mid_channels, out_channels, + kernel_size=3, padding=1, bias=False) + + def forward(self, x): + x = self.conv1(x) + x = self.norm1(x) + x = self.activation1(x) + x = self.conv2(x) + x = self.norm2(x) + return self.activation2(x) + + +class depthwise_separable_conv(nn.Module): + def __init__(self, in_ch, out_ch, stride=1, kernel_size=3, padding=1, bias=False, xavier_init=1e-2): + super().__init__() + self.depthwise = nn.Conv2d(in_ch, in_ch, kernel_size=kernel_size, + padding=padding, groups=in_ch, bias=bias, stride=stride) + self.pointwise = nn.Conv2d(in_ch, out_ch, kernel_size=1, bias=bias) + self.xavier_init = xavier_init + self._reset_parameters() + + def forward(self, x): + out = self.depthwise(x) + out = self.pointwise(out) + + return out + + def _reset_parameters(self): + for layer in [self.depthwise, self.pointwise]: + for param in layer.parameters(): + if param.ndim > 1: + xavier_uniform_(param, gain=self.xavier_init) + else: + constant_(param, 0) + +class UNet(nn.Module): + def __init__(self, in_channels, + out_channels, + dim=64, + encoder_layers=5, + decoder_layers=None, + scale_factor=2, + input_size=(224,224), + return_latent=False, + debug=False, + **kwargs): + '''new implementation for eit-transformer paper''' + super(UNet, self).__init__() + self.layers = decoder_layers if decoder_layers else encoder_layers + enc_dim = [dim*2**(k-1) for k in range(1, encoder_layers+1)] + if decoder_layers: + dec_dim = [dim*2**(k-1) for k in range(decoder_layers, 0, -1)] + else: + dec_dim = enc_dim[::-1] + enc_dim = [in_channels] + enc_dim + self.encoder = nn.ModuleList( + [DoubleConv(enc_dim[i], enc_dim[i+1]) for i in range(encoder_layers)]) + self.pool = nn.MaxPool2d(scale_factor) + + self.up_blocks = nn.ModuleList([nn.ConvTranspose2d(dec_dim[i], dec_dim[i+1], scale_factor, scale_factor) for i in range(encoder_layers-1)]) + self.decoder = nn.ModuleList([DoubleConv(dec_dim[i], dec_dim[i+1]) for i in range(len(dec_dim)-1)]) + self.outconv = nn.Conv2d(dec_dim[-1], out_channels, 1) + + self.input_size = input_size + self.return_latent = return_latent + self.debug = debug + + # def forward(self, x, xp=None, pos=None, grid=None): # 101x101x20 + def forward(self, x, pos=None, grid=None): + + # if xp.ndim == x.ndim: + # x = torch.cat([x, xp], dim=-1) + x = x.permute(0, 3, 1, 2) + + if self.input_size: + _, _, *size = x.size() + x = F.interpolate(x, size=self.input_size, + mode='bilinear', + align_corners=True) + + latent_enc = [] + for i, enc in enumerate(self.encoder): + x = enc(x) + latent_enc.append(x) + if i < self.layers-1: + x = self.pool(x) + + latent_enc = latent_enc[::-1][1:] + + if self.debug: + for i, z in enumerate(latent_enc): + print(f"{i+1}-th latent from encoder: \t {z.size()}") + + for i, (up, dec) in enumerate(zip(self.up_blocks, self.decoder)): + x = up(x) + x = torch.cat([x, latent_enc[i]], dim=1) + x = dec(x) + + x = self.outconv(x) + + if self.input_size: + x = F.interpolate(x, size=size, + mode='bilinear', + align_corners=True) + + if self.return_latent: + return dict(preds=x.permute(0, 2, 3, 1), + preds_latent=[z.permute(0, 2, 3, 1) for z in latent_enc]) + else: + return dict(preds=x.permute(0, 2, 3, 1)) diff --git a/app/lafem-ims/lafemims/model/ut.py b/app/lafem-ims/lafemims/model/ut.py new file mode 100644 index 000000000..ba12bb6e3 --- /dev/null +++ b/app/lafem-ims/lafemims/model/ut.py @@ -0,0 +1,912 @@ +import torch +from torch import nn, Tensor +import torch.nn.functional as F +import numpy as np +import math +from .unet import * +from scipy.special import hankel1 +from .eit import UnitGaussianNormalizer + + +class OutConv(nn.Module): + def __init__(self, in_channels, out_channels): + super(OutConv, self).__init__() + self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=1) + + def forward(self, x): + return self.conv(x) + + +class EuclideanPositionEncoding(nn.Module): + def __init__(self, dmodel, + coords_dim=2, + trainable=False, + debug=False): + super(EuclideanPositionEncoding, self).__init__() + """ + channel expansion for input + """ + self.pos_proj = nn.Conv2d(coords_dim, dmodel, kernel_size=1) + self.trainable = trainable + self.debug = debug + + def _get_position(self, x): + bsz, _, h, w = x.size() # x is bsz, channel, h, w + grid_x, grid_y = torch.linspace(0, 1, h), torch.linspace(0, 1, w) + mesh = torch.stack(torch.meshgrid( + [grid_x, grid_y], indexing='ij'), dim=0) + return mesh + + def forward(self, x): + pos = self._get_position(x).to(x.device) + pos = pos.unsqueeze(0).repeat(x.size(0), 1, 1, 1) + pos = self.pos_proj(pos) + + x = x + pos + return x + +class PositionalEncoding2D(nn.Module): + def __init__(self, channels, + channel_last=False, + trainable=False, + pos_cache=None, + debug=False): + """ + modified from https://github.com/tatp22/multidim-positional-encoding + :param channels: The last dimension of the tensor you want to apply pos emb to. + """ + super(PositionalEncoding2D, self).__init__() + channels = int(math.ceil(channels / 2)) + self.channels = channels + inv_freq = 1. / (10000 + ** (torch.arange(0, channels, 2).float() / channels)) + self.register_buffer('inv_freq', inv_freq) + self.channel_last = channel_last + self.trainable = trainable + self.debug = debug + self.pos_cache = pos_cache + + def forward(self, x): + """ + :param x: A 4d tensor of size (batch_size, C, h, w) + :return: Positional Encoding Matrix of size (batch_size, C, x, y) + """ + if self.channel_last: + x = x.permute(0, 3, 1, 2) + + if self.pos_cache is not None and self.pos_cache.shape == x.shape: + return self.pos_cache + x + + bsz, n_channel, h, w = x.shape + pos_x = torch.arange(h, device=x.device, dtype=self.inv_freq.dtype) + pos_y = torch.arange(w, device=x.device, dtype=self.inv_freq.dtype) + sin_inp_x = torch.einsum("i,j->ij", self.inv_freq, pos_x) + sin_inp_y = torch.einsum("i,j->ij", self.inv_freq, pos_y) + + emb_x = torch.cat((sin_inp_x.sin(), sin_inp_x.cos()), + dim=0).unsqueeze(-1) + emb_y = torch.cat((sin_inp_y.sin(), sin_inp_y.cos()), + dim=0).unsqueeze(-2) + + emb = torch.zeros((self.channels * 2, h, w), + device=x.device, dtype=x.dtype) + emb[:self.channels, ...] = emb_x + emb[self.channels:2 * self.channels, ...] = emb_y + + emb = emb[:n_channel, ...].unsqueeze(0).repeat(bsz, 1, 1, 1) + + if self.channel_last: + emb = emb.permute(0, 2, 3, 1) + + self.pos_cache = emb + return self.pos_cache + x + + +class Attention(nn.Module): + def __init__(self, dim, + heads=4, + dim_head=None, + attn_drop=0., + proj_drop=0., + bias=False, + norm_type='layer', + skip_connection=True, + return_attention=False, + softmax=True, + sinosoidal_pe=False, + pe_trainable=False, + debug=False): + super(Attention, self).__init__() + + self.heads = heads + self.dim_head = dim // heads * 2 if dim_head is None else dim_head + self.inner_dim = self.dim_head * heads # like dim_feedforward + self.attn_factor = self.dim_head ** (-0.5) + self.bias = bias + self.softmax = softmax + self.skip_connection = skip_connection + self.return_attention = return_attention + self.debug = debug + + self.to_qkv = depthwise_separable_conv( + dim, self.inner_dim*3, bias=self.bias) + self.to_out = depthwise_separable_conv( + self.inner_dim, dim, bias=self.bias) + + self.attn_drop = nn.Dropout(attn_drop) + self.proj_drop = nn.Dropout(proj_drop) + + PE = PositionalEncoding2D if sinosoidal_pe else EuclideanPositionEncoding + self.pe = PE(dim, trainable=pe_trainable) + self.norm_type = norm_type + self.norm_q = self._get_norm(self.dim_head, self.heads, + eps=1e-6) + + self.norm_k = self._get_norm(self.dim_head, self.heads, + eps=1e-6) + + self.norm_out = self._get_norm(self.dim_head, self.heads, + eps=1e-6) + self.norm = LayerNorm2d(dim, eps=1e-6) + + def _get_norm(self, dim, n_head, **kwargs): + if self.norm_type == 'layer': + norm = nn.LayerNorm + elif self.norm_type == "batch": + norm = nn.BatchNorm1d + elif self.norm_type == "instance": + norm = nn.InstanceNorm1d + else: + norm = Identity + return nn.ModuleList( + [copy.deepcopy(norm(dim, **kwargs)) for _ in range(n_head)]) + + def forward(self, x): + + _, _, h, w = x.size() + x = self.pe(x) + + #B, inner_dim, H, W + qkv = self.to_qkv(x) + q, k, v = qkv.chunk(3, dim=1) + + q, k, v = map(lambda t: rearrange(t, 'b (dim_head heads) h w -> b heads (h w) dim_head', + dim_head=self.dim_head, + heads=self.heads, + h=h, w=w), (q, k, v)) + + q = torch.stack( + [norm(x) for norm, x in + zip(self.norm_q, (q[:, i, ...] for i in range(self.heads)))], dim=1) + k = torch.stack( + [norm(x) for norm, x in + zip(self.norm_k, (k[:, i, ...] for i in range(self.heads)))], dim=1) + + q_k_attn = torch.einsum('bhid,bhjd->bhij', q, k) + + q_k_attn *= self.attn_factor + if self.softmax: + q_k_attn = F.softmax(q_k_attn, dim=-1) + else: + q_k_attn /= (h*w) + + q_k_attn = self.attn_drop(q_k_attn) + + out = torch.einsum('bhij,bhjd->bhid', q_k_attn, v) + + if self.skip_connection: + out = out + v + + out = torch.stack( + [norm(x) for norm, x in + zip(self.norm_out, (out[:, i, ...] for i in range(self.heads)))], dim=1) + + out = rearrange(out, 'b heads (h w) dim_head -> b (dim_head heads) h w', + h=h, w=w, dim_head=self.dim_head, heads=self.heads) + + out = self.to_out(out) + + out = self.proj_drop(out) + + out = self.norm(out) + + if self.return_attention: + return out, q_k_attn + else: + return out + + +class CrossConv(nn.Module): + def __init__(self, dim, dim_c, + scale_factor=2): + super(CrossConv, self).__init__() + + self.dim = dim # dim = C + self.dim_c = dim_c # dim_c = 2*C + self.convt = nn.ConvTranspose2d( + dim_c, dim, scale_factor, stride=scale_factor) + + def forward(self, xf, xc): + x = self.convt(xc) + x = torch.cat([xf, x], dim=1) + return x + + +class CrossAttention(nn.Module): + def __init__(self, dim, + dim_c, + scale_factor=[2, 2], + heads=4, + dim_head=64, + attn_drop=0.1, + proj_drop=0.1, + skip_connection=False, + hadamard=False, + softmax=True, + pe_trainable=False, + sinosoidal_pe=False, + bias=False, + return_attn=False, + debug=False): + super(CrossAttention, self).__init__() + + self.heads = heads + self.dim_head = dim // heads * 2 if dim_head is None else dim_head + self.inner_dim = self.dim_head * heads # like dim_feedforward + self.c2f_factor = scale_factor + self.f2c_factor = [1/s for s in scale_factor] + self.attn_factor = self.dim_head ** (-0.5) + self.bias = bias + self.hadamard = hadamard + self.softmax = softmax + self.skip_connection = skip_connection + self.return_attn = return_attn + self.debug = debug + self.dim = dim + self.dim_c = dim_c + + self.q_proj = depthwise_separable_conv( + self.dim_c, self.inner_dim, bias=self.bias) + self.k_proj = depthwise_separable_conv( + self.dim_c, self.inner_dim, bias=self.bias) + self.v_proj = depthwise_separable_conv( + self.dim, self.inner_dim, bias=self.bias) + self.out_proj = depthwise_separable_conv( + self.inner_dim, self.dim, bias=self.bias) + self.skip_proj = depthwise_separable_conv( + self.dim_c, self.dim, bias=self.bias) + + self.attn_drop = nn.Dropout(attn_drop) + self.proj_drop = nn.Dropout(proj_drop) + + PE = PositionalEncoding2D if sinosoidal_pe else EuclideanPositionEncoding + self.pe = PE(self.dim, trainable=pe_trainable) + self.pe_c = PE(self.dim_c, trainable=pe_trainable) + self.norm_k = self._get_norm(self.dim_head, self.heads, eps=1e-6) + self.norm_q = self._get_norm(self.dim_head, self.heads, eps=1e-6) + self.norm_out = LayerNorm2d(2*self.dim, eps=1e-6) + + def _get_norm(self, dim, n_head, norm=None, **kwargs): + norm = nn.LayerNorm if norm is None else norm + return nn.ModuleList( + [copy.deepcopy(norm(dim, **kwargs)) for _ in range(n_head)]) + + def forward(self, xf, xc): + + _, _, hf, wf = xf.size() + xf = self.pe(xf) + + _, _, ha, wa = xc.size() + xc = self.pe_c(xc) + + #B, inner_dim, H, W + q = self.q_proj(xc) + k = self.k_proj(xc) + v = self.v_proj(xf) + + res = self.skip_proj(xc) + res = F.interpolate(res, scale_factor=self.c2f_factor, + mode='bilinear', + align_corners=True, + recompute_scale_factor=True) + + q, k = map(lambda t: rearrange(t, 'b (dim_head heads) h w -> b heads (h w) dim_head', + dim_head=self.dim_head, heads=self.heads, h=ha, w=wa), (q, k)) + + v = F.interpolate(v, scale_factor=self.f2c_factor, + mode='bilinear', + align_corners=True, + recompute_scale_factor=True) + v = rearrange(v, 'b (dim_head heads) h w -> b heads (h w) dim_head', + dim_head=self.dim_head, heads=self.heads, h=ha, w=wa) + + q = torch.stack( + [norm(x) for norm, x in + zip(self.norm_q, (q[:, i, ...] for i in range(self.heads)))], dim=1) + k = torch.stack( + [norm(x) for norm, x in + zip(self.norm_k, (k[:, i, ...] for i in range(self.heads)))], dim=1) + + q_k_attn = torch.einsum('bhid,bhjd->bhij', q, k) + + q_k_attn *= self.attn_factor + + if self.softmax: + q_k_attn = F.softmax(q_k_attn, dim=-1) + else: + q_k_attn /= (ha*wa) + q_k_attn = self.attn_drop(q_k_attn) + + out = torch.einsum('bhij,bhjd->bhid', q_k_attn, v) + out = rearrange(out, 'b heads (h w) dim_head -> b (dim_head heads) h w', + h=ha, w=wa, dim_head=self.dim_head, heads=self.heads) + + out = self.out_proj(out) + out = self.proj_drop(out) + + out = F.interpolate(out, scale_factor=self.c2f_factor, + mode='bilinear', + align_corners=True, + recompute_scale_factor=True) + + if self.hadamard: + out = torch.sigmoid(out) + out = out*xf + + if self.skip_connection: + out = out+xf + + out = torch.cat([out, res], dim=1) + + out = self.norm_out(out) + + if self.return_attn: + return out, q_k_attn + else: + return out + + +class DownBlock(nn.Module): + """Downscaling with interp then double conv""" + + def __init__(self, in_channels, + out_channels, + scale_factor=[0.5, 0.5], + batch_norm=True, + activation='relu'): + super().__init__() + self.scale_factor = scale_factor + self.conv = DoubleConv(in_channels, out_channels, + batch_norm=batch_norm, + activation=activation) + + def forward(self, x): + x = F.interpolate(x, scale_factor=self.scale_factor, + mode='bilinear', + align_corners=True, + recompute_scale_factor=True) + return self.conv(x) + + +class UpBlock(nn.Module): + def __init__(self, nc_coarse, nc_fine, + heads=4, + activation='relu', + hadamard=False, + attention=True, + softmax=True, + skip_connection=False, + sinosoidal_pe=False, + pe_trainable=False, + batch_norm=False, + debug=False): + super(UpBlock, self).__init__() + if attention: + self.up = CrossAttention(nc_fine, + nc_coarse, + heads=heads, + dim_head=nc_coarse//2, + skip_connection=skip_connection, + hadamard=hadamard, + softmax=softmax, + sinosoidal_pe=sinosoidal_pe, + pe_trainable=pe_trainable) + else: + self.up = CrossConv(nc_fine, nc_coarse) + + self.conv = DoubleConv(nc_coarse, nc_fine, + batch_norm=batch_norm, + activation=activation) + self.debug = debug + + def forward(self, xf, xc): + x = self.up(xf, xc) + x = self.conv(x) + return x + + +class UTransformer(nn.Module): + def __init__(self, in_channels, + out_channels, + dim=64, + heads=4, + input_size=(224, 224), + activation='gelu', + attention_coarse=True, + attention_up=True, + batch_norm=False, + attn_norm_type='layer', + skip_connection=True, + softmax=True, + pe_trainable=False, + hadamard=False, + sinosoidal_pe=False, + add_grad_channel=True, + out_latent=False, + debug=False, + **kwargs): + super(UTransformer, self).__init__() + + self.inc = DoubleConv(in_channels, dim, + activation=activation, + batch_norm=batch_norm) + self.down1 = DownBlock(dim, 2*dim, + activation=activation, + batch_norm=batch_norm) + self.down2 = DownBlock(2*dim, 4*dim, + activation=activation, + batch_norm=batch_norm) + self.down3 = DownBlock(4*dim, 8*dim, + activation=activation, + batch_norm=batch_norm) + if attention_coarse: + self.up0 = Attention(8*dim, heads=heads, + softmax=softmax, + norm_type=attn_norm_type, + sinosoidal_pe=sinosoidal_pe, + pe_trainable=pe_trainable, + skip_connection=skip_connection) + else: + self.up0 = DoubleConv(8*dim, 8*dim, + activation=activation, + batch_norm=batch_norm) + self.up1 = UpBlock(8*dim, 4*dim, + heads=heads, + batch_norm=batch_norm, + hadamard=hadamard, + attention=attention_up, + softmax=softmax, + sinosoidal_pe=sinosoidal_pe, + pe_trainable=pe_trainable) + self.up2 = UpBlock(4*dim, 2*dim, + heads=heads, + batch_norm=batch_norm, + hadamard=hadamard, + attention=attention_up, + softmax=softmax, + sinosoidal_pe=sinosoidal_pe, + pe_trainable=pe_trainable) + self.up3 = UpBlock(2*dim, dim, + heads=heads, + batch_norm=batch_norm, + hadamard=hadamard, + attention=attention_up, + softmax=softmax, + sinosoidal_pe=sinosoidal_pe, + pe_trainable=pe_trainable,) + self.out = OutConv(dim, out_channels) + self.out_latent = out_latent + self.add_grad_channel = add_grad_channel + self.input_size = input_size + self.debug = debug + + # def forward(self, x, gradx, *args, **kwargs): + # "input dim: bsz, n, n, C" + # if gradx.ndim == x.ndim and self.add_grad_channel: + # x = torch.cat([x, gradx], dim=-1) + # x = x.permute(0, 3, 1, 2) + + def forward(self, x_gradx, *args, **kwargs): + + x_gradx = x_gradx.to(torch.float32) + x = x_gradx.permute(0, 3, 1, 2) + + if self.input_size: + _, _, *size = x.size() + x = F.interpolate(x, size=self.input_size, + mode='bilinear', + align_corners=True) + + x1 = self.inc(x) + + x2 = self.down1(x1) + + x3 = self.down2(x2) + + x4 = self.down3(x3) + + x4 = self.up0(x4) + + x = self.up1(x3, x4) + + x = self.up2(x2, x) + + x = self.up3(x1, x) + + out = self.out(x) + + if self.input_size: + out = F.interpolate(out, size=size, + mode='bilinear', + align_corners=True) + + out = out.permute(0, 2, 3, 1) + + if self.out_latent: + return dict(preds=out, + preds_latent=[x2, x3, x4]) + else: + return dict(preds=out) + +class MiniUTransformer(nn.Module): + def __init__(self, in_channels, + out_channels, + dim=64, + heads=4, + input_size=(224, 224), + activation='gelu', + attention_coarse=True, + attention_up=True, + batch_norm=False, + attn_norm_type='layer', + skip_connection=True, + softmax=True, + pe_trainable=False, + hadamard=False, + sinosoidal_pe=False, + add_grad_channel=True, + out_latent=False, + debug=False, + **kwargs): + super(MiniUTransformer, self).__init__() + + self.inc = DoubleConv(in_channels, dim, + activation=activation, + batch_norm=batch_norm) + self.down1 = DownBlock(dim, 2*dim, + activation=activation, + batch_norm=batch_norm) + self.down2 = DownBlock(2*dim, 4*dim, + activation=activation, + batch_norm=batch_norm) + # self.down3 = DownBlock(4*dim, 8*dim, + # activation=activation, + # batch_norm=batch_norm) + if attention_coarse: + self.up0 = Attention(4*dim, heads=heads, + softmax=softmax, + norm_type=attn_norm_type, + sinosoidal_pe=sinosoidal_pe, + pe_trainable=pe_trainable, + skip_connection=skip_connection) + else: + self.up0 = DoubleConv(4*dim, 4*dim, + activation=activation, + batch_norm=batch_norm) + # self.up1 = UpBlock(8*dim, 4*dim, + # heads=heads, + # batch_norm=batch_norm, + # hadamard=hadamard, + # attention=attention_up, + # softmax=softmax, + # sinosoidal_pe=sinosoidal_pe, + # pe_trainable=pe_trainable) + self.up1= UpBlock(4*dim, 2*dim, + heads=heads, + batch_norm=batch_norm, + hadamard=hadamard, + attention=attention_up, + softmax=softmax, + sinosoidal_pe=sinosoidal_pe, + pe_trainable=pe_trainable) + self.up2 = UpBlock(2*dim, dim, + heads=heads, + batch_norm=batch_norm, + hadamard=hadamard, + attention=attention_up, + softmax=softmax, + sinosoidal_pe=sinosoidal_pe, + pe_trainable=pe_trainable,) + self.out = OutConv(dim, out_channels) + self.out_latent = out_latent + self.add_grad_channel = add_grad_channel + self.input_size = input_size + self.debug = debug + + # def forward(self, x, gradx, *args, **kwargs): + # "input dim: bsz, n, n, C" + # if gradx.ndim == x.ndim and self.add_grad_channel: + # x = torch.cat([x, gradx], dim=-1) + # x = x.permute(0, 3, 1, 2) + + def forward(self, x_gradx, *args, **kwargs): + + x_gradx = x_gradx.to(torch.float32) + x = x_gradx.permute(0, 3, 1, 2) + + if self.input_size: + _, _, *size = x.size() + x = F.interpolate(x, size=self.input_size, + mode='bilinear', + align_corners=True) + + x1 = self.inc(x) + + x2 = self.down1(x1) + + x3 = self.down2(x2) + + x3 = self.up0(x3) + + x = self.up1(x2, x3) + + x = self.up2(x1, x) + + out = self.out(x) + + if self.input_size: + out = F.interpolate(out, size=size, + mode='bilinear', + align_corners=True) + + out = out.permute(0, 2, 3, 1) + + if self.out_latent: + return dict(preds=out, + preds_latent=[x2, x3]) + else: + return dict(preds=out) + + +class DSMReconstructionGenerator(nn.Module): + def __init__(self, + s_initial_value, + train_s:bool, + g_f_real, + g_f_imag, + v, + w, + v_inv, + device + ): + super(DSMReconstructionGenerator, self).__init__() + self.num_of_channels = g_f_real.shape[-1] + self.train_s = train_s + #多通道s + if isinstance(s_initial_value, list): + self.multiple_s = True + s_list = nn.ParameterList() + for item in torch.tensor(s_initial_value): + s_list.append(item) + self.s = s_list + #单通道s + elif isinstance(s_initial_value, (int, float)): + self.multiple_s = False + self.s = nn.Parameter(torch.Tensor([s_initial_value]), requires_grad=self.train_s) + else: + raise ValueError("Input must be either a list or a number.") + self.g_f_real = g_f_real.to(device).to(torch.float32) #[r, n*n, C] + self.g_f_imag = g_f_imag.to(device).to(torch.float32) + self.v = v.to(device).to(torch.float32) + self.w = w.to(device).to(torch.float32) + self.v_inv = v_inv.to(device).to(torch.float32) + self.device = device + + def forward(self, u_s, *args, **kwargs): + "u_s dim: (bsz, r, C)" + C = self.num_of_channels + u_s_real = (u_s[0][:, ::10, :]).to(self.device).to(torch.float32) + u_s_imag = (u_s[1][:, ::10, :]).to(self.device).to(torch.float32) + # u_s_real = (u_s[0]).to(self.device).to(torch.float32) + # u_s_imag = (u_s[1]).to(self.device).to(torch.float32) + g_f = torch.complex(self.g_f_real, self.g_f_imag) + if self.multiple_s: + lb_real = torch.zeros_like(u_s_real, dtype=torch.float32) + lb_imag = torch.zeros_like(u_s_imag, dtype=torch.float32) + for i in range(len(self.s)): + Lam = torch.diag(torch.pow(self.w, self.s[i])) + # lb_real[..., i:i+1] = self.v@Lam@self.v_inv@u_s_real[..., i:i+1] + # lb_imag[..., i:i+1] = self.v@Lam@self.v_inv@u_s_imag[..., i:i+1] + lb_real[..., i:i+1] = torch.einsum('ij, ajk->aik', self.v@Lam@self.v_inv, u_s_real[..., i:i+1]) + lb_imag[..., i:i+1] = torch.einsum('ij, ajk->aik', self.v@Lam@self.v_inv, u_s_imag[..., i:i+1]) + else: + Lam = torch.diag(torch.pow(self.w, self.s)) + # lb_real = torch.einsum('ij, aik->ajk', self.v@Lam@self.v_inv, u_s_real) + # lb_imag = torch.einsum('ij, aik->ajk', self.v@Lam@self.v_inv, u_s_imag) + lb_real = torch.einsum('ij, ajk->aik', self.v@Lam@self.v_inv, u_s_real) + lb_imag = torch.einsum('ij, ajk->aik', self.v@Lam@self.v_inv, u_s_imag) + # lb_imag = u_s_imag + + lb_u_s = torch.complex(lb_real, lb_imag) + u_s = torch.complex(u_s_real, u_s_imag) + bsz = u_s_real.shape[0] + n = int(math.sqrt(g_f.shape[-2])) + + phi_real = torch.einsum('bic, ijc -> bjc', lb_real, torch.real(g_f.conj()))\ + - torch.einsum('bic, ijc -> bjc', lb_imag, torch.imag(g_f.conj())) + phi_imag = torch.einsum('bic, ijc -> bjc', lb_real, torch.imag(g_f.conj()))\ + + torch.einsum('bic, ijc -> bjc', lb_imag, torch.real(g_f.conj())) + phi = torch.complex(phi_real, phi_imag) + phi_ = torch.sqrt(phi_real**2 + phi_imag**2) + u_s_norm = torch.norm(lb_u_s, dim=1) + g_f_norm = torch.norm(g_f, dim=0) + # print(g_f_norm.shape) + norm = torch.einsum('bc, kc -> bkc', u_s_norm, g_f_norm) + # out = (phi_ / norm).reshape(bsz, n, n, C) + out = phi_.reshape(bsz, n, n, C) + return out + + # phi_real = torch.einsum('bic, ijc -> bjc', lb_real, torch.real(g_f.conj()))\ + # - torch.einsum('bic, ijc -> bjc', lb_imag, torch.imag(g_f.conj())) + # phi_imag = torch.einsum('bic, ijc -> bjc', lb_real, torch.imag(g_f.conj()))\ + # + torch.einsum('bic, ijc -> bjc', lb_imag, torch.real(g_f.conj())) + # phi = torch.norm(torch.complex(phi_real, phi_imag), dim=1).unsqueeze(1) + # u_s_norm = torch.norm(u_s, dim=1).unsqueeze(1) + # g_f_norm = torch.norm(g_f, dim=0) + # norm = torch.einsum('bij, kj -> bkj', u_s_norm, g_f_norm) + # out = (phi / norm).reshape(bsz, n, n, C) + # return out + + + +class DataPreprocessor(nn.Module): + def __init__(self, + normalizer_x=None, + normalization=False, + subsample: int = 1, + subsample_attn: int = 4, + subsample_method = 'nearest', + channel = 3, + train_data = True, + online_grad=True, + return_grad=True, + return_boundary=True + ): + + super(DataPreprocessor, self).__init__() + + self.normalizer_x = normalizer_x + self.normalization = normalization + self.subsample = subsample + self.subsample_attn = subsample_attn + self.subsample_method = subsample_method + self.channel = channel + self.train_data = train_data + self.online_grad = online_grad + self.return_grad = return_grad + self.return_boundary = return_boundary + + self.n_grid_fine = 64 + self.n_grid = int(((self.n_grid_fine - 1)/self.subsample) + 1) + self.n_grid_coarse = int( + ((self.n_grid_fine - 1)/self.subsample_attn) + 1) + self.h = 1/self.n_grid_fine + + self.grid_c = self.get_grid(self.n_grid_coarse) + self.grid = self.get_grid(self.n_grid_fine, + subsample=self.subsample, + return_boundary=self.return_boundary) + if self.train_data and self.normalization: + self.normalizer_x = UnitGaussianNormalizer() + self.normalizer_y = UnitGaussianNormalizer() + self.phi = self.normalizer_x.fit_transform(self.phi) + + @staticmethod + def get_grad(f, h): + ''' + h: mesh size + n: grid size + separate input for online grad generating + input f: (N, C, n, n) + ''' + bsz, N_C = f.shape[:2] + # n = int(((n - 1)/s) + 1) + # f = torch.tensor(f, dtype=torch.complex128) + fx, fy = [], [] + for i in range(N_C): + '''smaller mem footprint''' + _fx, _fy = DataPreprocessor.central_diff(f[:, i], h) + fx.append(_fx) + fy.append(_fy) + gradf = torch.stack(fx+fy, dim=1) # (N, 2*C, n, n) + return gradf + + @staticmethod + def central_diff(f, h, mode='constant', padding=True): + """ + mode: see + https://numpy.org/doc/stable/reference/generated/numpy.pad.html + """ + # x: (batch, n, n) + # b = x.shape[0] + # f_tensor = torch.tensor(f, dtype=torch.complex128) + # Add padding if required + if padding: + f = torch.nn.functional.pad(f, (1, 1, 1, 1), mode=mode, value=0) + d, s = 2, 1 # dilation and stride + grad_x = (f[:, d:, s:-s] - f[:, :-d, s:-s]) / d + grad_y = (f[:, s:-s, d:] - f[:, s:-s, :-d]) / d + + return grad_x/h, grad_y/h + + @staticmethod + def get_grid(n_grid, subsample=1, return_boundary=True): + x = torch.linspace(0, 1, n_grid) + y = torch.linspace(0, 1, n_grid) + x, y = torch.meshgrid(x, y, indexing='ij') + s = subsample + + if return_boundary: + x = x[::s, ::s] + y = y[::s, ::s] + else: + x = x[::s, ::s][1:-1, 1:-1] + y = y[::s, ::s][1:-1, 1:-1] + + grid = torch.stack([x, y], dim=-1) + + # grid is DoF, excluding boundary (n, n, 2), or (n-2, n-2, 2) + return grid + + def forward(self, dsm_phi, *args, **kwargs): + bsz = dsm_phi.shape[0] + s = self.subsample + + if self.return_grad and not self.online_grad: + gradphi = self.get_grad(dsm_phi, self.h) # u is (bsz, C, n, n) + gradphi = gradphi[..., ::s, ::s] + gradphi = gradphi.transpose((0, 2, 3, 1)) # (N, n, n, C) + else: + gradphi = np.zeros((bsz, )) # placeholder + self.gradphi = gradphi + + if self.train_data and self.normalization: + self.normalizer_x = UnitGaussianNormalizer() + self.normalizer_y = UnitGaussianNormalizer() + dsm_phi = self.normalizer_x.fit_transform(dsm_phi) + + # if self.return_boundary: + # _ = self.normalizer_y.fit_transform(x=targets) + # else: + # _ = self.normalizer_y.fit_transform( + # x=targets[:, 1:-1, 1:-1, :]) + elif self.normalization: + dsm_phi = self.normalizer_x.transform(dsm_phi) + + pos_dim = 2 + # uniform grid fine for all samples (n, n, 2) + if self.subsample_attn is None: + grid_c = torch.tensor([1.0]) # place holder + else: + grid_c = self.grid_c.reshape(-1, pos_dim) # (n_s*n_s, 2) + + grid = self.grid + # dsm_phi = dsm_phi[..., ::s, ::s] + # dsm_phi = dsm_phi.permute((0, 2, 3, 1)) # (N, n, n, C) + + if self.return_grad and self.online_grad: + dsm_phi = dsm_phi.permute(0, 3, 1, 2) + # dsm_phi (bsz, C, n, n) + gradphi = self.get_grad(dsm_phi, self.h)#(bsz, 2*C, n, n) + gradphi = gradphi.permute(0, 2, 3, 1) #(bsz, n, n, 2*C) + dsm_phi = dsm_phi[..., ::s, ::s] + dsm_phi = dsm_phi.permute((0, 2, 3, 1)) + return torch.cat([dsm_phi, gradphi], dim=-1) + elif self.return_grad: + dsm_phi = dsm_phi[..., ::s, ::s] + gradphi = self.gradphi + return torch.cat([dsm_phi, gradphi], dim=-1) + else: + dsm_phi = dsm_phi[..., ::s, ::s] + gradphi = torch.tensor(float('nan')) + return dsm_phi diff --git a/fealpy/ml/generator/near_field_data_generator.py b/fealpy/ml/generator/near_field_data_generator.py index 19bbb4b13..7f447f482 100644 --- a/fealpy/ml/generator/near_field_data_generator.py +++ b/fealpy/ml/generator/near_field_data_generator.py @@ -2,9 +2,10 @@ import os import matplotlib.pyplot as plt from numpy.typing import NDArray -import numpy as bm +import numpy as np from typing import Sequence, Callable +from fealpy.backend import backend_manager as bm from fealpy.mesh import TriangleMesh, QuadrangleMesh, UniformMesh2d from fealpy.pde.pml_2d import PMLPDEModel2d from fealpy.functionspace import LagrangeFESpace @@ -94,7 +95,7 @@ def get_nearfield_data(self, k: float, d: Sequence[float]): space = LagrangeFESpace(self.mesh, p=self.p) - # 定义积分器 + # 定义积分子 D = ScalarDiffusionIntegrator(pde.diffusion_coefficient, q=self.q) C = ScalarConvectionIntegrator(pde.convection_coefficient, q=self.q) M = ScalarMassIntegrator(pde.reaction_coefficient, q=self.q) @@ -200,7 +201,7 @@ def save(self, save_path: str, scatterer_index: int): name = f"{k_name}, d={d_name}" data_dict[name] = self.data_for_dsm(k=k_values[i], d=d_values[j]) filename = os.path.join(save_path, f"data_for_dsm_{scatterer_index}.npz") - bm.savez(filename, **data_dict) + np.savez(filename, **data_dict) def visualization_of_nearfield_data(self, k: float, d: Sequence[float]): """ From 9965d07eb26ddfd08394cbfd4078779e97b88b17 Mon Sep 17 00:00:00 2001 From: AlbertZyy Date: Wed, 27 Nov 2024 15:08:12 +0800 Subject: [PATCH 49/63] fix(mesh): update coarsen --- fealpy/mesh/triangle_mesh.py | 9 ++++++--- fealpy/mesh/utils.py | 12 ++++++++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/fealpy/mesh/triangle_mesh.py b/fealpy/mesh/triangle_mesh.py index e1d1fe50b..41e0e872a 100644 --- a/fealpy/mesh/triangle_mesh.py +++ b/fealpy/mesh/triangle_mesh.py @@ -600,6 +600,7 @@ def coarsen(self, isMarkedCell=None, options={}): https://lyc102.github.io/ifem/afem/coarsen/ """ + from .utils import inverse_relation if isMarkedCell is None: return @@ -619,9 +620,10 @@ def coarsen(self, isMarkedCell=None, options={}): isIGoodNode = (valence == valenceNew) & (valence == 4) isBGoodNode = (valence == valenceNew) & (valence == 2) - node2cell = self.node_to_cell() + # node2cell = self.node_to_cell() - I, J = bm.nonzero(node2cell[isIGoodNode, :]) + # I, J = bm.nonzero(node2cell[isIGoodNode, :]) + _, J, _ = inverse_relation(cell, NN, isIGoodNode) nodeStar = J.reshape(-1, 4) ix = (cell[nodeStar[:, 0], 2] == cell[nodeStar[:, 3], 1]) @@ -649,7 +651,8 @@ def coarsen(self, isMarkedCell=None, options={}): cell = bm.set_at(cell , (t2, 2) , p1) cell = bm.set_at(cell , (t3, 0) , -1) - I, J = bm.nonzero(node2cell[isBGoodNode, :]) + # I, J = bm.nonzero(node2cell[isBGoodNode, :]) + _, J, _ = inverse_relation(cell, NN, isBGoodNode) nodeStar = J.reshape(-1, 2) idx = (cell[nodeStar[:, 0], 2] == cell[nodeStar[:, 1], 1]) nodeStar = bm.set_at(nodeStar , idx , nodeStar[idx, :][:, [0, 1]]) diff --git a/fealpy/mesh/utils.py b/fealpy/mesh/utils.py index 5227af72e..b841710a7 100644 --- a/fealpy/mesh/utils.py +++ b/fealpy/mesh/utils.py @@ -47,9 +47,13 @@ def edim2entity(storage: Dict, factory: Dict, edim: int, index=None): return et[index] -def inverse_relation(entity: TensorLike, size: int, index=None): +def inverse_relation(entity: TensorLike, size: int, index=None, *, sorted=True): """Return the inverse relationship of a homogeneous entity in COO sparse format, - including the row indices, column indices, and shape.""" + including the row indices, column indices, and shape. + + For instance, if `entity` is cell_to_node, that a indices field on cells, + this function returns node_to_cell. In this case, `size` should be the number + of nodes, and `index` should be a bool field on nodes.""" assert entity.ndim == 2 if index is None: @@ -66,6 +70,10 @@ def inverse_relation(entity: TensorLike, size: int, index=None): num_selected_each_entity = bm.sum(relation_flag, axis=-1, dtype=bm.int32) col = bm.repeat(bm.arange(entity.shape[0]), num_selected_each_entity) + if sorted: + order = bm.lexsort([col, row]) + row, col = row[order], col[order] + return row, col, (size, entity.shape[0]) From a8140b3a42214d099b9432b19f7349ea3f935948 Mon Sep 17 00:00:00 2001 From: AlbertZyy Date: Wed, 27 Nov 2024 16:06:27 +0800 Subject: [PATCH 50/63] fix(backend, mesh): specify context for tensors in `inverse_realtion`; update `index_add`. --- fealpy/backend/pytorch_backend.py | 17 +++++++++-------- fealpy/mesh/triangle_mesh.py | 4 ++-- fealpy/mesh/utils.py | 5 +++-- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/fealpy/backend/pytorch_backend.py b/fealpy/backend/pytorch_backend.py index 325645fb3..cde6ef8c5 100644 --- a/fealpy/backend/pytorch_backend.py +++ b/fealpy/backend/pytorch_backend.py @@ -2,7 +2,7 @@ from typing import Union, Optional, Tuple, Any from itertools import combinations_with_replacement from functools import reduce, partial -from math import factorial +from math import factorial, prod try: import torch @@ -357,15 +357,16 @@ def add_at(a: Tensor, indices, src, /): @staticmethod def index_add(a: Tensor, index, src, /, *, axis: int=0, alpha=1): - if index.ndim > 1: + src_flat_shape = a.shape[:axis] + (index.numel(), ) + a.shape[axis+1:] + + if isinstance(src, (int, float, complex)): + src = torch.full([1,]*len(src_flat_shape), src, dtype=a.dtype, device=a.device) + src = torch.broadcast_to(src, src_flat_shape) + else: src_shape = a.shape[:axis] + index.shape + a.shape[axis+1:] - src_flat_shape = a.shape[:axis] + (index.numel(), ) + a.shape[axis+1:] src = torch.broadcast_to(src, src_shape).reshape(src_flat_shape) - index = index.ravel() - if isinstance(src, (int, float)): - max_idx = torch.max(index) - src = torch.full(max_idx+1, src, dtype=a.dtype, device=a.device) - return a.index_add_(axis, index, src, alpha=alpha) + + return a.index_add_(axis, index.ravel(), src, alpha=alpha) @staticmethod def scatter(x: Tensor, index, src, /, *, axis: int=0): diff --git a/fealpy/mesh/triangle_mesh.py b/fealpy/mesh/triangle_mesh.py index 41e0e872a..682cdb511 100644 --- a/fealpy/mesh/triangle_mesh.py +++ b/fealpy/mesh/triangle_mesh.py @@ -612,10 +612,10 @@ def coarsen(self, isMarkedCell=None, options={}): node = self.entity('node') valence = bm.zeros(NN, dtype=self.itype, device=self.device) - bm.add_at(valence, cell, 1) + valence = bm.index_add(valence, cell, 1) valenceNew = bm.zeros(NN, dtype=self.itype, device=self.device) - bm.add_at(valenceNew, cell[isMarkedCell][:, 0], 1) + valenceNew = bm.index_add(valenceNew, cell[isMarkedCell][:, 0], 1) isIGoodNode = (valence == valenceNew) & (valence == 4) isBGoodNode = (valence == valenceNew) & (valence == 2) diff --git a/fealpy/mesh/utils.py b/fealpy/mesh/utils.py index b841710a7..9830c8516 100644 --- a/fealpy/mesh/utils.py +++ b/fealpy/mesh/utils.py @@ -55,10 +55,11 @@ def inverse_relation(entity: TensorLike, size: int, index=None, *, sorted=True): this function returns node_to_cell. In this case, `size` should be the number of nodes, and `index` should be a bool field on nodes.""" assert entity.ndim == 2 + kwargs = {'dtype': entity.dtype, 'device': entity.device} if index is None: row = entity.reshape(-1) - col = bm.repeat(bm.arange(entity.shape[0]), entity.shape[1]) + col = bm.repeat(bm.arange(entity.shape[0], **kwargs), entity.shape[1]) else: if isinstance(index, TensorLike) and index.dtype == bm.bool: flag = index @@ -68,7 +69,7 @@ def inverse_relation(entity: TensorLike, size: int, index=None, *, sorted=True): relation_flag = flag[entity] row = entity.reshape(-1)[relation_flag.reshape(-1)] num_selected_each_entity = bm.sum(relation_flag, axis=-1, dtype=bm.int32) - col = bm.repeat(bm.arange(entity.shape[0]), num_selected_each_entity) + col = bm.repeat(bm.arange(entity.shape[0], **kwargs), num_selected_each_entity) if sorted: order = bm.lexsort([col, row]) From be20cf70b52c6615e90d4cb2935f08ebe45b37a7 Mon Sep 17 00:00:00 2001 From: BellaLq <2655493031@qq.com> Date: Wed, 27 Nov 2024 21:57:21 +0800 Subject: [PATCH 51/63] update --- .../parametric_lagrange_fe_space.py | 2 +- fealpy/mesh/lagrange_quadrangle_mesh.py | 134 ++++++++---------- fealpy/mesh/lagrange_triangle_mesh.py | 17 +-- test/mesh/lagrange_quadrangle_mesh_data.py | 16 +-- test/mesh/test_lagrange_quadrangle_mesh.py | 40 +++--- test/mesh/test_lagrange_triangle_mesh.py | 6 +- 6 files changed, 101 insertions(+), 114 deletions(-) diff --git a/fealpy/functionspace/parametric_lagrange_fe_space.py b/fealpy/functionspace/parametric_lagrange_fe_space.py index c9b0cec35..df4107529 100644 --- a/fealpy/functionspace/parametric_lagrange_fe_space.py +++ b/fealpy/functionspace/parametric_lagrange_fe_space.py @@ -23,7 +23,7 @@ def __init__(self, mesh: _MT, p, q=None, spacetype='C'): self.mesh = mesh self.p = p self.cellmeasure = mesh.cell_area() - self.dof = LinearMeshCFEDof(mesh.tmesh, p) + self.dof = LinearMeshCFEDof(mesh.linearmesh, p) self.multi_index_matrix = mesh.multi_index_matrix self.device = mesh.device diff --git a/fealpy/mesh/lagrange_quadrangle_mesh.py b/fealpy/mesh/lagrange_quadrangle_mesh.py index aa0df3f7e..7a35a5ffe 100644 --- a/fealpy/mesh/lagrange_quadrangle_mesh.py +++ b/fealpy/mesh/lagrange_quadrangle_mesh.py @@ -72,11 +72,10 @@ def quadrature_formula(self, q, etype: Union[int, str] = 'cell'): raise ValueError(f"entity type: {etype} is wrong!") def bc_to_point(self, bc: TensorLike, index: Index=_S, etype='cell'): - #TODO 复制粘贴 LagrangeTriangleMesh 的代码 node = self.node TD = len(bc) - phi = self.shape_function(bc) - p = bm.einsum() + phi = self.shape_function(bc, p=p) + p = bm.einsum('cqn, cni -> cqi', phi, node[entity]) return p # shape function @@ -87,61 +86,13 @@ def shape_function(self, bc: TensorLike, p=None): bc[i] 是一个一维积分公式的重心坐标数组 假设 bc[0]==bc[1]== ... ==bc[TD-1] """ - #TODO test - return self.linearmesh.shape_function(bc, p) + return self.linearmesh.cell_shape_function(bc, p) def grad_shape_function(self, bc: TensorLike, p=None, index: Index=_S, variables='x'): - return self.linearmesh.grad_shape_function(bc, p, index, variables) - - def vtk_cell_type(self, etype='cell'): - """ - - Notes - ----- - 返回网格单元对应的 vtk 类型。 - """ - if etype in {'cell', 2}: - VTK_LAGRANGE_QUADRILATERAL = 70 - return VTK_LAGRANGE_QUADRILATERAL - elif etype in {'face', 'edge', 1}: - VTK_LAGRANGE_CURVE = 68 - return VTK_LAGRANGE_CURVE - - - def to_vtk(self, etype='cell', index: Index=_S, fname=None): - """ - Parameters - ---------- - - @berif 把网格转化为 VTK 的格式 - """ - from fealpy.mesh.vtk_extent import vtk_cell_index, write_to_vtu + return self.linearmesh.cell_grad_shape_function(bc, p, index, variables) - node = self.entity('node') - GD = self.geo_dimension() - if GD == 2: - node = bm.concatenate((node, bm.zeros((node.shape[0], 1), dtype=bm.float64)), axis=1) - - #cell = self.entity(etype)[index] - cell = self.entity(etype, index) - cellType = self.vtk_cell_type(etype) - idx = vtk_cell_index(self.p, cellType) - NV = cell.shape[-1] - - cell = bm.concatenate((bm.zeros((len(cell), 1), dtype=cell.dtype), cell[:, idx]), axis=1) - cell[:, 0] = NV - - NC = len(cell) - if fname is None: - return node, cell.flatten(), cellType, NC - else: - print("Writting to vtk...") - write_to_vtu(fname, node, NC, cellType, cell.flatten(), - nodedata=self.nodedata, - celldata=self.celldata) - - # ipoint --> copy TriangleMesh + # ipoints def number_of_local_ipoints(self, p:int, iptype:Union[int, str]='cell'): """ @berif 每个ltri单元上插值点的个数 @@ -181,40 +132,34 @@ def cell_area(self, q=None, index: Index=_S): """ Calculate the area of a cell. """ - #TODO Test p = self.p q = p if q is None else q - GD = self.geo_dimension() qf = self.quadrature_formula(q, etype='cell') bcs, ws = qf.get_quadrature_points_and_weights() - J = self.jacobi_matrix(bcs, index=index) - n = bm.cross(J[..., 0], J[..., 1], axis=-1) - if GD == 3: - n = bm.sqrt(bm.sum(n**2, axis=-1)) # (NC, NQ) - a = bm.einsum('i, ji -> j', ws, n)/2.0 + G = self.first_fundamental_form(bcs) + d = bm.sqrt(bm.linalg.det(G)) + a = bm.einsum('q, cq -> c', ws, d) return a def edge_length(self, q=None, index: Index=_S): """ Calculate the length of the side. """ - #TODO Test p = self.p q = p if q is None else q qf = self.quadrature_formula(q, etype='edge') bcs, ws = qf.get_quadrature_points_and_weights() J = self.jacobi_matrix(bcs, index=index) - a = bm.sqrt(bm.sum(J**2, axis=(-1, -2))) - l = bm.einsum('i, ij -> j', ws, a) - return l + l = bm.sqrt(bm.sum(J**2, axis=(-1, -2))) + a = bm.einsum('q, cq -> c', ws, l) + return a def cell_unit_normal(self, bc: TensorLike, index: Index=_S): """ When calculating the surface,the direction of the unit normal at the integration point. """ - #TODO Test J = self.jacobi_matrix(bc, index=index) n = bm.cross(J[..., 0], J[..., 1], axis=-1) if self.GD == 3: @@ -224,12 +169,11 @@ def cell_unit_normal(self, bc: TensorLike, index: Index=_S): def jacobi_matrix(self, bc: TensorLike, p=None, index: Index=_S, return_grad=False): """ - @berif 计算参考单元 (xi, eta) 到实际 Lagrange 三角形(x) 之间映射的 Jacobi 矩阵。 + @berif 计算参考单元 (xi, eta) 到实际 Lagrange 四边形(x) 之间映射的 Jacobi 矩阵。 x(xi, eta) = phi_0 x_0 + phi_1 x_1 + ... + phi_{ldof-1} x_{ldof-1} """ - #TODO 测试,如果不对,那就复制粘贴 TensorMesh 的代码 - TD = bc.shape[-1] - 1 + TD = len(bc) entity = self.entity(TD, index) gphi = self.grad_shape_function(bc, p=p, variables='u') J = bm.einsum( @@ -246,8 +190,7 @@ def first_fundamental_form(self, bc: Union[TensorLike, Tuple[TensorLike]], """ Compute the first fundamental form of a mesh surface at integration points. """ - #TODO 测试 - TD = bc.shape[-1] - 1 + TD = len(bc) J = self.jacobi_matrix(bc, index=index, return_grad=return_grad) @@ -284,7 +227,6 @@ def integral(self, f, q=3, celltype=False) -> TensorLike: """ @brief 在网格中数值积分一个函数 """ - #TODO 测试 GD = self.geo_dimension() qf = self.integrator(q, etype='cell') bcs, ws = qf.get_quadrature_points_and_weights() @@ -323,7 +265,6 @@ def error(self, u, v, q=3, power=2, celltype=False) -> TensorLike: """ @brief Calculate the error between two functions. """ - #TODO 测试 GD = self.geo_dimension() qf = self.quadrature_formula(q, etype='cell') bcs, ws = qf.get_quadrature_points_and_weights() @@ -370,3 +311,50 @@ def error(self, u, v, q=3, power=2, celltype=False) -> TensorLike: e = bm.power(bm.sum(e, axis=tuple(range(1, len(e.shape)))), 1/power) return e # float or (NC, ) + # 可视化 + def vtk_cell_type(self, etype='cell'): + """ + + Notes + ----- + 返回网格单元对应的 vtk 类型。 + """ + if etype in {'cell', 2}: + VTK_LAGRANGE_QUADRILATERAL = 70 + return VTK_LAGRANGE_QUADRILATERAL + elif etype in {'face', 'edge', 1}: + VTK_LAGRANGE_CURVE = 68 + return VTK_LAGRANGE_CURVE + + + def to_vtk(self, etype='cell', index: Index=_S, fname=None): + """ + Parameters + ---------- + + @berif 把网格转化为 VTK 的格式 + """ + from fealpy.mesh.vtk_extent import vtk_cell_index, write_to_vtu + + node = self.entity('node') + GD = self.geo_dimension() + if GD == 2: + node = bm.concatenate((node, bm.zeros((node.shape[0], 1), dtype=bm.float64)), axis=1) + + cell = self.entity(etype, index) + cellType = self.vtk_cell_type(etype) + idx = vtk_cell_index(self.p, cellType) + NV = cell.shape[-1] + + cell = bm.concatenate((bm.zeros((len(cell), 1), dtype=cell.dtype), cell[:, idx]), axis=1) + cell[:, 0] = NV + + NC = len(cell) + if fname is None: + return node, cell.flatten(), cellType, NC + else: + print("Writting to vtk...") + write_to_vtu(fname, node, NC, cellType, cell.flatten(), + nodedata=self.nodedata, + celldata=self.celldata) + diff --git a/fealpy/mesh/lagrange_triangle_mesh.py b/fealpy/mesh/lagrange_triangle_mesh.py index 09a860483..c4639b627 100644 --- a/fealpy/mesh/lagrange_triangle_mesh.py +++ b/fealpy/mesh/lagrange_triangle_mesh.py @@ -32,7 +32,7 @@ def __init__(self, node: TensorLike, cell: TensorLike, p=1, surface=None, self.construct() self.meshtype = 'ltri' - self.tmesh = None # 网格的顶点必须在球面上 + self.linearmesh = None # 网格的顶点必须在球面上 self.nodedata = {} self.edgedata = {} @@ -59,7 +59,7 @@ def generate_local_lagrange_edges(self, p: int) -> TensorLike: def interpolation_points(self, p: int, index: Index=_S): """Fetch all p-order interpolation points on the triangle mesh.""" - node = self.tmesh.entity('node') + node = self.linearmesh.entity('node') if p == 1: return node[index] if p <= 0: @@ -104,7 +104,7 @@ def from_triangle_mesh(cls, mesh, p: int, surface=None): node, _ = surface.project(node) lmesh = cls(node, cell, p=p, construct=True) - lmesh.tmesh = mesh + lmesh.linearmesh = mesh lmesh.edge2cell = mesh.edge2cell # (NF, 4) lmesh.cell2edge = mesh.cell_to_edge() @@ -169,7 +169,7 @@ def grad_shape_function(self, bc: TensorLike, p=None, index: Index=_S, variables gphi = bm.einsum('cqkm, cqmn, qln -> cqlk', J, G, gphi) return gphi - # ipoint --> copy TriangleMesh + # ipoints def number_of_local_ipoints(self, p:int, iptype:Union[int, str]='cell'): """ @berif 每个ltri单元上插值点的个数 @@ -270,7 +270,7 @@ def cell_area(self, q=None, index: Index=_S): n = bm.cross(J[..., 0], J[..., 1], axis=-1) if GD == 3: n = bm.sqrt(bm.sum(n**2, axis=-1)) # (NC, NQ) - a = bm.einsum('i, ji -> j', ws, n)/2.0 + a = bm.einsum('q, cq -> c', ws, n)/2.0 return a def edge_length(self, q=None, index: Index=_S): @@ -283,9 +283,9 @@ def edge_length(self, q=None, index: Index=_S): bcs, ws = qf.get_quadrature_points_and_weights() J = self.jacobi_matrix(bcs, index=index) - a = bm.sqrt(bm.sum(J**2, axis=(-1, -2))) - l = bm.einsum('i, ij -> j', ws, a) - return l + l = bm.sqrt(bm.sum(J**2, axis=(-1, -2))) + a = bm.einsum('q, cq -> c', ws, l) + return a def cell_unit_normal(self, bc: TensorLike, index: Index=_S): """ @@ -443,6 +443,7 @@ def error(self, u, v, q=3, power=2, celltype=False) -> TensorLike: e = bm.power(bm.sum(e, axis=tuple(range(1, len(e.shape)))), 1/power) return e # float or (NC, ) + # 可视化 def vtk_cell_type(self, etype='cell'): """ @berif 返回网格单元对应的 vtk类型。 diff --git a/test/mesh/lagrange_quadrangle_mesh_data.py b/test/mesh/lagrange_quadrangle_mesh_data.py index 8f885f667..210f29f7b 100644 --- a/test/mesh/lagrange_quadrangle_mesh_data.py +++ b/test/mesh/lagrange_quadrangle_mesh_data.py @@ -7,16 +7,10 @@ mesh = QuadrangleMesh.from_unit_sphere_surface() node = mesh.interpolation_points(3) cell = mesh.cell_to_ipoint(3) -""" -from_quadrangle_mesh_data = [ + +cell_area_data = [ { - "p": 3, - "surface": surface, - "cell": np.array(dtype=np.int32), - "NN": - "NE": - "NF": - "NC": - } + "sphere_cm": 4*np.pi*(1**2) + } ] -""" + diff --git a/test/mesh/test_lagrange_quadrangle_mesh.py b/test/mesh/test_lagrange_quadrangle_mesh.py index 2ceebc577..14596bc1b 100644 --- a/test/mesh/test_lagrange_quadrangle_mesh.py +++ b/test/mesh/test_lagrange_quadrangle_mesh.py @@ -21,35 +21,39 @@ def test_surface_mesh(self, backend): surface = SphereSurface() mesh = QuadrangleMesh.from_unit_sphere_surface() - fname = f"sphere_qtest_quad.vtu" - mesh.to_vtk(fname=fname) - lmesh = LagrangeQuadrangleMesh.from_quadrangle_mesh(mesh, p=3, surface=surface) fname = f"sphere_qtest.vtu" lmesh.to_vtk(fname=fname) @pytest.mark.parametrize("backend", ['numpy']) - @pytest.mark.parametrize("data", from_quadrangle_mesh_data) - def test_from_quadranglemesh(self, data, backend): + @pytest.mark.parametrize("data", cell_area_data) + def test_cell_area(self, data, backend): bm.set_backend(backend) - p = data['p'] - surface = data['surface'] + surface = SphereSurface() #以原点为球心,1 为半径的球 mesh = QuadrangleMesh.from_unit_sphere_surface() - - lmesh = LagrangeQuadrangleMesh.from_triangle_mesh(mesh, p, surface=surface) - - assert lmesh.number_of_nodes() == data["NN"] - assert lmesh.number_of_edges() == data["NE"] - assert lmesh.number_of_faces() == data["NF"] - assert lmesh.number_of_cells() == data["NC"] - cell = lmesh.entity('cell') - np.testing.assert_allclose(bm.to_numpy(cell), data["cell"], atol=1e-14) - + # 计算收敛阶 + maxit = 4 + cm = np.zeros(maxit, dtype=np.float64) + em = np.zeros(maxit, dtype=np.float64) + for i in range(maxit): + lmesh = LagrangeQuadrangleMesh.from_quadrangle_mesh(mesh, p=3, surface=surface) + + cm[i] = np.sum(lmesh.cell_area()) + + x = bm.to_numpy(cm[i]) + y = data["sphere_cm"] + em[i] = np.abs(x - y) # absolute error + + if i < maxit-1: + mesh.uniform_refine() + + em_ratio = em[0:-1] / em[1:] + print("unit_sphere:", em_ratio) if __name__ == "__main__": a = TestLagrangeQuadrangleMeshInterfaces() a.test_surface_mesh('numpy') - #a.test_from_quadrangle_mesh(from_quadrangle_mesh_data[0], 'numpy') + #a.test_cell_area(cell_area_data[0], 'numpy') #pytest.main(["./test_lagrange_triangle_mesh.py"]) diff --git a/test/mesh/test_lagrange_triangle_mesh.py b/test/mesh/test_lagrange_triangle_mesh.py index 93e23aa85..3e8576671 100644 --- a/test/mesh/test_lagrange_triangle_mesh.py +++ b/test/mesh/test_lagrange_triangle_mesh.py @@ -126,11 +126,11 @@ def test_error(self, backend): if __name__ == "__main__": - #a = TestLagrangeTriangleMeshInterfaces() + a = TestLagrangeTriangleMeshInterfaces() #a.test_init_mesh(init_data[0], 'numpy') #a.test_from_triangle_mesh(from_triangle_mesh_data[0], 'numpy') - #a.test_surface_mesh('numpy') + a.test_surface_mesh('numpy') #a.test_cell_area(cell_area_data[0], 'numpy') #a.test_(cell_[0], 'numpy') #a.test_error('numpy') - pytest.main(["./test_lagrange_triangle_mesh.py"]) + #pytest.main(["./test_lagrange_triangle_mesh.py"]) From 6621bc59aa05ea1f5fc404540d8109e26ad0fdd6 Mon Sep 17 00:00:00 2001 From: gaotingyi <1115633022@qq.com> Date: Thu, 28 Nov 2024 10:42:13 +0800 Subject: [PATCH 52/63] cm --- .../fem/doublelaplace_fem_dirichlet.py | 3 +- .../old/experiment/fem/interpolation_fem.py | 39 +++-- .../fem/trilaplace_fem_dirichlet.py | 151 ++++++++++++++++++ fealpy/fem/mlaplace_bernstein_integrator.py | 13 +- fealpy/fem/mthlaplace_integrator.py | 7 +- fealpy/fem/scalar_source_integrator.py | 2 +- .../functionspace/cm_conforming_fe_space.py | 14 +- .../functionspace/cm_conforming_fe_space3d.py | 24 ++- fealpy/mesh/mesh_base.py | 4 +- fealpy/pde/biharmonic_triharmonic_2d.py | 3 +- 10 files changed, 224 insertions(+), 36 deletions(-) create mode 100644 example/old/experiment/fem/trilaplace_fem_dirichlet.py diff --git a/example/old/experiment/fem/doublelaplace_fem_dirichlet.py b/example/old/experiment/fem/doublelaplace_fem_dirichlet.py index 5b10da3e5..f99291195 100644 --- a/example/old/experiment/fem/doublelaplace_fem_dirichlet.py +++ b/example/old/experiment/fem/doublelaplace_fem_dirichlet.py @@ -60,6 +60,7 @@ next(tmr) x = sp.symbols('x') y = sp.symbols('y') +#u = (sp.sin(sp.pi*y)*sp.sin(sp.pi*x))**4 u = (sp.sin(2*sp.pi*y)*sp.sin(2*sp.pi*x))**2 pde = DoubleLaplacePDE(u, device=device) ulist = get_flist(u, device=device)[:3] @@ -106,7 +107,7 @@ gdof = space.number_of_global_dofs() NDof[i] = 1/4/2**i - bc1 = DirichletBC(space, gD = ulist) + bc1 = DirichletBC(space, gd = ulist) #import ipdb #ipdb.set_trace() A, F = bc1.apply(A, F) diff --git a/example/old/experiment/fem/interpolation_fem.py b/example/old/experiment/fem/interpolation_fem.py index 8a7895e54..45d39f385 100644 --- a/example/old/experiment/fem/interpolation_fem.py +++ b/example/old/experiment/fem/interpolation_fem.py @@ -10,7 +10,7 @@ ## 参数解析 parser = argparse.ArgumentParser(description= """ - 光滑元有限元方法插值 + 三维光滑元有限元方法插值 """) parser.add_argument('--degree', @@ -56,8 +56,13 @@ x = sp.symbols('x') y = sp.symbols('y') z = sp.symbols('z') -u = sp.sin(2*x)*sp.sin(2*y)*sp.sin(z) -#u = (sp.sin(2*sp.pi*y)*sp.sin(2*sp.pi*x)*sp.sin(2*sp.pi*z))**4 +#u = sp.sin(2*x)*sp.sin(2*y)*sp.sin(z) +#u = (sp.sin(2*sp.pi*y)*sp.sin(2*sp.pi*x)*sp.sin(2*sp.pi*z)) +#u = (sp.sin(sp.pi*y)*sp.sin(sp.pi*x)*sp.sin(sp.pi*z))**2 +#u = (sp.sin(sp.pi*y)*sp.sin(sp.pi*x)*sp.sin(sp.pi*z)) +#u = sp.sin(5*x)*sp.sin(5*y)*sp.sin(5*z) +u = sp.sin(7*x)*sp.sin(7*y)*sp.sin(7*z) +#u = (sp.sin(2*sp.pi*y)*sp.sin(2*sp.pi*x)*sp.sin(sp.pi*z))**2 flist = get_flist(u) NDof = bm.zeros(maxit, dtype=bm.float64) @@ -67,9 +72,9 @@ '$||\\nabla^3 u - \\nabla^3 u_h||_{\\Omega,0}$'] errorMatrix = bm.zeros((4, maxit), dtype=bm.float64) tmr.send('网格和pde生成时间') - +nx = n for i in range(maxit): - mesh = TetrahedronMesh.from_box([0,1,0,1,0,1], 2**i, 2**i, 2**i) + mesh = TetrahedronMesh.from_box([0,1,0,1,0,1], nx, nx, nx) node = mesh.entity('node') isCornerNode = bm.zeros(len(node),dtype=bm.bool) for n in bm.array([[0,0,0],[1,0,0],[0,1,0],[1,1,0],[1,1,1],[0,0,1],[1,0,1],[0,1,1]], dtype=bm.float64): @@ -78,8 +83,13 @@ from fealpy.functionspace.cm_conforming_fe_space3d_old import CmConformingFESpace3d as CmConformingFESpace3d_old #space = CmConformingFESpace3d_old(mesh,9, 1, isCornerNode) space = CmConformingFESpace3d(mesh, p=p, m=m, isCornerNode=isCornerNode) + gdof = space.number_of_global_dofs() + print("gdof", gdof) + tmr.send(f'第{i}次空间生成时间') - fI = space.interpolation(flist) + fI = space.function() + fI[:] = space.interpolation(flist) + print('插值') tmr.send(f'第{i}次插值时间') @barycentric @@ -92,12 +102,18 @@ def ug2val(p): @barycentric def ug3val(p): return fI.grad_m_value(p, 3) - - errorMatrix[0, i] = mesh.error(flist[0], fI, q=p+3) - errorMatrix[1, i] = mesh.error(flist[1], ug1val, q=p+3) - #errorMatrix[2, i] = mesh.error(flist[2], ug2val, q=p+3) - #errorMatrix[3, i] = mesh.error(flist[3], ug3val, q=p+3) + errorMatrix[0, i] = mesh.error(flist[0], fI) + print('error1') + errorMatrix[1, i] = mesh.error(flist[1], ug1val) + print('error2') + errorMatrix[2, i] = mesh.error(flist[2], ug2val) + print('error3') + errorMatrix[3, i] = mesh.error(flist[3], ug3val) + print('error4') print(errorMatrix) + + nx = nx*2 + #n = int(n) @@ -107,6 +123,7 @@ def ug3val(p): print("order : ", bm.log2(errorMatrix[0,:-1]/errorMatrix[0,1:])) print("order : ", bm.log2(errorMatrix[1,:-1]/errorMatrix[1,1:])) print("order : ", bm.log2(errorMatrix[2,:-1]/errorMatrix[2,1:])) +print("order : ", bm.log2(errorMatrix[3,:-1]/errorMatrix[3,1:])) diff --git a/example/old/experiment/fem/trilaplace_fem_dirichlet.py b/example/old/experiment/fem/trilaplace_fem_dirichlet.py new file mode 100644 index 000000000..cff89eb11 --- /dev/null +++ b/example/old/experiment/fem/trilaplace_fem_dirichlet.py @@ -0,0 +1,151 @@ +import argparse +import sympy as sp +from matplotlib import pyplot as plt + +from fealpy import logger +logger.setLevel('WARNING') +from fealpy.mesh import TriangleMesh +from fealpy.functionspace import CmConformingFESpace2d +from fealpy.fem import BilinearForm +from fealpy.fem.mthlaplace_integrator import MthLaplaceIntegrator +from fealpy.fem import LinearForm, ScalarSourceIntegrator +from fealpy.fem import DirichletBC +from fealpy.backend import backend_manager as bm +from fealpy.solver import cg +from fealpy.pde.biharmonic_triharmonic_2d import TripleLaplacePDE, get_flist +from fealpy.utils import timer +from fealpy.decorator import barycentric +from scipy.sparse.linalg import spsolve +from scipy.sparse import csr_matrix +from fealpy import logger +from fealpy.solver import spsolve +logger.setLevel('INFO') +## 参数解析 +parser = argparse.ArgumentParser(description= + """ + 光滑元有限元方法求解双调和方程 + """) + +parser.add_argument('--degree', + default=5, type=int, + help='光滑有限元空间的次数, 默认为 5 次.') + +parser.add_argument('--n', + default=4, type=int, + help='初始网格剖分段数.') + +parser.add_argument('--maxit', + default=4, type=int, + help='默认网格加密求解的次数, 默认加密求解 4 次') + +parser.add_argument('--backend', + default='numpy', type=str, + help='默认后端为numpy') + +parser.add_argument('--device', + default='cpu', type=str, + help='默认gpu计算') + +args = parser.parse_args() + + +bm.set_backend(args.backend) +#device = "cuda" +p = args.degree +n = args.n +maxit = args.maxit +device = args.device + +tmr = timer() +next(tmr) +x = sp.symbols('x') +y = sp.symbols('y') +#u = (sp.sin(sp.pi*y)*sp.sin(sp.pi*x))**4 +#u = (sp.sin(4*sp.pi*y)*sp.sin(4*sp.pi*x))**6 +#u = (sp.sin(2*y)*sp.sin(2*x)) +#u = x**3*y**4 +#u = x**2*(x-1)**5*y+2*x*y**2*(y-1)**5 +u = (sp.sin(2*sp.pi*y)*sp.sin(2*sp.pi*x)) +pde = TripleLaplacePDE(u) +ulist = get_flist(u, device=device) +mesh = TriangleMesh.from_box([0,1,0,1], n, n, device=device) + +ikwargs = bm.context(mesh.cell) +fkwargs = bm.context(mesh.node) + +NDof = bm.zeros(maxit, **ikwargs) + +errorType = ['$|| u - u_h||_{\\Omega,0}$', + '$||\\nabla u - \\nabla u_h||_{\\Omega,0}$', + '$||\\nabla^2 u - \\nabla^2 u_h||_{\\Omega,0}$'] +errorMatrix = bm.zeros((3, maxit), **fkwargs) +tmr.send('网格和pde生成时间') + +for i in range(maxit): + node = mesh.entity('node') + isCornerNode = bm.zeros(len(node),dtype=bm.bool, device=device) + for n in bm.array([[0,0],[1,0],[0,1],[1,1]], **fkwargs): + isCornerNode = isCornerNode | (bm.linalg.norm(node-n[None, :], axis=1)<1e-10) + + + + space = CmConformingFESpace2d(mesh, p, 2, isCornerNode) + + tmr.send(f'第{i}次空间生成时间') + + uh = space.function() + + bform = BilinearForm(space) + coef = 1 + integrator = MthLaplaceIntegrator(m=3, coef=1, q=p+4) + bform.add_integrator(integrator) + lform = LinearForm(space) + lform.add_integrator(ScalarSourceIntegrator(pde.source, q=p+4)) + + A = bform.assembly() + #print(space.number_of_global_dofs()) + F = lform.assembly() + tmr.send(f'第{i}次矩组装时间') + + + + gdof = space.number_of_global_dofs() + NDof[i] = 1/4/2**i + bc1 = DirichletBC(space, gd = ulist) + #import ipdb + #ipdb.set_trace() + A, F = bc1.apply(A, F) + tmr.send(f'第{i}次边界处理时间') + #A = A.to_scipy() + + #from numpy.linalg import cond + #print(gdof) + #print(cond(A.toarray())) + #A = coo_matrix(A) + #A = csr_matrix((A.values(), A.indices()),A.shape) + #uh[:] = bm.tensor(spsolve(A, F)) + uh[:] = spsolve(A, F, "scipy") + + #uh[:] = cg(A, F, maxiter=400000, atol=1e-14, rtol=1e-14) + tmr.send(f'第{i}次求解器时间') + + @barycentric + def ugval(p): + return space.grad_m_value(uh, p, 1) + + @barycentric + def ug2val(p): + return space.grad_m_value(uh, p, 2) + errorMatrix[0, i] = mesh.error(pde.solution, uh) + errorMatrix[1, i] = mesh.error(pde.gradient, ugval) + errorMatrix[2, i] = mesh.error(pde.hessian, ug2val) + if i < maxit-1: + mesh.uniform_refine(n=1) + tmr.send(f'第{i}次误差计算及网格加密时间') + +next(tmr) +print("最终误差",errorMatrix) +print("order : ", bm.log2(errorMatrix[0,:-1]/errorMatrix[0,1:])) +print("order : ", bm.log2(errorMatrix[1,:-1]/errorMatrix[1,1:])) +print("order : ", bm.log2(errorMatrix[2,:-1]/errorMatrix[2,1:])) + diff --git a/fealpy/fem/mlaplace_bernstein_integrator.py b/fealpy/fem/mlaplace_bernstein_integrator.py index 75ba9d774..6b363adef 100644 --- a/fealpy/fem/mlaplace_bernstein_integrator.py +++ b/fealpy/fem/mlaplace_bernstein_integrator.py @@ -105,8 +105,17 @@ def integrator(a): # a 为多重指标 symglambda = bm.einsum('ci,ci,i->c', glambda1, glambda2, num) idx22 = bm.broadcast_to(idx2[None, :], (l, l)) B1 = B * c1 * c2 - BB = B1[None, :, :] * symglambda[:, None, None] - bm.add_at(gmB, (slice(None), idx11, idx22), BB) + #BB = B1[None, :, :] * symglambda[:, None, None] + BB = bm.multiply(B1, symglambda[:, None, None]) + #bm.add_at(gmB, (slice(None), idx11, idx22), B1[None, :, :] * symglambda[:, None, None]) + #bm.add_at(gmB, (slice(None), idx11, idx22), BB) + #gmB[:, idx11, idx22] += B1[None, :, :] * symglambda[:, None, None] + gmB[:, idx11, idx22] += BB + del B1,BB + import gc + gc.collect() + del B + gc.collect() gmB = gmB * factorial(int(p)) * factorial(int(p)) return gmB diff --git a/fealpy/fem/mthlaplace_integrator.py b/fealpy/fem/mthlaplace_integrator.py index 60a92273f..3a83f7ab2 100644 --- a/fealpy/fem/mthlaplace_integrator.py +++ b/fealpy/fem/mthlaplace_integrator.py @@ -78,9 +78,10 @@ def assembly_without_numerical_integration(self, space: _FS): q = space.p+3 if self.q is None else self.q cmcoeff = space.coeff bgm = MLaplaceBernsteinIntegrator(m=m, q=q).assembly(space.bspace) - M = bm.einsum('cil,clm,cpm->cip', cmcoeff, bgm, cmcoeff) + #M = bm.einsum('cil,clm,cpm->cip', cmcoeff, bgm, cmcoeff) + M = cmcoeff @ bgm @ cmcoeff.transpose(0,2,1) + #import numpy as np + #np.save('Mcn8.npy', M) return M - - diff --git a/fealpy/fem/scalar_source_integrator.py b/fealpy/fem/scalar_source_integrator.py index 74b2158b9..612295b73 100644 --- a/fealpy/fem/scalar_source_integrator.py +++ b/fealpy/fem/scalar_source_integrator.py @@ -47,6 +47,6 @@ def assembly(self, space: _FS) -> TensorLike: f = self.source mesh = getattr(space, 'mesh', None) bcs, ws, phi, cm, index = self.fetch(space) - val = process_coef_func(f, bcs=bcs, mesh=mesh, etype='cell', index=index) + return linear_integral(phi, ws, cm, val, batched=self.batched) diff --git a/fealpy/functionspace/cm_conforming_fe_space.py b/fealpy/functionspace/cm_conforming_fe_space.py index d6ac8f17b..d6ea336f1 100644 --- a/fealpy/functionspace/cm_conforming_fe_space.py +++ b/fealpy/functionspace/cm_conforming_fe_space.py @@ -307,9 +307,9 @@ def grad_m_basis(self, bcs, m): return bm.einsum('cil, cqlg->cqig', coeff, bgmphi) - def boundary_interpolate(self, gD, uh, threshold=None, method="interp"): + def boundary_interpolate(self, gd, uh, threshold=None, method="interp"): ''' - @param gD : [right, tr], 第一个位置为方程的右端项,第二个位置为迹函数 + @param gd : [right, tr], 第一个位置为方程的右端项,第二个位置为迹函数 ''' #TODO 只处理的边界为 0 的情况 mesh = self.mesh @@ -338,10 +338,10 @@ def boundary_interpolate(self, gD, uh, threshold=None, method="interp"): nodefram[bdnidxmap[coridx]] = bm.tile(bm.eye(2,dtype=bm.float64, device=self.device), (len(coridx), 1, 1)) # 顶点自由度 - uh[n2id[:, 0]] = gD[0](node) + uh[n2id[:, 0]] = gd[0](node) k = 1; for r in range(1, 2*m+1): - val = gD[r](node) + val = gd[r](node) multiIndex = self.mesh.multi_index_matrix(r, 1) symidx, num = symmetry_index(2, r, device=self.device) @@ -367,7 +367,7 @@ def boundary_interpolate(self, gD, uh, threshold=None, method="interp"): b2l = self.bspace.bernstein_to_lagrange(p-r, 1) point = self.mesh.bc_to_point(bcs)[isBdEdge] if r==0: - ffval = gD[0](point) #(ldof, NE) + ffval = gd[0](point) #(ldof, NE) bcoeff = bm.einsum('el, il->ei', ffval, b2l) uh[e2id[:, k:l]] = bcoeff[:, 2*m+1-r: -2*m-1+r] else: @@ -379,13 +379,13 @@ def boundary_interpolate(self, gD, uh, threshold=None, method="interp"): #idx = self.mesh.multi_index_matrix(r, GD-1) #num = factorial(r)/bm.prod(factorial(idx), axis=1) - ffval = gD[r](point) #(ldof, NE, L), L 指的是分量个数,k 阶导有 k 个 + ffval = gd[r](point) #(ldof, NE, L), L 指的是分量个数,k 阶导有 k 个 bcoeff = bm.einsum('ej, elj, j, il->ei', nnn, ffval, num, b2l) uh[e2id[:, k:l]] = bcoeff[:, 2*m+1-r: -2*m-1+r] k = l l += p-4*m+r isDDof = self.is_boundary_dof(threshold=threshold) - uI = self.interpolation(gD) + uI = self.interpolation(gd) isDDofidx = bm.where(isDDof)[0] #uh[isDDof] = uI[isDDof] return uh, isDDof diff --git a/fealpy/functionspace/cm_conforming_fe_space3d.py b/fealpy/functionspace/cm_conforming_fe_space3d.py index 12a759e6e..1f6fa0553 100644 --- a/fealpy/functionspace/cm_conforming_fe_space3d.py +++ b/fealpy/functionspace/cm_conforming_fe_space3d.py @@ -262,14 +262,14 @@ def cell_to_dof(self): c2dof[:, ldof-cidof:] = c2id return c2dof - def boundary_interpolate(self, gD, uh, threshold=None, method="interp"): + def boundary_interpolate(self, gd, uh, threshold=None, method="interp"): isDDof = self.is_boundary_dof(threshold=threshold) - uI = self.interpolation(gD) + uI = self.interpolation(gd) uh[isDDof] = uI[isDDof] return uh, isDDof - def is_boundary_dof(self, threshold=None): #TODO:threshold 未实现 + def is_boundary_dof(self, threshold=None, method='interp'): #TODO:threshold 未实现 mesh = self.mesh m = self.m p = self.p @@ -669,7 +669,11 @@ def coefficient_matrix(self): T[:, iii, jjj] = bm.sum(Nv_sym*nv_sym*num[None, :], axis=1) coeff1[:, ndof*v+kk:ndof*v+kk+NSr2, ndof*v+kk:ndof*v+kk+NSr2] = T kk += NSr2 - coeff[:, :4*ndof] = bm.einsum('cji, cjk->cik',coeff1, coeff[:, :4*ndof]) + #coeff[:, :4*ndof] = bm.einsum('cji, cjk->cik',coeff1, coeff[:, :4*ndof]) + coeff[:, :4*ndof] = coeff1.transpose(0,2,1)@coeff[:, :4*ndof] + del coeff1 + import gc + gc.collect() # edge edof = self.number_of_internal_dofs('edge') @@ -705,13 +709,18 @@ def coefficient_matrix(self): 2**r) for j in range(r+1): coeff2[:, dof2num[edof_idxr[i]]-ndof*4, dof2num[edof_idxr[j]]-ndof*4] = num[j] * Ncoef_sym[:, symidx[j], None] - coeff[:, 4*ndof:4*ndof+6*edof] = bm.einsum('cji, cjk->cik',coeff2, coeff[:, 4*ndof:4*ndof+6*edof]) - return coeff[:, : , dof2num] + #coeff[:, 4*ndof:4*ndof+6*edof] = bm.einsum('cji, cjk->cik',coeff2, coeff[:, 4*ndof:4*ndof+6*edof]) + coeff[:, 4*ndof:4*ndof+6*edof] = coeff2.transpose(0,2,1)@coeff[:, 4*ndof:4*ndof+6*edof] + del coeff2 + gc.collect() + coeff = coeff[:,:,dof2num] + return coeff def basis(self, bcs, index=_S): coeff = self.coeff bphi = self.bspace.basis(bcs) - return bm.einsum('cil, cql -> cqi', coeff, bphi)[:,:,index] + #return bm.einsum('cil, cql -> cqi', coeff, bphi)[:,:,index] + return bphi @ (coeff.transpose(0,2,1)[index]) def grad_m_basis(self, bcs, m): coeff = self.coeff @@ -773,7 +782,6 @@ def interpolation(self, flist): # node - __import__('ipdb').set_trace() fI[n2id[:, 0]] = flist[0](node) k = 1 for r in range(1, 4*m+1): diff --git a/fealpy/mesh/mesh_base.py b/fealpy/mesh/mesh_base.py index f61ecdd17..87c2597a8 100644 --- a/fealpy/mesh/mesh_base.py +++ b/fealpy/mesh/mesh_base.py @@ -431,8 +431,8 @@ def error(self, u, v, q=3, power=2, celltype=False) -> TensorLike: v = v(ps) cm = self.entity_measure('cell') NC = self.number_of_cells() - if v.shape[-1] == NC: - v = bm.swapaxes(v, 0, -1) + #if v.shape[-1] == NC: + # v = bm.swapaxes(v, 0, -1) #f = bm.power(bm.abs(u - v), power) f = bm.abs(u - v)**power if len(f.shape) == 1: diff --git a/fealpy/pde/biharmonic_triharmonic_2d.py b/fealpy/pde/biharmonic_triharmonic_2d.py index 3d819bb5e..720f0a9a3 100644 --- a/fealpy/pde/biharmonic_triharmonic_2d.py +++ b/fealpy/pde/biharmonic_triharmonic_2d.py @@ -187,7 +187,7 @@ def __init__(self, u): def init_mesh(self, n=1, meshtype='poly'): mesh = TriangleMesh.from_box([0, 1, 0, 1], 4, 4) return mesh - + @cartesian def source(self, p): x = p[..., 0] y = p[..., 1] @@ -212,6 +212,7 @@ def gradient(self, p): val[..., 1] = self.uy(x, y) return val + @cartesian def hessian(self, p): x = p[..., 0] y = p[..., 1] From 1dcaf5ca0580b8677cc232a018700e8ca5733bb0 Mon Sep 17 00:00:00 2001 From: BenHBLiu Date: Thu, 28 Nov 2024 14:35:54 +0800 Subject: [PATCH 53/63] add MPA --- fealpy/opt/marine_predators_alg.py | 67 ++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 fealpy/opt/marine_predators_alg.py diff --git a/fealpy/opt/marine_predators_alg.py b/fealpy/opt/marine_predators_alg.py new file mode 100644 index 000000000..d5dc077e9 --- /dev/null +++ b/fealpy/opt/marine_predators_alg.py @@ -0,0 +1,67 @@ +from ..backend import backend_manager as bm +from ..typing import TensorLike, Index, _S +from .. import logger +from .optimizer_base import Optimizer +from .opt_function import levy +""" +Marine Predators Algorithm + +Reference: +~~~~~~~~~~ +Faramarzi A, Heidarinejad M, Mirjalili S, et al. +Marine Predators Algorithm: A nature-inspired metaheuristic. +Expert systems with applications, 2020, 152: 113377. + +""" +class MarinePredatorsAlg(Optimizer): + def __init__(self, option) -> None: + super().__init__(option) + + + def run(self): + options = self.options + x = options["x0"] + N = options["NP"] + fit = self.fun(x)[:, None] + MaxIT = options["MaxIters"] + dim = options["ndim"] + lb, ub = options["domain"] + gbest_index = bm.argmin(fit) + gbest = x[gbest_index] + gbest_f = fit[gbest_index] + NN = int(N / 2) + P = 0.5 + FADs = 0.2 + x_new = bm.zeros((N, dim)) + # curve = bm.zeros((1, MaxIT)) + for it in range(0, MaxIT): + CF = (1 - it / MaxIT) ** (2 * it / MaxIT) + if it <= MaxIT / 3: + RB = bm.random.randn(N, dim) + x_new = x + P * bm.random.rand(N, dim) * RB * (gbest - RB * x) + elif it > MaxIT / 3 and it <= 2 * MaxIT / 3: + RB = bm.random.randn(NN, dim) + x_new[0 : NN] = gbest + P * CF * RB * (RB * gbest - x[0 : NN]) + RL = 0.05 * levy(NN, dim, 1.5) + x_new[NN : N] = x[NN : N] + P * bm.random.rand(NN, dim) * RL * (gbest - RL * x[NN : N]) + else: + RL = 0.05 * levy(N, dim, 1.5) + x_new = gbest + P * CF * RL * (RL * gbest - x) + + x_new = x_new + (lb - x_new) * (x_new < lb) + (ub - x_new) * (x_new > ub) + fit_new = self.fun(x_new)[:, None] + mask = fit_new < fit + x, fit = bm.where(mask, x_new, x), bm.where(mask, fit_new, fit) + gbest_idx = bm.argmin(fit) + (gbest, gbest_f) = (x[gbest_idx], fit[gbest_idx]) if fit[gbest_idx] < gbest_f else (gbest, gbest_f) + if bm.random.rand(1) < FADs: + x = x + CF * ((lb + bm.random.rand(N, dim) * (ub - lb)) * (bm.random.rand(N, dim) < FADs)) + x = x + (lb - x) * (x < lb) + (ub - x) * (x > ub) + else: + x = x + (FADs * (1 - bm.random.rand(1)) + bm.random.rand(1)) * (x[bm.random.randint(0, N, (N,))] - x[bm.random.randint(0, N, (N,))]) + x = x + (lb - x) * (x < lb) + (ub - x) * (x > ub) + fit = self.fun(x)[:, None] + gbest_idx = bm.argmin(fit) + (gbest, gbest_f) = (x[gbest_idx], fit[gbest_idx]) if fit[gbest_idx] < gbest_f else (gbest, gbest_f) + # curve[0, it] = gbest_f + return gbest, gbest_f \ No newline at end of file From 4de84aa70075b6ba3582295a6e023243df7a4458 Mon Sep 17 00:00:00 2001 From: BenHBLiu Date: Thu, 28 Nov 2024 14:36:45 +0800 Subject: [PATCH 54/63] update --- fealpy/opt/__init__.py | 4 +++- test/opt/test_iopt_alg.py | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/fealpy/opt/__init__.py b/fealpy/opt/__init__.py index a06ebe799..1c6e3b287 100644 --- a/fealpy/opt/__init__.py +++ b/fealpy/opt/__init__.py @@ -20,4 +20,6 @@ from .Butterfly_opt_alg import ButterflyOptAlg from .exponential_trigonometric_opt_alg import ExponentialTrigonometricOptAlg from .differential_evolution import DifferentialEvolution -from .differentialted_creative_search import DifferentialtedCreativeSearch \ No newline at end of file +from .differentialted_creative_search import DifferentialtedCreativeSearch +from .cuckoo_quantum_particle_swarm_opt import CuckooQuantumParticleSwarmOpt +from .marine_predators_alg import MarinePredatorsAlg \ No newline at end of file diff --git a/test/opt/test_iopt_alg.py b/test/opt/test_iopt_alg.py index ceda3615e..7c3635e92 100644 --- a/test/opt/test_iopt_alg.py +++ b/test/opt/test_iopt_alg.py @@ -10,12 +10,13 @@ class TestIOptInterfaces: @pytest.mark.parametrize("backend", ['numpy', 'pytorch']) @pytest.mark.parametrize("data", iopt_data) - @pytest.mark.parametrize("NP", [100]) + @pytest.mark.parametrize("NP", [130]) def test_opt_alg(self, backend, data, NP): bm.set_backend(backend) lb, ub = data['domain'] x0 = initialize(NP, data['ndim'], ub, lb) option = opt_alg_options(x0, data['objective'], data['domain'], NP) + optimizer = MarinePredatorsAlg(option) # optimizer = HoneybadgerAlg(option) # optimizer = CrayfishOptAlg(option) # optimizer = QuantumParticleSwarmOpt(option) @@ -31,7 +32,7 @@ def test_opt_alg(self, backend, data, NP): # optimizer = ExponentialTrigonometricOptAlg(option) # optimizer = DifferentialEvolution(option) # optimizer = DifferentialtedCreativeSearch(option) - optimizer = CuckooQuantumParticleSwarmOpt(option) + # optimizer = CuckooQuantumParticleSwarmOpt(option) gbest, gbest_f = optimizer.run() if __name__ == "__main__": From e193c507916c448380a07b5d9210f5779f7ca945 Mon Sep 17 00:00:00 2001 From: Wangwenbinmath <1243702877@qq.com> Date: Thu, 28 Nov 2024 16:12:10 +0800 Subject: [PATCH 55/63] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=A7=BB=E5=8A=A8?= =?UTF-8?q?=E7=BD=91=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/mmpde/harmap_mmpde.py | 802 +++++++++++++++++++++----------------- 1 file changed, 447 insertions(+), 355 deletions(-) diff --git a/app/mmpde/harmap_mmpde.py b/app/mmpde/harmap_mmpde.py index f3f97866e..b346d680e 100644 --- a/app/mmpde/harmap_mmpde.py +++ b/app/mmpde/harmap_mmpde.py @@ -1,4 +1,5 @@ from fealpy.backend import backend_manager as bm +from typing import Any, Union ,Optional from fealpy.typing import TensorLike from fealpy.mesh import TriangleMesh from fealpy.mesh import TetrahedronMesh @@ -7,40 +8,66 @@ from fealpy.mesh import TetrahedronMesh as THM from fealpy.functionspace import LagrangeFESpace from fealpy.fem import (BilinearForm - ,ScalarDiffusionIntegrator - ,LinearForm - ,ScalarSourceIntegrator - ,DirichletBC) + ,ScalarDiffusionIntegrator + ,LinearForm + ,ScalarSourceIntegrator + ,DirichletBC) from scipy.sparse.linalg import spsolve +from scipy.sparse.linalg import spsolve as spsolve1 from scipy.integrate import solve_ivp -from scipy.sparse import csr_matrix,spdiags,hstack,block_diag,bmat,diags +from scipy.sparse import csr_matrix,spdiags,block_diag,bmat from sympy import * -from typing import Union + class LogicMesh(): - def __init__(self , mesh: Union[TriangleMesh,TetrahedronMesh]) -> None: - """ - mesh : 物理网格 + def __init__(self , mesh: Union[TriangleMesh,TetrahedronMesh], + Vertex_idx : TensorLike, + Bdinnernode_idx : TensorLike, + Arrisnode_idx : Optional[TensorLike] = None, + sort_BdNode_idx : Optional[TensorLike] = None) -> None: + """ + @param mesh: 物理网格 + @param Vertex_idx : 角点全局编号 + @param Bdinnernode_idx : 面内点全局编号 + @param Arrisnode_idx : 棱内点全局编号 + @param sort_BdNode_idx : 排序后的边界点全局编号 """ self.mesh = mesh self.TD = mesh.top_dimension() self.node = mesh.entity('node') self.cell = mesh.entity('cell') self.edge = mesh.entity('edge') - + self.BdNodeidx = mesh.boundary_node_index() self.BdFaceidx = mesh.boundary_face_index() + self.Vertex_idx = Vertex_idx + self.Bdinnernode_idx = Bdinnernode_idx + self.sort_BdNode_idx = sort_BdNode_idx + + self.logic_mesh = self.get_logic_mesh() + self.logic_node = self.logic_mesh.entity('node') + self.isconvex = self.is_convex() # 新网格下没有该方法 - if self.TD ==2: + if self.TD == 2: self.node2edge = TM(self.node, self.cell).ds.node_to_edge() - else: - self.node2face = self.node_to_face() - self.local_n2f_norm,self.normal = self.get_basic_information() + self.Bi_Lnode_normal = self.get_normal_information(self.logic_mesh) + self.Bi_Pnode_normal = self.get_normal_information(self.mesh) + + if self.TD == 3: + if Arrisnode_idx is None: + raise ValueError('TD = 3, you must give the Arrisnode_idx') + self.Arrisnode_idx = Arrisnode_idx + self.Bi_Lnode_normal, self.Ar_Lnode_normal = self.get_normal_information(self.logic_mesh) + self.isconvex = self.is_convex() + if self.isconvex == False: + if sort_BdNode_idx is None: + raise ValueError('The boundary is not convex, you must give the sort_BdNode') + self.roll_SortBdNode() def get_logic_mesh(self) : if self.TD == 2: if self.is_convex(): - logic_mesh = TriangleMesh(self.node.copy(),self.cell) + logic_mesh = TriangleMesh(self.node.copy(),self.cell) # 更新 else: logic_node = self.get_logic_node() logic_cell = self.cell @@ -52,15 +79,13 @@ def get_logic_mesh(self) : else: logic_mesh = TetrahedronMesh(self.node.copy(),self.cell) return logic_mesh - + def is_convex(self): """ - 判断边界是否是凸的 + @brief is_convex : 判断边界是否是凸的 """ from scipy.spatial import ConvexHull - ln2f_norm = self.local_n2f_norm - vertex_idx = self.BdNodeidx[ln2f_norm[:,-1] >= 0] - intnode = self.node[vertex_idx] + intnode = self.node[self.Vertex_idx] hull = ConvexHull(intnode) return len(intnode) == len(hull.vertices) @@ -81,129 +106,90 @@ def node_to_face(self): # 作为三维网格的辅助函数 ), shape=(NN, NF)) return node2face - def sort_bdnode_and_bdface(self): + def get_normal_information(self,mesh:Union[TriangleMesh,TetrahedronMesh]): """ - 对二维边界点和边界面进行排序 + @brief get_normal_information: 获取边界点法向量 """ - BdNodeidx = self.BdNodeidx - BdEdgeidx = self.BdFaceidx - node = self.node - edge = self.edge - # 对边界边和点进行排序 - node2edge = self.node2edge - bdnode2edge = node2edge[BdNodeidx][:,BdEdgeidx] - i,j = bm.nonzero(bdnode2edge) - bdnode2edge = j.reshape(-1,2) - glob_bdnode2edge = bm.zeros_like(node,dtype=bm.int32) - glob_bdnode2edge = bm.set_at(glob_bdnode2edge,BdNodeidx,BdEdgeidx[bdnode2edge]) - - sort_glob_bdedge_idx_list = [] - sort_glob_bdnode_idx_list = [] - - start_bdnode_idx = BdNodeidx[0] - sort_glob_bdnode_idx_list.append(start_bdnode_idx) - current_node_idx = start_bdnode_idx - - for i in range(bdnode2edge.shape[0]): - if edge[glob_bdnode2edge[current_node_idx,0],1] == current_node_idx: - next_edge_idx = glob_bdnode2edge[current_node_idx,1] - else: - next_edge_idx = glob_bdnode2edge[current_node_idx,0] - sort_glob_bdedge_idx_list.append(next_edge_idx) - next_node_idx = edge[next_edge_idx,1] - # 处理空洞区域 - if next_node_idx == start_bdnode_idx: - if i < bdnode2edge.shape[0] - 1: - remian_bdnode_idx = list(set(BdNodeidx)-set(sort_glob_bdnode_idx_list)) - start_bdnode_idx = remian_bdnode_idx[0] - next_node_idx = start_bdnode_idx - else: - # 闭环跳出循环 - break - sort_glob_bdnode_idx_list.append(next_node_idx) - current_node_idx = next_node_idx - return bm.array(sort_glob_bdnode_idx_list,dtype=bm.int32),\ - bm.array(sort_glob_bdedge_idx_list,dtype=bm.int32) - - def get_basic_information(self): - """ - 返回逻辑网格的基本信息 - """ - BdNodeidx = self.BdNodeidx + Bdinnernode_idx = self.Bdinnernode_idx BdFaceidx = self.BdFaceidx if self.TD == 3: + Arrisnode_idx = self.Arrisnode_idx node2face = self.node_to_face() + Ar_node2face = node2face[Arrisnode_idx][:,BdFaceidx] + i0 , j0 = bm.nonzero(Ar_node2face) + bdfun0 = mesh.face_unit_normal(index=BdFaceidx[j0]) + normal0,inverse0 = bm.unique(bdfun0,return_inverse=True ,axis = 0) + _,index0,counts0 = bm.unique(i0,return_index=True,return_counts=True) + maxcount = bm.max(counts0) + mincount = bm.min(counts0) + Ar_node2normal_idx = -bm.ones((len(Arrisnode_idx),maxcount),dtype=bm.int32) + Ar_node2normal_idx = bm.set_at(Ar_node2normal_idx, + (slice(None),slice(mincount)), + inverse0[index0[:,None]+bm.arange(mincount)]) + for i in range(maxcount-mincount): + isaimnode = counts0 > mincount+i + Ar_node2normal_idx = bm.set_at(Ar_node2normal_idx,(isaimnode,mincount+i) , + inverse0[index0[isaimnode]+mincount+i]) + Ar_node2normal_idx = bm.apply_along_axis(lambda x: bm.unique(x[x>=0]) + ,axis=1,arr=Ar_node2normal_idx) + Ar_node2normal = normal0[Ar_node2normal_idx] + elif self.TD == 2: + node2face = self.node2edge + + Bi_node2face = node2face[Bdinnernode_idx][:,BdFaceidx] + i1 , j1 = bm.nonzero(Bi_node2face) + bdfun1 = mesh.face_unit_normal(index=BdFaceidx[j1]) + _,index1 = bm.unique(i1,return_index=True) + Bi_node_normal = bdfun1[index1] + if self.TD == 2: + return Bi_node_normal else: - mesh = TM(self.node,self.cell) - node2face = mesh.ds.node_to_face() - BdNodeidx,BdFaceidx = self.sort_bdnode_and_bdface() - self.BdNodeidx = BdNodeidx - - bd_node2face = node2face[BdNodeidx][:,BdFaceidx] - i , j = bm.nonzero(bd_node2face) - - bdfun = self.mesh.face_unit_normal(index=BdFaceidx[j]) - normal = bm.unique(bdfun ,axis = 0) - K = bm.arange(len(normal),dtype = bm.int32) - _,index = bm.unique(i,return_index = True) - index = bm.concatenate((index , [j.shape[0]])) - - node2face_normal = -bm.ones((BdNodeidx.shape[0],self.TD),dtype=bm.int32) - for n in range(BdNodeidx.shape[0]): - b = bm.arange(index[n],index[n+1]) - lface_normal = bm.unique(bdfun[b],axis = 0) - num_of_lface_normal = lface_normal.shape[0] - if num_of_lface_normal == 1: - tag = bm.all(normal == lface_normal,axis = 1)@K - node2face_normal[n,0] = tag - elif num_of_lface_normal == 2: - tag = [bm.all(normal == lface_normal[0],axis=1), - bm.all(normal == lface_normal[1],axis=1)]@K - node2face_normal[n,:2] = tag - else: - # 无关几个面,只取前三个 - tag = [bm.all(normal == lface_normal[0],axis=1), - bm.all(normal == lface_normal[1],axis=1), - bm.all(normal == lface_normal[2],axis=1)]@K - node2face_normal[n,:3] = tag - return node2face_normal,normal + return Bi_node_normal, Ar_node2normal + def roll_SortBdNode(self): + """ + 对齐边界点与角点 + """ + sBdnodeidx = self.sort_BdNode_idx + Vertexidx = self.Vertex_idx + if sBdnodeidx is not None and sBdnodeidx[0] != Vertexidx[0]: + K = bm.where(sBdnodeidx[:,None] == Vertexidx)[0][0] + self.sort_BdNode_idx = bm.roll(sBdnodeidx,-K) + def get_boundary_condition(self,p) -> TensorLike: """ - 逻辑网格的边界条件,为边界点的集合 + 逻辑网格的边界条件 """ node = self.node - local_n2f_norm = self.local_n2f_norm - Vertexidx = self.BdNodeidx[local_n2f_norm[:,-1] >= 0] - physics_domain = node[Vertexidx] + sBdNodeidx = self.sort_BdNode_idx + Vertexidx = self.Vertex_idx + physics_domain = node[Vertexidx] num_sides = physics_domain.shape[0] angles = bm.linspace(0,2*bm.pi,num_sides,endpoint=False) - logic_domain = bm.stack([bm.cos(angles),bm.sin(angles)],axis=1) - logic_bdnode = bm.zeros_like(node,dtype=bm.float64) - logic_bdnode = bm.set_at(logic_bdnode,Vertexidx,logic_domain) - Pside_length = bm.linalg.norm(bm.roll(physics_domain,-1,axis=0) - - physics_domain,axis=1) - BDNN = len(self.BdNodeidx) - K = bm.arange(BDNN,dtype=bm.int32)[local_n2f_norm[:,-1] >= 0] - - for i in range(num_sides): - G = bm.arange(K[i]+1,K[i+1] if i < num_sides-1 else BDNN) - side_node = node[self.BdNodeidx[G]] - side_part_length = bm.linalg.norm(side_node - physics_domain[i] - ,axis=1) - rate = (side_part_length/Pside_length[i]).reshape(-1,1) - - logic_bdnode = bm.set_at(logic_bdnode,self.BdNodeidx[G],(1-rate)*logic_domain[i]\ - + rate*logic_domain[(i+1)%num_sides]) - - map = [] - for node_p in p: - idx = bm.where((node == node_p).all(axis=1))[0][0] - map.append(idx) + Pside_vector = bm.roll(physics_domain,-1,axis=0) - physics_domain + Lside_vector = bm.roll(logic_domain,-1,axis=0) - logic_domain + Pside_length = bm.linalg.norm(Pside_vector,axis=1) + Lside_length = bm.linalg.norm(Lside_vector,axis=1) + rate = Lside_length / Pside_length + theta = bm.arctan2(Lside_vector[:,1],Lside_vector[:,0]) -\ + bm.arctan2(Pside_vector[:,1],Pside_vector[:,0]) + A = rate[:,None,None] * (bm.array([[bm.cos(theta),bm.sin(theta)], + [-bm.sin(theta),bm.cos(theta)]],dtype=bm.float64)).T + + K = bm.where(sBdNodeidx[:,None] == Vertexidx)[0] + K = bm.concatenate([K,[len(sBdNodeidx)]]) + + A_repeat = bm.repeat(A,K[1:]-K[:-1],axis=0) + PVertex_repeat = bm.repeat(physics_domain,K[1:]-K[:-1],axis=0) + LVertex_repeat = bm.repeat(logic_domain,K[1:]-K[:-1],axis=0) + Aim_vector = (A_repeat@((node[sBdNodeidx]-PVertex_repeat)[:,:,None])).reshape(-1,2) + logic_bdnode = bm.set_at(logic_bdnode,sBdNodeidx,Aim_vector+LVertex_repeat) + + map = bm.where((node[:,None] == p).all(axis=2))[0] return logic_bdnode[map] def get_logic_node(self) -> TensorLike: @@ -214,7 +200,6 @@ def get_logic_node(self) -> TensorLike: bdc = self.get_boundary_condition p = 1 # 有限元空间次数 space = LagrangeFESpace(mesh, p=p) - bform = BilinearForm(space) bform.add_integrator(ScalarDiffusionIntegrator(q=p+1)) A = bform.assembly() @@ -223,22 +208,25 @@ def get_logic_node(self) -> TensorLike: F = lform.assembly() bc0 = DirichletBC(space = space, gd = lambda p : bdc(p)[:,0]) bc1 = DirichletBC(space = space, gd = lambda p : bdc(p)[:,1]) - uh0 = bm.zeros(space.number_of_global_dofs(),dtype=bm.float64) - uh1 = bm.zeros(space.number_of_global_dofs(),dtype=bm.float64) + uh0 = space.function() + uh1 = space.function() A1, F1 = bc0.apply(A, F, uh0) A2, F2 = bc1.apply(A, F, uh1) - uh0 = bm.set_at(uh0 , slice(None), spsolve(csr_matrix(A1.toarray()), F1)) - uh1 = bm.set_at(uh1 , slice(None), spsolve(csr_matrix(A2.toarray()), F2)) + uh0 = bm.set_at(uh0 , slice(None), spsolve(A1, F1 , solver="scipy")) + uh1 = bm.set_at(uh1 , slice(None), spsolve(A2, F2 , solver="scipy")) logic_node = bm.stack([uh0,uh1],axis=1) return logic_node - class Harmap_MMPDE(LogicMesh): def __init__(self, - mesh:TriangleMesh , + mesh:Union[TriangleMesh,TetrahedronMesh] , uh:TensorLike, pde , beta :float , + Vertex_idx : TensorLike, + Bdinnernode_idx : TensorLike, + Arrisnode_idx : Optional[TensorLike] = None, + sort_BdNode_idx : Optional[TensorLike] = None, alpha = 0.5, mol_times = 1 , redistribute = True) -> None: @@ -251,20 +239,24 @@ def __init__(self, mol_times : 磨光次数 redistribute : 是否预处理边界节点 """ - super().__init__(mesh) + super().__init__(mesh = mesh, + Vertex_idx = Vertex_idx, + Bdinnernode_idx = Bdinnernode_idx, + Arrisnode_idx = Arrisnode_idx, + sort_BdNode_idx = sort_BdNode_idx) self.uh = uh self.pde = pde self.beta = beta self.alpha = alpha self.mol_times = mol_times - self.NN = mesh.number_of_nodes() self.BDNN = len(self.BdNodeidx) + self.cm = mesh.entity_measure('cell') # 新网格下没有该方法 if self.TD == 2: self.node2cell = TM(self.node, self.cell).ds.node_to_cell() - else: + elif self.TD == 3: self.node2cell = THM(self.node, self.cell).ds.node_to_cell() self.isBdNode = mesh.boundary_node_flag() self.redistribute = redistribute @@ -272,13 +264,11 @@ def __init__(self, self.W = bm.array([[0,1],[-1,0]],dtype=bm.int32) self.localEdge = bm.array([[1,2],[2,0],[0,1]],dtype=bm.int32) - self.logic_mesh = self.get_logic_mesh() - self.logic_node = self.logic_mesh.entity('node') - self.isconvex = self.is_convex() - self.star_measure,self.i,self.j = self.get_star_measure() - self.G = self.get_control_function(beta,mol_times) + self.G = self.get_control_function(self.beta , self.mol_times) self.A , self.b = self.get_linear_constraint() + if redistribute and sort_BdNode_idx is None: + raise ValueError('redistributing boundary , you must give the sort_BdNode') def get_star_measure(self)->TensorLike: """ @@ -289,178 +279,161 @@ def get_star_measure(self)->TensorLike: bm.add_at(star_measure , i , self.cm[j]) return star_measure,i,j - def get_control_function(self,beta, mol_times): + def get_control_function(self,beta:float,mol_times:int): """ - 计算控制函数 - beta : 控制函数的参数 - mol_times : 磨光次数 + @brief 计算控制函数 + @param beta: float 控制函数的参数 + @param mol_times: int 磨光次数 """ - node = self.node cell = self.cell cm = self.cm gphi = self.mesh.grad_lambda() - guh_incell = bm.sum(self.uh[self.cell,None] * gphi,axis=1) - - M_incell = bm.sqrt(1 + beta *bm.sum( guh_incell**2,axis=1)) - M = bm.zeros(node.shape[0],dtype=bm.float64) - if mol_times == 0: - bm.add_at(M , self.i , (cm *M_incell)[self.j]) - else: + guh_incell = bm.sum(self.uh[cell,None] * gphi,axis=1) + max_norm_guh = bm.max(bm.linalg.norm(guh_incell,axis=1)) + M_incell = bm.sqrt(1 +beta *bm.sum( guh_incell**2,axis=1)/max_norm_guh) + M = bm.zeros(self.NN,dtype=bm.float64) + bm.add_at(M , self.i , (cm *M_incell)[self.j]) + M /= self.star_measure + if mol_times > 0: for k in range(mol_times): + M = bm.zeros(self.NN,dtype=bm.float64) bm.add_at(M , self.i , (cm *M_incell)[self.j]) M /= self.star_measure M_incell = bm.mean(M[cell],axis=1) return 1/M_incell - - def get_stiff_matrix(self): + + def get_stiff_matrix(self,mesh:Union[TriangleMesh,TetrahedronMesh],G:TensorLike): """ - 组装刚度矩阵 + @brief 组装刚度矩阵 + @param mesh: 物理网格 + @param G: 控制函数 """ - mesh = self.mesh q = 3 - qf = mesh.integrator(q) + cm = mesh.entity_measure('cell') + qf = mesh.quadrature_formula(q) bcs, ws = qf.get_quadrature_points_and_weights() space = LagrangeFESpace(mesh, p=1) gphi = space.grad_basis(bcs) cell2dof = space.cell_to_dof() GDOF = space.number_of_global_dofs() - - H = bm.einsum('q , cqid , c ,cqjd, c -> cij ',ws, gphi ,self.G , gphi, self.cm) + + H = bm.einsum('q , cqid , c ,cqjd, c -> cij ',ws, gphi ,G , gphi, cm) I = bm.broadcast_to(cell2dof[:, :, None], shape=H.shape) J = bm.broadcast_to(cell2dof[:, None, :], shape=H.shape) H = csr_matrix((H.flat, (I.flat, J.flat)), shape=(GDOF, GDOF)) - - H11 = H[~self.isBdNode][:, ~self.isBdNode] - H12 = H[~self.isBdNode][:, self.isBdNode] - H21 = H[self.isBdNode][:, ~self.isBdNode] - H22 = H[self.isBdNode][:, self.isBdNode] - return H11,H12,H21,H22 + return H def get_linear_constraint(self): """ - 组装线性约束 + @brief 组装线性约束 """ logic_node = self.logic_node NN = self.NN BDNN = self.BDNN - isBdNode = self.isBdNode BdNodeidx = self.BdNodeidx - local_n2f_norm = self.local_n2f_norm - normal = self.normal + Vertex_idx = self.Vertex_idx + Bdinnernode_idx = self.Bdinnernode_idx + Binnorm = self.Bi_Lnode_normal + logic_Bdinnode = logic_node[Bdinnernode_idx] + logic_Vertex = logic_node[Vertex_idx] if self.TD == 2: - isBdinnernode = local_n2f_norm[:,1] < 0 - isVertex = ~isBdinnernode b = bm.zeros(NN,dtype=bm.float64) - logic_Bdnode = logic_node[BdNodeidx] - b_val0 = bm.sum(logic_Bdnode* - normal[local_n2f_norm[:,0]],axis=1) - b_val1 = bm.sum(logic_Bdnode[isVertex]* - normal[local_n2f_norm[isVertex,1]],axis=1) - b = bm.set_at(b,BdNodeidx,b_val0) - b = bm.concatenate([b[isBdNode],b_val1]) - - # 处理排序后的边界节点的顺序错位 + b_val0 = bm.sum(logic_Bdinnode*Binnorm,axis=1) + b_val1 = logic_Vertex[:,0] + b_val2 = logic_Vertex[:,1] + b = bm.set_at(b , Bdinnernode_idx , b_val0) + b = bm.set_at(b , Vertex_idx , b_val1)[BdNodeidx] + b = bm.concatenate([b,b_val2]) + A1_diag = bm.zeros(NN , dtype=bm.float64) A2_diag = bm.zeros(NN , dtype=bm.float64) - A1_diag = bm.set_at(A1_diag,BdNodeidx, - normal[local_n2f_norm[:,0]][:,0])[isBdNode] - A2_diag = bm.set_at(A2_diag,BdNodeidx, - normal[local_n2f_norm[:,0]][:,1])[isBdNode] - A1 = spdiags(A1_diag ,0 , BDNN , BDNN,format='csr') - A2 = spdiags(A2_diag ,0 , BDNN , BDNN,format='csr') - - VNN = (isVertex).sum() - Vbd1_diag = normal[local_n2f_norm[isVertex,1]][:,0] - Vbd2_diag = normal[local_n2f_norm[isVertex,1]][:,1] - Vbd_constraint1 = bm.zeros((VNN,NN),dtype=bm.float64) - Vbd_constraint2 = bm.zeros((VNN,NN),dtype=bm.float64) - K = BdNodeidx[isVertex] - Vbd_constraint1 = bm.set_at(Vbd_constraint1, - (bm.arange(VNN),K),Vbd1_diag) - Vbd_constraint2 = bm.set_at(Vbd_constraint2, - (bm.arange(VNN),K),Vbd2_diag) - - A = bmat([[A1,A2],[Vbd_constraint1[:,isBdNode], - Vbd_constraint2[:,isBdNode]]],format='csr') - - elif self.TD == 3: - # 此处为边界局部信息,非全局 - isBdinnernode = local_n2f_norm[:,1] < 0 - isArrisnode = (local_n2f_norm[:,1] >= 0) & (local_n2f_norm[:,2] < 0) - isVertex = local_n2f_norm[:,2] >= 0 - - logic_Bdnode = logic_node[BdNodeidx] - b_val0 = bm.sum(logic_Bdnode*normal[local_n2f_norm[:,0]],axis=1) - b_val1 = bm.sum(logic_Bdnode[~isBdinnernode]* - normal[local_n2f_norm[~isBdinnernode,1]],axis=1) - b_val2 = bm.sum(logic_Bdnode[isVertex]* - normal[local_n2f_norm[isVertex,2]],axis=1) - b = bm.concatenate([b_val0,b_val1,b_val2]) - - A1_diag = normal[local_n2f_norm[:,0]][:,0] - A2_diag = normal[local_n2f_norm[:,0]][:,1] - A3_diag = normal[local_n2f_norm[:,0]][:,2] - - A1 = spdiags(A1_diag ,0 , BDNN , BDNN,format='csr') - A2 = spdiags(A2_diag ,0 , BDNN , BDNN,format='csr') - A3 = spdiags(A3_diag ,0 , BDNN , BDNN,format='csr') - BDBDNN = (~isBdinnernode).sum() - K1 = bm.arange(BDNN,dtype=bm.int32)[~isBdinnernode] - Bdbd1_diag = normal[local_n2f_norm[~isBdinnernode,1]][:,0] - Bdbd2_diag = normal[local_n2f_norm[~isBdinnernode,1]][:,1] - Bdbd3_diag = normal[local_n2f_norm[~isBdinnernode,1]][:,2] - Bdbd_constraint1 = bm.zeros((BDBDNN,BDNN),dtype=bm.float64) - Bdbd_constraint2 = bm.zeros((BDBDNN,BDNN),dtype=bm.float64) - Bdbd_constraint3 = bm.zeros((BDBDNN,BDNN),dtype=bm.float64) - Bdbd_constraint1 = bm.set_at(Bdbd_constraint1, - (bm.arange(BDBDNN),K1),Bdbd1_diag) - Bdbd_constraint2 = bm.set_at(Bdbd_constraint2, - (bm.arange(BDBDNN),K1),Bdbd2_diag) - Bdbd_constraint3 = bm.set_at(Bdbd_constraint3, - (bm.arange(BDBDNN),K1),Bdbd3_diag) - VNN = isVertex.sum() - K2 = bm.arange(BDNN,dtype=bm.int32)[isVertex] - Vbd1_diag = normal[local_n2f_norm[isVertex,2]][:,0] - Vbd2_diag = normal[local_n2f_norm[isVertex,2]][:,1] - Vbd3_diag = normal[local_n2f_norm[isVertex,2]][:,2] - Vertex_constraint1 = bm.zeros((VNN,BDNN),dtype=bm.float64) - Vertex_constraint2 = bm.zeros((VNN,BDNN),dtype=bm.float64) - Vertex_constraint3 = bm.zeros((VNN,BDNN),dtype=bm.float64) - Vertex_constraint1 = bm.set_at(Vertex_constraint1, - (bm.arange(VNN),K2),Vbd1_diag) - Vertex_constraint2 = bm.set_at(Vertex_constraint2, - (bm.arange(VNN),K2),Vbd2_diag) - Vertex_constraint3 = bm.set_at(Vertex_constraint3, - (bm.arange(VNN),K2),Vbd3_diag) - A = bmat([[A1,A2,A3],[Bdbd_constraint1,Bdbd_constraint2,Bdbd_constraint3], - [Vertex_constraint1,Vertex_constraint2,Vertex_constraint3]],format='csr') - - return A,b - - def solve(self): - """ - 交替求解逻辑网格点 - logic_node : 新逻辑网格点 - vector_field : 逻辑网格点移动向量场 - """ + A1_diag = bm.set_at(A1_diag , Bdinnernode_idx , Binnorm[:,0]) + A2_diag = bm.set_at(A2_diag , Bdinnernode_idx , Binnorm[:,1]) + A1_diag = bm.set_at(A1_diag , Vertex_idx , 1)[BdNodeidx] + A2_diag = bm.set_at(A2_diag , Vertex_idx , 0)[BdNodeidx] + A1 = spdiags(A1_diag,0,BDNN,BDNN , format='csr') + A2 = spdiags(A2_diag,0,BDNN,BDNN , format='csr') + VNN = len(Vertex_idx) + Vbd_constraint1 = csr_matrix((VNN, BDNN), dtype=bm.float64) + data = bm.ones(VNN,dtype=bm.float64) + Vbd_constraint2 = csr_matrix((data, (bm.arange(VNN), Vertex_idx)), shape=(VNN, NN)) + A = bmat([[A1,A2],[Vbd_constraint1,Vbd_constraint2[:,BdNodeidx]]],format='csr') + + elif self.TD == 3: + Arrisnode_idx = self.Arrisnode_idx + Arnnorm = self.Ar_Lnode_normal + logic_Arnode = logic_node[Arrisnode_idx] + ArNN = len(Arrisnode_idx) + VNN = len(Vertex_idx) + + b = bm.zeros(NN,dtype=bm.float64) + b_val0 = bm.sum(logic_Bdinnode*Binnorm,axis=1) + b_val1 = bm.sum(logic_Arnode*Arnnorm[:,0,:],axis=1) + b_val2 = bm.sum(logic_Arnode*Arnnorm[:,1,:],axis=1) + b_val3 = logic_Vertex[:,0] + b_val4 = logic_Vertex[:,1] + b_val5 = logic_Vertex[:,2] + bm.add_at(b_val2 , slice(VNN) , b_val4) + b = bm.set_at(b , Bdinnernode_idx , b_val0) + b = bm.set_at(b , Arrisnode_idx , b_val1) + b = bm.set_at(b , Vertex_idx , b_val3) + b = bm.concatenate([b,b_val2,b_val5]) + + index0 = NN * bm.arange(self.TD) + Bdinnernode_idx[:,None] + index1 = NN * bm.arange(self.TD) + Arrisnode_idx[:,None] + index2 = NN * bm.arange(self.TD) + BdNodeidx[:,None] + + A_diag = bm.zeros(self.TD * NN , dtype=bm.float64) + A_diag = bm.set_at(A_diag , index0 , Binnorm) + A_diag = bm.set_at(A_diag , index1 , Arnnorm[:,0,:]) + A_diag = bm.set_at(A_diag , Vertex_idx , 1) + + A1 = spdiags(A_diag[:NN][BdNodeidx],0, BDNN ,BDNN , format='csr') + A2 = spdiags(A_diag[NN:2*NN][BdNodeidx],0, BDNN ,BDNN , format='csr') + A3 = spdiags(A_diag[2*NN:][BdNodeidx],0, BDNN ,BDNN , format='csr') + + rol_Ar = bm.repeat(bm.arange(ArNN)[None,:],3,axis=0).flat + rol_Ar = bm.concatenate([rol_Ar,bm.arange(VNN)]) + cow_Ar = bm.concatenate([index1.T.flat,Vertex_idx+NN]) + data_Ar = bm.concatenate([Arnnorm[:,1,:].T.flat,bm.ones(VNN,bm.float64)]) + Ar_constraint = csr_matrix((data_Ar,(rol_Ar, cow_Ar)),shape=(ArNN,3*NN)) + Vertex_constraint = csr_matrix((bm.ones(VNN,dtype=bm.float64), + (bm.arange(VNN),Vertex_idx + 2 * NN)),shape=(VNN,3*NN)) + + Ar_constraint = Ar_constraint[:, (index2.T).flat] + Vertex_constraint = Vertex_constraint[:, (index2.T).flat] + A_part = bmat([[A1,A2,A3]],format='csr') + A = bmat([[A_part],[Ar_constraint],[Vertex_constraint]],format='csr') + return A,b + + def solve_move_LogicNode(self): + """ + @brief 交替求解逻辑网格点 + process_logic_node : 新逻辑网格点 + move_vector_field : 逻辑网格点移动向量场 + """ + from scipy.sparse.linalg import spsolve isBdNode = self.isBdNode - logic_node = self.logic_node - H11,H12,H21,H22 = self.get_stiff_matrix() - A,b= self.A,self.b + H = self.get_stiff_matrix(self.mesh , self.G) + H11 = H[~self.isBdNode][:, ~self.isBdNode] + H12 = H[~self.isBdNode][:, self.isBdNode] + H21 = H[self.isBdNode][:, ~self.isBdNode] + H22 = H[self.isBdNode][:, self.isBdNode] + A,b= self.A,self.b # 获得一个初始逻辑网格点的拷贝 - init_logic_node = logic_node.copy() - process_logic_node = logic_node.copy() + init_logic_node = self.logic_node.copy() + process_logic_node = self.logic_node.copy() if self.redistribute: process_logic_node = self.redistribute_boundary() # 移动逻辑网格点 f1 = -H12@process_logic_node[isBdNode,0] f2 = -H12@process_logic_node[isBdNode,1] - - move_innerlogic_node_x = spsolve(H11, f1) - move_innerlogic_node_y = spsolve(H11, f2) + + move_innerlogic_node_x = spsolve1(H11, f1 ) + move_innerlogic_node_y = spsolve1(H11, f2 ) process_logic_node = bm.set_at(process_logic_node , ~isBdNode, bm.stack([move_innerlogic_node_x, move_innerlogic_node_y],axis=1)) @@ -477,13 +450,15 @@ def solve(self): process_logic_node = bm.set_at(process_logic_node , isBdNode, bm.stack((move_bdlogic_node[:H22.shape[0]], move_bdlogic_node[H22.shape[0]:]),axis=1)) + + move_vector_field = init_logic_node - process_logic_node + return process_logic_node,move_vector_field - vector_field = init_logic_node - process_logic_node - return process_logic_node,vector_field - - def get_physical_node(self,vector_field,logic_node_move): + def get_physical_node(self,move_vertor_field,logic_node_move): """ - 计算物理网格点 + @brief 计算物理网格点 + @param move_vertor_field: 逻辑网格点移动向量场 + @param logic_node_move: 移动后的逻辑网格点 """ node = self.node cell = self.cell @@ -491,22 +466,19 @@ def get_physical_node(self,vector_field,logic_node_move): A = (node[cell[:,1:]] - node[cell[:,0,None]]).transpose(0,2,1) B = (logic_node_move[cell[:,1:]] - logic_node_move[cell[:,0,None]]).transpose(0,2,1) - grad_x_incell = (A@bm.linalg.inv(B)) * cm[:,None,None] - grad_x = bm.zeros((self.NN,2,2),dtype=bm.float64) bm.add_at(grad_x , self.i , grad_x_incell[self.j]) grad_x /= self.star_measure[:,None,None] - delta_x = (grad_x @ vector_field[:,:,None] ).reshape(-1,2) + delta_x = (grad_x @ move_vertor_field[:,:,None]).reshape(-1,2) + + Bin_tangent = self.Bi_Pnode_normal @ self.W + Bdinnernode_idx = self.Bdinnernode_idx + dot = bm.sum(Bin_tangent * delta_x[Bdinnernode_idx],axis=1) + delta_x = bm.set_at(delta_x,Bdinnernode_idx,dot[:,None] * Bin_tangent) - local_n2f_norm = self.local_n2f_norm - bdnode_normal = self.normal[local_n2f_norm[:,0]] - bdtangent = bdnode_normal @ self.W - BdNodeidx = self.BdNodeidx - dot = bm.sum(bdtangent * delta_x[BdNodeidx],axis=1) - delta_x = bm.set_at(delta_x,BdNodeidx,dot[:,None] * bdtangent) # 物理网格点移动距离 C = (delta_x[cell[:,1:]] - delta_x[cell[:,0,None]]).transpose(0,2,1) a = C[:,0,0]*C[:,1,1] - C[:,0,1]*C[:,1,0] @@ -519,42 +491,40 @@ def get_physical_node(self,vector_field,logic_node_move): positive_x1 = bm.where(x1 > 0, x1, bm.inf) positive_x2 = bm.where(x2 > 0, x2, bm.inf) eta = bm.min([bm.min(positive_x1),bm.min(positive_x2),1]) - node = node + self.alpha * eta * delta_x + return node + def redistribute_boundary(self): """ - 预处理边界节点 + @brief 预处理边界节点 """ node = self.node logic_node = self.logic_node.copy() - - local_n2f_norm = self.local_n2f_norm - vertex_idx = self.BdNodeidx[local_n2f_norm[:,-1] >= 0] - BDNN = self.BDNN - VNN = len(vertex_idx) - K = bm.arange(BDNN,dtype=bm.int32)[local_n2f_norm[:,-1] >= 0] - + Vertex_idx = self.Vertex_idx + sort_Bdnode_idx = self.sort_BdNode_idx + K = bm.where(sort_Bdnode_idx[:,None] == Vertex_idx)[0] isBdedge = self.mesh.boundary_face_flag() node2edge = TM(self.node, self.cell).ds.node_to_edge() edge2cell = self.mesh.face_to_cell() - for i in range(VNN): - G = bm.arange(K[i]+1,K[i+1] if i < VNN-1 else BDNN) - side_node_idx_bar = bm.concatenate(([vertex_idx[i]], - self.BdNodeidx[G], - [vertex_idx[(i+1)%VNN]])) - side_node2edge = node2edge[self.BdNodeidx[G]][:,isBdedge] + G_cell = self.get_control_function(self.beta,mol_times=4)[0] + + VNN = len(Vertex_idx) + for n in range(VNN): + side_node_idx = sort_Bdnode_idx[K[n]:K[n+1]+1] \ + if n < VNN - 1 else sort_Bdnode_idx[K[n]:] + side_node2edge = node2edge[side_node_idx[1:-1]][:,isBdedge] i,j = bm.nonzero(side_node2edge) _,k = bm.unique(j,return_index=True) j = j[bm.sort(k)] side_cell_idx = edge2cell[isBdedge][j][:,0] - side_G = self.G[side_cell_idx] + side_G = G_cell[side_cell_idx] - NN = side_node_idx_bar.shape[0] - side_node = node[side_node_idx_bar] + SNN = side_node_idx.shape[0] + side_node = node[side_node_idx] side_length = bm.linalg.norm(side_node[-1] - side_node[0]) - logic_side_node = logic_node[side_node_idx_bar] + logic_side_node = logic_node[side_node_idx] direction = logic_side_node[-1] - logic_side_node[0] angle = bm.arctan2(direction[1],direction[0]) @@ -563,108 +533,230 @@ def redistribute_boundary(self): rate =bm.linalg.norm(direction)/side_length x = bm.linalg.norm(side_node - side_node[0],axis=1) - cell = bm.stack([bm.arange(NN-1),bm.arange(1,NN)],axis=1) + cell = bm.stack([bm.arange(SNN-1),bm.arange(1,SNN)],axis=1) side_mesh = IntervalMesh(x , cell) - space = LagrangeFESpace(side_mesh, p=1) - qf = side_mesh.integrator(q=3) - bcs, ws = qf.get_quadrature_points_and_weights() - gphi = space.grad_basis(bcs) - cell2dof = space.cell_to_dof() - GDOF = space.number_of_global_dofs() - H = bm.einsum('q , cqid , c ,cqjd, c -> cij ',ws, gphi ,side_G , gphi, side_mesh.entity_measure('cell')) - I = bm.broadcast_to(cell2dof[:, :, None], shape=H.shape) - J = bm.broadcast_to(cell2dof[:, None, :], shape=H.shape) - H = csr_matrix((H.flat, (I.flat, J.flat)), shape=(GDOF, GDOF)) - lform = LinearForm(space) - lform.add_integrator(ScalarSourceIntegrator(source=0,q=3)) - F = lform.assembly() - bdIdx = bm.zeros(NN , dtype= bm.int32) - bdIdx[[0,-1]] = 1 - D0 = spdiags(1-bdIdx ,0, NN, NN) - D1 = spdiags(bdIdx , 0 , NN, NN) + H = self.get_stiff_matrix(side_mesh,side_G) + F = bm.zeros(SNN , dtype= bm.float64) + F = bm.set_at(F , [0,-1] , [x[0],x[-1]]) + bdIdx = bm.zeros(SNN , dtype= bm.float64) + bdIdx = bm.set_at(bdIdx , [0,-1] , 1) + D0 = spdiags(1-bdIdx ,0, SNN, SNN) + D1 = spdiags(bdIdx , 0 , SNN, SNN) H = D0@H + D1 - F[[0,-1]] = x[[0,-1]] - x = spsolve(H,F) + x = spsolve1(H,F) logic_side_node = logic_side_node[0] + rate * \ bm.stack([x,bm.zeros_like(x)],axis=1) @ rotate - logic_node = bm.set_at(logic_node , side_node_idx_bar[1:-1] , logic_side_node[1:-1]) - + logic_node = bm.set_at(logic_node , side_node_idx[1:-1] , logic_side_node[1:-1]) return logic_node - - # 更新插值 + def interpolate(self,move_node): """ - @breif 将解插值到新网格上 + @brief 将解插值到新网格上 + @param move_node: 移动后的物理节点 """ + from scipy.sparse.linalg import spsolve delta_x = self.node - move_node - mesh0 = TriangleMesh(move_node,self.cell) + mesh0 = TriangleMesh(self.node,self.cell) space = LagrangeFESpace(mesh0, p=1) - qf = mesh0.integrator(3,'cell') + cell2dof = space.cell_to_dof() + qf = mesh0.quadrature_formula(3,'cell') bcs, ws = qf.get_quadrature_points_and_weights() cm = mesh0.entity_measure('cell') phi = space.basis(bcs) - H = bm.einsum('q , cqi ,cqj, c -> cij ',ws, phi ,phi , cm) + M = bm.einsum('q , cqi ,cqj, c -> cij ',ws, phi ,phi , cm) gphi = space.grad_basis(bcs) - G = bm.einsum('q , cqid , cid ,cqj ,c -> cij' , ws , gphi, delta_x[self.cell], phi, cm) + P = bm.einsum('q , cqid , cid ,cqj ,c -> cij' , ws , gphi, delta_x[cell2dof], phi, cm) GDOF = space.number_of_global_dofs() - I = bm.broadcast_to(space.cell_to_dof()[:, :, None], shape=G.shape) - J = bm.broadcast_to(space.cell_to_dof()[:, None, :], shape=G.shape) - H = csr_matrix((H.flat, (I.flat, J.flat)), shape=(GDOF, GDOF)) - G = csr_matrix((G.flat, (I.flat, J.flat)), shape=(GDOF, GDOF)) + I = bm.broadcast_to(space.cell_to_dof()[:, :, None], shape=P.shape) + J = bm.broadcast_to(space.cell_to_dof()[:, None, :], shape=P.shape) + M = csr_matrix((M.flat, (I.flat, J.flat)), shape=(GDOF, GDOF)) + P = csr_matrix((P.flat, (I.flat, J.flat)), shape=(GDOF, GDOF)) def ODEs(t,y): - f = spsolve(H,G@y) + f = spsolve(M,P@y) return f # 初值条件 uh0 = self.uh # 范围 tau_span = [0,1] # 求解 - sol = solve_ivp(ODEs,tau_span,uh0,method='RK23') - return sol.y[:,-1] + sol = solve_ivp(ODEs,tau_span,uh0,method='RK23').y[:,-1] + return sol def construct(self,new_mesh): """ - @breif 重构信息 - @param new_mesh 新的网格 + @brief construct: 重构信息 + @param new_mesh:新的网格 """ self.mesh = new_mesh # node 更新之前完成插值 self.uh = self.interpolate(new_mesh.entity('node')) self.node = new_mesh.entity('node') self.cm = new_mesh.entity_measure('cell') - self.star_measure = self.get_star_measure()[0] + self.G = self.get_control_function(self.beta , self.mol_times) - self.G = self.get_control_function(self.beta,self.mol_times) - def solve_elliptic_Equ(self,tol = None , maxit = 100): + def mesh_redistribution(self ,uh, tol = None , maxit = 1000): """ - @breif 求解椭圆方程 - @param pde PDEData - @param tol 容许误差 + @brief mesh_redistribution: 网格重构算法 + @param tol: 容许误差 @param maxit 最大迭代次数 """ + self.uh = uh # 计算容许误差 em = self.logic_mesh.entity_measure('edge') if tol is None: tol = bm.min(em)* 0.1 print(f'容许误差为{tol}') - init_logic_node = self.logic_node.copy() - for i in range(maxit): - logic_node,vector_field = self.solve() - L_infty_error = bm.max(bm.linalg.norm(init_logic_node - logic_node,axis=1)) + logic_node,vector_field = self.solve_move_LogicNode() + L_infty_error = bm.max(bm.linalg.norm(self.logic_node - logic_node,axis=1)) node = self.get_physical_node(vector_field,logic_node) mesh0 = TriangleMesh(node,self.cell) print(f'第{i+1}次迭代的差值为{L_infty_error}') + self.construct(mesh0) if L_infty_error < tol: - self.construct(mesh0) print(f'迭代总次数:{i+1}次') - return mesh0 + return mesh0 , self.uh elif i == maxit - 1: print('超出最大迭代次数') break + + + + +class Mesh_Data_Harmap(): + def __init__(self,mesh:Union[TriangleMesh,TetrahedronMesh],Vertex ) -> None: + self.mesh = mesh + self.node = mesh.entity('node') + self.isBdNode = mesh.boundary_node_flag() + self.Vertex = Vertex + self.isconvex = self.is_convex() + + def is_convex(self): + """ + 判断边界是否是凸的 + """ + from scipy.spatial import ConvexHull + Vertex = self.Vertex + hull = ConvexHull(Vertex) + return len(Vertex) == len(hull.vertices) + + def sort_bdnode_and_bdface(self) -> TensorLike: + mesh = self.mesh + BdNodeidx = mesh.boundary_node_index() + BdEdgeidx = mesh.boundary_face_index() + node = mesh.node + edge = mesh.edge + cell = mesh.cell + # 对边界边和点进行排序 + mesh_0 = TM(node,cell) + node2edge = mesh_0.ds.node_to_face() + bdnode2edge = node2edge[BdNodeidx][:,BdEdgeidx] + i,j = bm.nonzero(bdnode2edge) + bdnode2edge = j.reshape(-1,2) + glob_bdnode2edge = bm.zeros_like(node,dtype=bm.int32) + glob_bdnode2edge = bm.set_at(glob_bdnode2edge,BdNodeidx,BdEdgeidx[bdnode2edge]) + + sort_glob_bdedge_idx_list = [] + sort_glob_bdnode_idx_list = [] + + start_bdnode_idx = BdNodeidx[0] + sort_glob_bdnode_idx_list.append(start_bdnode_idx) + current_node_idx = start_bdnode_idx + + for i in range(bdnode2edge.shape[0]): + if edge[glob_bdnode2edge[current_node_idx,0],1] == current_node_idx: + next_edge_idx = glob_bdnode2edge[current_node_idx,1] else: - self.construct(mesh0) - init_logic_node = logic_node.copy() \ No newline at end of file + next_edge_idx = glob_bdnode2edge[current_node_idx,0] + sort_glob_bdedge_idx_list.append(next_edge_idx) + next_node_idx = edge[next_edge_idx,1] + # 处理空洞区域 + if next_node_idx == start_bdnode_idx: + if i < bdnode2edge.shape[0] - 1: + remian_bdnode_idx = list(set(BdNodeidx)-set(sort_glob_bdnode_idx_list)) + start_bdnode_idx = remian_bdnode_idx[0] + next_node_idx = start_bdnode_idx + else: + # 闭环跳出循环 + break + sort_glob_bdnode_idx_list.append(next_node_idx) + current_node_idx = next_node_idx + return bm.array(sort_glob_bdnode_idx_list,dtype=bm.int32),\ + bm.array(sort_glob_bdedge_idx_list,dtype=bm.int32) + + def get_normal_inform(self,sort_BdNode_idx = None) -> None: + from scipy.sparse import csr_matrix + mesh = self.mesh + BdNodeidx = mesh.boundary_node_index() + if sort_BdNode_idx is not None: + BdNodeidx = sort_BdNode_idx + BdFaceidx = mesh.boundary_face_index() + TD = mesh.top_dimension() + node = mesh.entity('node') + cell = mesh.entity('cell') + if TD == 2: + mesh = TM(node,cell) + node2face = mesh.ds.node_to_face() + elif TD == 3: + def node_to_face(mesh): # 作为三维网格的辅助函数 + NN = mesh.number_of_nodes() + NF = mesh.number_of_faces() + face = mesh.entity('face') + NVF = 3 + node2face = csr_matrix( + ( + bm.ones(NVF*NF, dtype=bm.bool), + ( + face.flat, + bm.repeat(range(NF), NVF) + ) + ), shape=(NN, NF)) + return node2face + node2face = node_to_face(mesh) + bd_node2face = node2face[BdNodeidx][:,BdFaceidx] + i , j = bm.nonzero(bd_node2face) + + bdfun = mesh.face_unit_normal(index=BdFaceidx[j]) + normal,inverse = bm.unique(bdfun,return_inverse=True ,axis = 0) + + _,index,counts = bm.unique(i,return_index=True,return_counts=True) + cow = bm.max(counts) + r = bm.min(counts) + + node2face_normal = -bm.ones((BdNodeidx.shape[0],cow),dtype=bm.int32) + node2face_normal = bm.set_at(node2face_normal,(slice(None),slice(r)),inverse[index[:,None]+bm.arange(r)]) + for i in range(cow-r): + isaimnode = counts > r+i + node2face_normal = bm.set_at(node2face_normal,(isaimnode,r+i) , + inverse[index[isaimnode]+r+i]) + node2face_normal = bm.apply_along_axis(lambda x: bm.set_at( + - bm.ones(TD , dtype = bm.int32) + , slice(len(bm.unique(x[x>=0]))) + ,bm.unique(x[x>=0])) + ,axis=1, arr=node2face_normal) + return node2face_normal,normal + + def get_basic_infom(self): + mesh = self.mesh + node2face_normal,normal = self.get_normal_inform() + BdNodeidx = mesh.boundary_node_index() + Bdinnernode_idx = BdNodeidx[node2face_normal[:,1] < 0] + is_convex = self.isconvex + if is_convex: + Vertex_idx = BdNodeidx[node2face_normal[:,-1] >= 0] + if mesh.TD == 3: + Arrisnode_idx = BdNodeidx[(node2face_normal[:,1] >= 0) & (node2face_normal[:,-1] < 0)] + return Vertex_idx,Bdinnernode_idx,Arrisnode_idx + return Vertex_idx,Bdinnernode_idx + else: + if self.Vertex is None: + raise ValueError('The boundary is not convex, you must give the Vertex') + minus = mesh.node - self.Vertex[:,None] + judge_vertex = bm.sum(((minus**2)[:,:,0],(minus**2)[:,:,1]),axis=0) < 1e-10 + K = bm.arange(mesh.number_of_nodes()) + Vertex_idx = judge_vertex @ K + sort_Bdnode_idx,sort_Bdface_idx = self.sort_bdnode_and_bdface() + return Vertex_idx,Bdinnernode_idx,sort_Bdnode_idx \ No newline at end of file From 4c9bdae532717587aff97bd02e23b1b850f511e6 Mon Sep 17 00:00:00 2001 From: "brighthe98@gmail.com" Date: Fri, 29 Nov 2024 10:52:24 +0800 Subject: [PATCH 56/63] update --- .../linear_elastic_integrator_data.py | 184 ---------------- .../linear_elasticity/test_local_assembly.py | 69 ++++++ app/soptx/soptx/opt/compliance.py | 6 +- app/soptx/soptx/solver/elastic_fem_solver.py | 6 - app/soptx/soptx/solver/test.py | 142 ++++++++++-- app/soptx/soptx/solver/test2.py | 204 ++++++++++++++++++ fealpy/fem/linear_elastic_integrator.py | 137 +++++++++--- fealpy/fem/utils.py | 161 +++++++++++++- 8 files changed, 666 insertions(+), 243 deletions(-) delete mode 100644 app/soptx/linear_elasticity/linear_elastic_integrator_data.py create mode 100644 app/soptx/linear_elasticity/test_local_assembly.py create mode 100644 app/soptx/soptx/solver/test2.py diff --git a/app/soptx/linear_elasticity/linear_elastic_integrator_data.py b/app/soptx/linear_elasticity/linear_elastic_integrator_data.py deleted file mode 100644 index 6dad4bdf3..000000000 --- a/app/soptx/linear_elasticity/linear_elastic_integrator_data.py +++ /dev/null @@ -1,184 +0,0 @@ -import numpy as np - -stress_KE_uniform_mesh_2d_p1_data= [ - { - "extent": (0, 6, 0, 2), - "h": (1.0, 1.0), - "origin": (0, 0), - - # 6 7-------4 5 - # | | - # | | - # | | - # 0 1---→---2 3 - "wiseclose_gd_priority": np.array([ - [0.49450549, 0.17857143, -0.3021978, -0.01373626, -0.24725275, -0.17857143, 0.05494505, 0.01373626], - [0.17857143, 0.49450549, 0.01373626, 0.05494505, -0.17857143, -0.24725275, -0.01373626, -0.3021978], - [-0.3021978, 0.01373626, 0.49450549, -0.17857143, 0.05494505, -0.01373626, -0.24725275, 0.17857143], - [-0.01373626, 0.05494505, -0.17857143, 0.49450549, 0.01373626, -0.3021978, 0.17857143, -0.24725275], - [-0.24725275, -0.17857143, 0.05494505, 0.01373626, 0.49450549, 0.17857143, -0.3021978, -0.01373626], - [-0.17857143, -0.24725275, -0.01373626, -0.3021978, 0.17857143, 0.49450549, 0.01373626, 0.05494505], - [0.05494505, -0.01373626, -0.24725275, 0.17857143, -0.3021978, 0.01373626, 0.49450549, -0.17857143], - [0.01373626, -0.3021978, 0.17857143, -0.24725275, -0.01373626, 0.05494505, -0.17857143, 0.49450549] - ], dtype=np.float64), - - # 2 3-------6 7 - # | | - # ↑ | - # | | - # 0 1-------4 5 - "yx_gd_priority": np.array([ - [0.49450549, 0.17857143, 0.05494505, 0.01373626, -0.3021978, -0.01373626, -0.24725275, -0.17857143], - [0.17857143, 0.49450549, -0.01373626, -0.3021978, 0.01373626, 0.05494505, -0.17857143, -0.24725275], - [0.05494505, -0.01373626, 0.49450549, -0.17857143, -0.24725275, 0.17857143, -0.3021978, 0.01373626], - [0.01373626, -0.3021978, -0.17857143, 0.49450549, 0.17857143, -0.24725275, -0.01373626, 0.05494505], - [-0.3021978, 0.01373626, -0.24725275, 0.17857143, 0.49450549, -0.17857143, 0.05494505, -0.01373626], - [-0.01373626, 0.05494505, 0.17857143, -0.24725275, -0.17857143, 0.49450549, 0.01373626, -0.3021978], - [-0.24725275, -0.17857143, -0.3021978, -0.01373626, 0.05494505, 0.01373626, 0.49450549, 0.17857143], - [-0.17857143, -0.24725275, 0.01373626, 0.05494505, -0.01373626, -0.3021978, 0.17857143, 0.49450549] - ], dtype=np.float64), - - # 1 5-------3 7 - # | | - # ↑ | - # | | - # 0 4-------2 6 - "yx_dof_priority": np.array([ - [ 0.49450549, 0.05494505, -0.3021978, -0.24725275, 0.17857143, 0.01373626, -0.01373626, -0.17857143], - [ 0.05494505, 0.49450549, -0.24725275, -0.3021978, -0.01373626, -0.17857143, 0.17857143, 0.01373626], - [-0.3021978, -0.24725275, 0.49450549, 0.05494505, 0.01373626, 0.17857143, -0.17857143, -0.01373626], - [-0.24725275, -0.3021978, 0.05494505, 0.49450549, -0.17857143, -0.01373626, 0.01373626, 0.17857143], - [ 0.17857143, -0.01373626, 0.01373626, -0.17857143, 0.49450549, -0.3021978, 0.05494505, -0.24725275], - [ 0.01373626, -0.17857143, 0.17857143, -0.01373626, -0.3021978, 0.49450549, -0.24725275, 0.05494505], - [-0.01373626, 0.17857143, -0.17857143, 0.01373626, 0.05494505, -0.24725275, 0.49450549, -0.3021978 ], - [-0.17857143, 0.01373626, -0.01373626, 0.17857143, -0.24725275, 0.05494505, -0.3021978, 0.49450549] - ], dtype=np.float64), - } -] - -KE_uniform_mesh_3d_p1_data = [ - { - "extent": (0, 30, 0, 10, 0, 2), - "h": (1.0, 1.0, 1.0), - "origin": (0, 0, 0), - - # 6 7------4 5 - # / | /| - # 1415-----1213| - # | | | | - # | 0 1-----|2 3 - # | / |/ - # 8 9------1011 - "wiseclose_gd_priority": np.array([ - [0.235042735042735, 0.0801282051282051, 0.0801282051282051, -0.106837606837607, 0.0160256410256410, 0.0160256410256410, -0.0854700854700855, -0.0801282051282051, 0.00801282051282051, 0.0534188034188034, -0.0160256410256410, 0.0400641025641026, 0.0534188034188034, 0.0400641025641026, -0.0160256410256410, -0.0854700854700855, 0.00801282051282051, -0.0801282051282051, -0.0587606837606838, -0.0400641025641026, -0.0400641025641026, -0.00534188034188034, -0.00801282051282051, -0.00801282051282051], - [0.0801282051282051, 0.235042735042735, 0.0801282051282051, -0.0160256410256410, 0.0534188034188034, 0.0400641025641026, -0.0801282051282051, -0.0854700854700855, 0.00801282051282051, 0.0160256410256410, -0.106837606837607, 0.0160256410256410, 0.0400641025641026, 0.0534188034188034, -0.0160256410256410, -0.00801282051282051, -0.00534188034188034, -0.00801282051282051, -0.0400641025641026, -0.0587606837606838, -0.0400641025641026, 0.00801282051282051, -0.0854700854700855, -0.0801282051282051], - [0.0801282051282051, 0.0801282051282051, 0.235042735042735, -0.0160256410256410, 0.0400641025641026, 0.0534188034188034, -0.00801282051282051, -0.00801282051282051, -0.00534188034188034, 0.0400641025641026, -0.0160256410256410, 0.0534188034188034, 0.0160256410256410, 0.0160256410256410, -0.106837606837607, -0.0801282051282051, 0.00801282051282051, -0.0854700854700855, -0.0400641025641026, -0.0400641025641026, -0.0587606837606838, 0.00801282051282051, -0.0801282051282051, -0.0854700854700855], - [-0.106837606837607, -0.0160256410256410, -0.0160256410256410, 0.235042735042735, -0.0801282051282051, -0.0801282051282051, 0.0534188034188034, 0.0160256410256410, -0.0400641025641026, -0.0854700854700855, 0.0801282051282051, -0.00801282051282051, -0.0854700854700855, -0.00801282051282051, 0.0801282051282051, 0.0534188034188034, -0.0400641025641026, 0.0160256410256410, -0.00534188034188034, 0.00801282051282051, 0.00801282051282051, -0.0587606837606838, 0.0400641025641026, 0.0400641025641026], - [0.0160256410256410, 0.0534188034188034, 0.0400641025641026, -0.0801282051282051, 0.235042735042735, 0.0801282051282051, -0.0160256410256410, -0.106837606837607, 0.0160256410256410, 0.0801282051282051, -0.0854700854700855, 0.00801282051282051, 0.00801282051282051, -0.00534188034188034, -0.00801282051282051, -0.0400641025641026, 0.0534188034188034, -0.0160256410256410, -0.00801282051282051, -0.0854700854700855, -0.0801282051282051, 0.0400641025641026, -0.0587606837606838, -0.0400641025641026], - [0.0160256410256410, 0.0400641025641026, 0.0534188034188034, -0.0801282051282051, 0.0801282051282051, 0.235042735042735, -0.0400641025641026, -0.0160256410256410, 0.0534188034188034, 0.00801282051282051, -0.00801282051282051, -0.00534188034188034, 0.0801282051282051, 0.00801282051282051, -0.0854700854700855, -0.0160256410256410, 0.0160256410256410, -0.106837606837607, -0.00801282051282051, -0.0801282051282051, -0.0854700854700855, 0.0400641025641026, -0.0400641025641026, -0.0587606837606838] - ], dtype=np.float64), - } -] - -strain_KE_triangle_p1_data = [ - { - "box": [0, 1, 0, 1], - "nx": 1, - "ny": 1, - - # 1 5-------3 7 - # | / | - # ↓ 1 / 0 ↑ - # | / | - # 0 4-------2 6 - # 2 5 - # / | - # / ↑ - # / | - # 0 3-------1 4 - - "yx_dof_priority": np.array([ - [0.86538462, -0.19230769, -0.67307692, -0.48076923, 0.28846154, 0.19230769], - [-0.19230769, 0.19230769, 0.0, 0.19230769, 0.0, -0.19230769], - [-0.67307692, 0.0, 0.67307692, 0.28846154, -0.28846154, 0.0], - [-0.48076923, 0.19230769, 0.28846154, 0.86538462, -0.67307692, -0.19230769], - [0.28846154, 0.0, -0.28846154, -0.67307692, 0.67307692, 0.0], - [0.19230769, -0.19230769, 0.0, -0.19230769, 0.0, 0.19230769] - ], dtype=np.float64), - - # 2 3-------6 7 - # | / | - # ↓ 1 / 0 ↑ - # | / | - # 0 1-------4 5 - # 4 5 - # / | - # / ↑ - # / | - # 0 1-------2 3 - "yx_gd_priority": np.array([ - [0.86538462, -0.48076923, -0.19230769, 0.28846154, -0.67307692, 0.19230769], - [-0.48076923, 0.86538462, 0.19230769, -0.67307692, 0.28846154, -0.19230769], - [-0.19230769, 0.19230769, 0.19230769, 0.0, 0.0, -0.19230769], - [0.28846154, -0.67307692, 0.0, 0.67307692, -0.28846154, 0.0], - [-0.67307692, 0.28846154, 0.0, -0.28846154, 0.67307692, 0.0], - [0.19230769, -0.19230769, -0.19230769, 0.0, 0.0, 0.19230769] - ], dtype=np.float64), - } -] - -stress_KE_triangle_p1_data = [ - { - "box": [0, 1, 0, 1], - "nx": 1, - "ny": 1, - - # 1 5-------3 7 - # | / | - # ↓ 1 / 0 ↑ - # | / | - # 0 4-------2 6 - # 2 5 - # / | - # / ↑ - # / | - # 0 3-------1 4 - - "yx_dof_priority": np.array([ - [0.74175824, -0.19230769, -0.54945055, -0.35714286, 0.16483516, 0.19230769], - [-0.19230769, 0.19230769, 0.0, 0.19230769, 0.0, -0.19230769], - [-0.54945055, 0.0, 0.54945055, 0.16483516, -0.16483516, 0.0], - [-0.35714286, 0.16483516, 0.19230769, 0.74175824, -0.54945055, -0.19230769], - [0.19230769, 0.0, -0.19230769, -0.54945055, 0.54945055, 0.0], - [0.16483516, -0.16483516, 0.0, -0.19230769, 0.0, 0.19230769] - ], dtype=np.float64), - - # 2 3-------6 7 - # | / | - # ↓ 1 / 0 ↑ - # | / | - # 0 1-------4 5 - # 4 5 - # / | - # / ↑ - # / | - # 0 1-------2 3 - "yx_gd_priority": np.array([ - [0.74175824, -0.35714286, -0.19230769, 0.16483516, -0.54945055, 0.19230769], - [-0.35714286, 0.74175824, 0.16483516, -0.54945055, 0.19230769, -0.19230769], - [-0.19230769, 0.19230769, 0.19230769, 0.0, 0.0, -0.19230769], - [0.19230769, -0.54945055, 0.0, 0.54945055, -0.19230769, 0.0], - [-0.54945055, 0.16483516, 0.0, -0.16483516, 0.54945055, 0.0], - [0.16483516, -0.19230769, -0.16483516, 0.0, 0.0, 0.19230769] - ], dtype=np.float64), - } -] - -KE_tetrahedron_p1_data = [ - { - "box": [0, 1, 0, 1, 0, 1], - "nx": 1, - "ny": 1, - "nz": 1, - } -] \ No newline at end of file diff --git a/app/soptx/linear_elasticity/test_local_assembly.py b/app/soptx/linear_elasticity/test_local_assembly.py new file mode 100644 index 000000000..0228718e4 --- /dev/null +++ b/app/soptx/linear_elasticity/test_local_assembly.py @@ -0,0 +1,69 @@ +from fealpy.backend import backend_manager as bm + +from fealpy.typing import TensorLike + +from fealpy.decorator import cartesian + +from fealpy.mesh import TriangleMesh, TetrahedronMesh, QuadrangleMesh + +from fealpy.functionspace import LagrangeFESpace, TensorFunctionSpace + +from fealpy.material.elastic_material import LinearElasticMaterial + +from fealpy.fem.linear_elastic_integrator import LinearElasticIntegrator + +from soptx.utils.timer import timer + + +bm.set_backend('numpy') +nx, ny = 10, 10 +mesh_simplex = TriangleMesh.from_box(box=[0, 1, 0, 1], nx=nx, ny=ny) +mesh_tensor = QuadrangleMesh.from_box(box=[0, 1, 0, 1], nx=nx, ny=ny) + +p = 4 + +q = p+1 +qf_simplex = mesh_simplex.quadrature_formula(q) +bcs_simplex, _ = qf_simplex.get_quadrature_points_and_weights() +phi_simplex = mesh_simplex.shape_function(bcs=bcs_simplex, p=p) # (NQ, ldof) + +qf_tensor = mesh_tensor.quadrature_formula(q) +bcs_tensor, _ = qf_tensor.get_quadrature_points_and_weights() +phi_tensor = mesh_tensor.shape_function(bcs=bcs_tensor, p=p) # (NQ, ldof) + +space = LagrangeFESpace(mesh_simplex, p=p, ctype='C') +tensor_space = TensorFunctionSpace(space, shape=(-1, 2)) +tldof = tensor_space.number_of_global_dofs() +print(f"tldof: {tldof}") +linear_elastic_material = LinearElasticMaterial(name='lam1_mu1', + lame_lambda=1, shear_modulus=1, + hypo='plane_stress') +integrator0 = LinearElasticIntegrator(material=linear_elastic_material, + q=tensor_space.p+3) +integrator1 = LinearElasticIntegrator(material=linear_elastic_material, + q=tensor_space.p+3, method='fast_stress') +integrator2 = LinearElasticIntegrator(material=linear_elastic_material, + q=tensor_space.p+3, method='symbolic_stress') +integrator1.keep_data() +integrator2.keep_data() # 保留中间数据 +# integrator2.keep_result() # 保留积分结果 +# 创建计时器 +t = timer("Local Assembly Timing") +next(t) # 启动计时器 + +KE0 = integrator0.assembly(space=tensor_space) +t.send('Assembly') +KE11 = integrator1.fast_assembly_stress(space=tensor_space) +t.send('Fast Assembly1') +KE12 = integrator1.fast_assembly_stress(space=tensor_space) +t.send('Fast Assembly2') + +KE21 = integrator2.symbolic_assembly_stress(space=tensor_space) +t.send('Symbolic Assembly1') +KE22 = integrator2.symbolic_assembly_stress(space=tensor_space) +t.send('Symbolic Assembly2') +KE23 = integrator2.symbolic_assembly_stress(space=tensor_space) +t.send('Symbolic Assembly3') +# 结束计时 +t.send(None) +print("-------------------------------") \ No newline at end of file diff --git a/app/soptx/soptx/opt/compliance.py b/app/soptx/soptx/opt/compliance.py index da25b7453..eba7fb8f2 100644 --- a/app/soptx/soptx/opt/compliance.py +++ b/app/soptx/soptx/opt/compliance.py @@ -140,7 +140,9 @@ def compliance_contribution(rho_i: float, ue_i: TensorLike, ke0_i: TensorLike) - E = self.material_properties.calculate_elastic_modulus(rho_i) # 计算单元柔顺度并取负值 : -(E * u^T * K * u) - return -E * bm.einsum('i, ij, j', ue_i, ke0_i, ue_i) + dE = -E * bm.einsum('i, ij, j', ue_i, ke0_i, ue_i) + + return dE # 创建向量化的梯度计算函数 # 最内层:lambda x: compliance_contribution(x, u, k) @@ -154,7 +156,7 @@ def compliance_contribution(rho_i: float, ue_i: TensorLike, ke0_i: TensorLike) - # 外层:bm.vmap(lambda r, u, k: ...) # vmap 将这个操作向量化,使其可以并行处理所有单元 - vmap_grad = bm.vmap(lambda r, u, k: bm.grad( + vmap_grad = bm.vmap(lambda r, u, k: bm.jacrev( lambda x: compliance_contribution(x, u, k) )(r)) diff --git a/app/soptx/soptx/solver/elastic_fem_solver.py b/app/soptx/soptx/solver/elastic_fem_solver.py index 2b39b197a..1d9efce9b 100644 --- a/app/soptx/soptx/solver/elastic_fem_solver.py +++ b/app/soptx/soptx/solver/elastic_fem_solver.py @@ -223,12 +223,6 @@ def _assemble_global_force_vector(self) -> TensorLike: force = self.pde.force F = self.tensor_space.interpolate(force) - # # 让 F 依赖于当前密度 - # if self._current_density is not None: - # # 创建一个非常小的系数,使 F 依赖于密度但几乎不改变其值 - # epsilon = 1e-30 - # F = F * (1.0 + epsilon * bm.sum(self._current_density)) - self._global_force_vector = F return F diff --git a/app/soptx/soptx/solver/test.py b/app/soptx/soptx/solver/test.py index 1357de668..c3f044088 100644 --- a/app/soptx/soptx/solver/test.py +++ b/app/soptx/soptx/solver/test.py @@ -3,7 +3,7 @@ from fealpy.backend import backend_manager as bm from fealpy.functionspace import LagrangeFESpace -from fealpy.fem import ScalarMassIntegrator +from fealpy.fem import ScalarMassIntegrator, ScalarDiffusionIntegrator from fealpy.mesh import TriangleMesh class SymbolicIntegration: @@ -31,6 +31,54 @@ def __init__(self, space1, space2=None): self.l = sp.symbols('l0:%d' % (self.GD+1), real=True) def basis(self, p, mi): + """计算p次拉格朗日基函数 + + 使用递推方法构造基函数,可以得到正确的系数和形式。 + 例如,对于 p=3 时: + - 顶点基函数:λᵢ(3λᵢ - 2)(3λᵢ - 1)/4 + - 边内部点基函数:9λᵢλⱼ(3λᵢ - 1)/2 + - 内部点基函数:27λ₀λ₁λ₂ + + Parameters + ---------- + p : int + 多项式次数 + mi : ndarray + 多重指标矩阵,形状为 (ldof, GD+1) + + Returns + ------- + phi : list + 基函数列表 + """ + GD = self.GD + ldof = mi.shape[0] + l = self.l + + # 初始化矩阵 A,大小为 (p+1) x (GD+1) + A = sp.ones(p+1, GD+1) + + # 递推计算 A 矩阵的元素 + for i in range(1, p + 1): + for j in range(GD + 1): + # 构造形如 (pλ - (i-1)) 的项 + A[i, j] = (p*l[j] - (i-1))*A[i-1, j] + for i in range(1, p+1): + # 除以阶乘 i! 来得到正确的系数 + A[i, :] /= sp.factorial(i) + + # 构造基函数 + phi = [] + for i in range(ldof): + phi_i = 1 + for j in range(GD + 1): + mi_ij = mi[i, j] + phi_i *= A[mi_ij, j] + phi.append(phi_i) + + return phi + + def basis_old(self, p, mi): GD = self.GD ldof = mi.shape[0] l = self.l @@ -58,6 +106,19 @@ def basis(self, p, mi): return phi + def grad_basis(self, p, mi): + phi = self.basis(p, mi) # 获取基函数 + ldof = len(phi) + grad_phi = [] + + # 对每个基函数计算导数 + for i in range(ldof): + # 计算对每个重心坐标的导数 + grad_i = [sp.diff(phi[i], self.l[j]) for j in range(self.GD+1)] + grad_phi.append(grad_i) + + return grad_phi + def multi_index(self, monomial): l = self.l GD = self.GD @@ -90,26 +151,50 @@ def phi_phi_matrix(self): ldof1 = self.ldof1 ldof2 = self.ldof2 - # 初始化矩阵 M - M = sp.zeros(ldof1, ldof2) - - # 计算单元面积 - area = self.mesh.entity_measure('cell')[0] # 获取第一个单元的面积 + M = sp.tensor.array.MutableDenseNDimArray( + sp.zeros(ldof1 * ldof2), + (1, ldof1, ldof2) + ) for i in range(ldof1): for j in range(ldof2): integrand = phi1[i] * phi2[j] - M[i, j] = self.integrate(integrand) - integral_value = self.integrate(integrand) - M[i, j] = integral_value * area # 将面积乘到积分结果上 + M[0, i, j] = self.integrate(integrand) return M + + + def gphi_gphi_matrix(self): + # 计算两个空间基函数的导数 + l = self.l + phi1 = self.basis(self.p1, self.mi1) + phi2 = self.basis(self.p2, self.mi2) + gphi1 = self.grad_basis(self.p1, self.mi1) + gphi2 = self.grad_basis(self.p2, self.mi2) + + # 初始化结果矩阵 + S = sp.tensor.array.MutableDenseNDimArray( + sp.zeros(self.ldof1 * self.ldof2 * (self.GD+1) * (self.GD+1)), + (self.ldof1, self.ldof2, self.GD+1, self.GD+1) + ) + + # 计算所有方向导数的组合 + for i in range(self.ldof1): + for j in range(self.ldof2): + for m in range(self.GD + 1): + for n in range(self.GD + 1): + # gphi1 = sp.diff(phi1[i],l[m]) + # gphi2 = sp.diff(phi2[j],l[n]) + # temp= sp.diff(phi1[i],l[m]) * sp.diff(phi2[j],l[n]) + temp = gphi1[i][m] * gphi2[j][n] + S[i, j, m, n] = self.integrate(temp) + + return S # 初始化网格和空间 mesh = TriangleMesh.from_box(box=[0, 1, 0, 1], nx=2, ny=2, device='cpu') # 定义两个不同的多项式次数 -p1 = 1 -p2 = 1 +p1, p2 = 3, 3 # 创建两个有限元空间 space1 = LagrangeFESpace(mesh=mesh, p=p1, ctype='C') @@ -118,13 +203,28 @@ def phi_phi_matrix(self): # 创建符号积分类的实例,传入两个空间 symbolic_int = SymbolicIntegration(space1, space2) -# 计算质量矩阵 -M = symbolic_int.phi_phi_matrix() - -integrator = ScalarMassIntegrator(q=5) -M1 = integrator.assembly(space=space1) -print("M1:", M1[0]) - -# 输出质量矩阵 -print("Mass Matrix M:") -sp.pprint(M) \ No newline at end of file +q = p1+1 +qf = mesh.quadrature_formula(q) +bcs, ws = qf.get_quadrature_points_and_weights() +gphi_lambda = space1.grad_basis(bcs,variable='u') +M = bm.einsum('q, qik, qjl->ijkl', ws, gphi_lambda, gphi_lambda) + +sphiu = symbolic_int.basis(p1, symbolic_int.mi1) +print("sphiu:", sphiu) +sgphiu = symbolic_int.grad_basis(p1, symbolic_int.mi1) +print("sgphiu:", sgphiu) +gphiu_gphiu_11 = symbolic_int.gphi_gphi_matrix() +print("gphiu_gphiu_11:\n", gphiu_gphiu_11) + +error = bm.sum(bm.abs(M - gphiu_gphiu_11)) +print(f"error: {error}") + +glambda_x = mesh.grad_lambda() +# print("glambda_x:\n", glambda_x) + +cm = mesh.entity_measure('cell') +gphix_gphix_11 = bm.einsum('ijkl, ck, cl, c -> cij', gphiu_gphiu_11, glambda_x[..., 0], glambda_x[..., 0], cm) +gphiy_gphiy_11 = bm.einsum('ijkl, ck, cl, c -> cij', gphiu_gphiu_11, glambda_x[..., 1], glambda_x[..., 1], cm) +gphix_gphiy_11 = bm.einsum('ijkl, ck, cl, c -> cij', gphiu_gphiu_11, glambda_x[..., 0], glambda_x[..., 1], cm) +gphiy_gphix_11 = bm.einsum('ijkl, ck, cl, c -> cij', gphiu_gphiu_11, glambda_x[..., 1], glambda_x[..., 0], cm) +print('--------------------------') diff --git a/app/soptx/soptx/solver/test2.py b/app/soptx/soptx/solver/test2.py new file mode 100644 index 000000000..9b0118efe --- /dev/null +++ b/app/soptx/soptx/solver/test2.py @@ -0,0 +1,204 @@ + +import numpy as np +import sympy as sp + +class LagrangeFEMSpace: + def __init__(self, GD): + self.GD = int(GD) + t = 'l0' + for i in range(1, GD+1): + t = t+', l%d'%(i) + self.l = sp.symbols(t, real=True) + + def number_of_dofs(self, p): + GD = self.GD + val = 1 + for i in range(1, GD+1): + val *= (i+p)/i + return int(val) + + def multi_index_matrix(self, p): + ldof = self.number_of_dofs(p) + GD = self.GD + if GD==1: + multiIndex = np.zeros((ldof, 2), dtype=np.int_) + multiIndex[:, 0] = np.arange(p, -1, -1) + multiIndex[:, 1] = p - multiIndex[:, 0] + elif GD==2: + idx = np.arange(0, ldof) + idx0 = np.floor((-1 + np.sqrt(1 + 8*idx))/2) + multiIndex = np.zeros((ldof, 3), dtype=np.int_) + multiIndex[:, 2] = idx - idx0*(idx0 + 1)/2 + multiIndex[:, 1] = idx0 - multiIndex[:,2] + multiIndex[:, 0] = p - multiIndex[:, 1] - multiIndex[:, 2] + elif GD==3: + idx = np.arange(1, ldof) + idx0 = (3*idx + np.sqrt(81*idx*idx - 1/3)/3)**(1/3) + idx0 = np.floor(idx0 + 1/idx0/3 - 1 + 1e-4) # a+b+c + idx1 = idx - idx0*(idx0 + 1)*(idx0 + 2)/6 + idx2 = np.floor((-1 + np.sqrt(1 + 8*idx1))/2) # b+c + multiIndex = np.zeros((ldof, 4), dtype=np.int_) + multiIndex[1:, 3] = idx1 - idx2*(idx2 + 1)/2 + multiIndex[1:, 2] = idx2 - multiIndex[1:, 3] + multiIndex[1:, 1] = idx0 - idx2 + multiIndex[:, 0] = p - np.sum(multiIndex[:, 1:], axis=1) + return multiIndex + + + def basis(self, p): + l = self.l + GD = self.GD + ldof = self.number_of_dofs(p) + A = sp.ones(p+1, GD+1) + for i in range(1, p+1): + for j in range(GD+1): + A[i, j] = (p*l[j] - (i-1))*A[i-1, j] + for i in range(1,p+1): + A[i,:] /= sp.factorial(i) + mi = self.multi_index_matrix(p) + phi = sp.ones(1, ldof) + for i in range(ldof): + for j in range(GD+1): + phi[i] *= A[mi[i, j], j] + return phi + + def multi_index(self, monoial): + """ + @brief 幂指数多重指标 + """ + l = self.l + GD = self.GD + m = monoial.as_powers_dict() + a = np.zeros(GD+1, dtype=np.int_) #返回幂指标 + for i in range(GD+1): + a[i] = int(m.get(l[i]) or 0) + return a + + + def integrate(self, f): + GD = self.GD + f = f.expand() + r = 0 #积分值 + for m in f.as_coeff_add()[1]: + c = m.as_coeff_mul()[0] #返回系数 + a = self.multi_index(m) #返回单项式的幂指标 + temp = 1 + for i in range(GD+1): + temp *= sp.factorial(a[i]) + r += sp.factorial(GD)*c*temp/sp.factorial(sum(a)+GD) + return r + f.as_coeff_add()[0] + + + def phi_phi_matrix(self, p1, p2, p3=None): + ldof1 = self.number_of_dofs(p1) + ldof2 = self.number_of_dofs(p2) + phi1 = self.basis(p1) + phi2 = self.basis(p2) + M = sp.tensor.array.MutableDenseNDimArray(sp.zeros(ldof1*ldof2),(1,ldof1,ldof2)) + for i in range(ldof1): + for j in range(ldof2): + M[0, i, j] = self.integrate(phi1[i]*phi2[j]) + return M + + def gphi_gphi_matrix(self, p1, p2): + l = self.l + GD = self.GD + ldof1 = self.number_of_dofs(p1) + ldof2 = self.number_of_dofs(p2) + phi1 = self.basis(p1) + phi2 = self.basis(p2) + S =sp.tensor.array.MutableDenseNDimArray(sp.zeros(ldof1*ldof2*(GD+1))*(GD+1)\ + , (ldof1,ldof2,GD+1,GD+1)) + for i in range(ldof1): + for j in range(ldof2): + for m in range(GD + 1): + for n in range(GD + 1): + gphi1 = sp.diff(phi1[i],l[m]) + gphi2 = sp.diff(phi2[j],l[n]) + temp= sp.diff(phi1[i],l[m]) * sp.diff(phi2[j],l[n]) + S[i,j,m,n] = self.integrate(temp) + return S + + + def gphi_phi_matrix(self, p1, p2): + l = self.l + GD = self.GD + ldof1 = self.number_of_dofs(p1) + ldof2 = self.number_of_dofs(p2) + phi1 = self.basis(p1) + phi2 = self.basis(p2) + #S = np.zeros(shape = (ldof1, ldof2, GD+1)) + S =sp.tensor.array.MutableDenseNDimArray(sp.zeros(ldof1*ldof2*(GD+1))\ + ,(ldof1, ldof2 ,GD+1)) + for i in range(ldof1): + for j in range(ldof2): + for n in range(GD + 1): + temp= sp.diff(phi1[i],l[n])*phi2[j] + S[i,j,n] = self.integrate(temp) + return S + + def phi_gphi_phi_matrix(self, p1, p2, p3): + l = self.l + GD = self.GD + ldof1 = self.number_of_dofs(p1) + ldof2 = self.number_of_dofs(p2) + ldof3 = self.number_of_dofs(p3) + phi1 = self.basis(p1) + phi2 = self.basis(p2) + phi3 = self.basis(p3) + #S = np.zeros(shape = (ldof1, ldof2, ldof3, GD+1)) + S =sp.tensor.array.MutableDenseNDimArray(sp.zeros(ldof1*ldof2*ldof3*(GD+1))\ + ,(ldof1, ldof2 ,ldof3, GD+1)) + for i in range(ldof1): + for j in range(ldof2): + for k in range(ldof3): + for n in range(GD + 1): + temp= phi1[i]*sp.diff(phi2[j],l[n])*phi3[k] + S[i, j, k, n] = self.integrate(temp) + return S + + + def phi_phi_phi_matrix(self, p1, p2, p3): + l = self.l + GD = self.GD + ldof1 = self.number_of_dofs(p1) + ldof2 = self.number_of_dofs(p2) + ldof3 = self.number_of_dofs(p3) + phi1 = self.basis(p1) + phi2 = self.basis(p2) + phi3 = self.basis(p3) + #S = np.zeros(shape = (ldof1, ldof2, ldof3)) + S = sp.tensor.array.MutableDenseNDimArray(sp.zeros(ldof1*ldof2*ldof3)\ + ,(ldof1, ldof2 ,ldof3)) + for i in range(ldof1): + for j in range(ldof2): + for k in range(ldof3): + temp= phi1[i]*phi2[j]*phi3[k] + S[i, j, k] = self.integrate(temp) + return S + + def gphi_gphi_phi_matrix(self, p1, p2, p3): + l = self.l + GD = self.GD + ldof1 = self.number_of_dofs(p1) + ldof2 = self.number_of_dofs(p2) + ldof3 = self.number_of_dofs(p3) + phi1 = self.basis(p1) + phi2 = self.basis(p2) + phi3 = self.basis(p3) + S=sp.tensor.array.MutableDenseNDimArray(sp.zeros(ldof3*ldof1*ldof2*(GD+1)*(GD+1))\ + , (ldof1,ldof2,ldof3,GD+1,GD+1)) + for i in range(ldof1): + for j in range(ldof2): + for k in range(ldof3): + for m in range(GD + 1): + for n in range(GD + 1): + temp = sp.diff(phi1[i],l[m])*sp.diff(phi2[j],l[n])*phi3[k] + S[i,j,k,m,n] = self.integrate(temp) + return S +if __name__ == "__main__": + from sympy import * + space = LagrangeFEMSpace(GD=2) + + M = space.gphi_gphi_matrix(3, 3) + print(M) \ No newline at end of file diff --git a/fealpy/fem/linear_elastic_integrator.py b/fealpy/fem/linear_elastic_integrator.py index 2e46f2629..d2025b817 100644 --- a/fealpy/fem/linear_elastic_integrator.py +++ b/fealpy/fem/linear_elastic_integrator.py @@ -11,6 +11,7 @@ enable_cache, assemblymethod ) +from fealpy.fem.utils import SymbolicIntegration class LinearElasticIntegrator(LinearInt, OpInt, CellInt): """ @@ -33,7 +34,7 @@ def to_global_dof(self, space: _FS) -> TensorLike: return space.cell_to_dof()[self.index] @enable_cache - def fetch(self, space: _FS): + def fetch_assembly(self, space: _FS): index = self.index mesh = getattr(space, 'mesh', None) @@ -47,12 +48,54 @@ def fetch(self, space: _FS): qf = mesh.quadrature_formula(q) bcs, ws = qf.get_quadrature_points_and_weights() gphi = space.grad_basis(bcs, index=index, variable='x') + + return bcs, ws, gphi, cm + + @enable_cache + def fetch_fast_assembly(self, space: _FS): + index = self.index + mesh = getattr(space, 'mesh', None) + + if not isinstance(mesh, HomogeneousMesh): + raise RuntimeError("The LinearElasticIntegrator only support spaces on" + f"homogeneous meshes, but {type(mesh).__name__} is" + "not a subclass of HomoMesh.") + + cm = mesh.entity_measure('cell', index=index) + q = space.p+3 if self.q is None else self.q + qf = mesh.quadrature_formula(q) + bcs, ws = qf.get_quadrature_points_and_weights() + # (NQ, LDOF, BC) + gphi_lambda = space.grad_basis(bcs, index=index, variable='u') + # (NC, LDOF, GD) + glambda_x = mesh.grad_lambda() + + return ws, cm, mesh, gphi_lambda, glambda_x + + + @enable_cache + def fetch_symbolic_assembly(self, space: _TS) -> TensorLike: + index = self.index + mesh = getattr(space, 'mesh', None) + + if not isinstance(mesh, HomogeneousMesh): + raise RuntimeError("The LinearElasticIntegrator only support spaces on" + f"homogeneous meshes, but {type(mesh).__name__} is" + "not a subclass of HomoMesh.") + + cm = mesh.entity_measure('cell', index=index) + # (NC, LDOF, GD) + glambda_x = mesh.grad_lambda() + + symbolic_int = SymbolicIntegration(space) + M = bm.tensor(symbolic_int.gphi_gphi_matrix()) - return bcs, ws, gphi, cm, index, q + + return cm, mesh, glambda_x, bm.asarray(M, dtype=bm.float64) def assembly(self, space: _TS) -> TensorLike: scalar_space = space.scalar_space - bcs, ws, gphi, cm, index, q = self.fetch(scalar_space) + bcs, ws, gphi, cm = self.fetch_assembly(scalar_space) D = self.material.elastic_matrix(bcs) B = self.material.strain_matrix(dof_priority=space.dof_priority, gphi=gphi) @@ -63,26 +106,18 @@ def assembly(self, space: _TS) -> TensorLike: @assemblymethod('fast_strain') def fast_assembly_strain(self, space: _TS) -> TensorLike: - index = self.index scalar_space = space.scalar_space - mesh = getattr(scalar_space, 'mesh', None) + + ws, cm, mesh, gphi_lambda, glambda_x = self.fetch_fast_assembly(scalar_space) if not isinstance(mesh, SimplexMesh): raise RuntimeError("The mesh should be an instance of SimplexMesh.") GD = mesh.geo_dimension() - cm = mesh.entity_measure('cell', index=index) - q = space.p+3 if self.q is None else self.q - qf = mesh.quadrature_formula(q) - bcs, ws = qf.get_quadrature_points_and_weights() - # (NQ, LDOF, BC) - gphi_lambda = scalar_space.grad_basis(bcs, index=index, variable='u') # (LDOF, LDOF, BC, BC) M = bm.einsum('q, qik, qjl -> ijkl', ws, gphi_lambda, gphi_lambda) - # (NC, LDOF, GD) - glambda_x = mesh.grad_lambda() # (NC, LDOF, LDOF) A_xx = bm.einsum('ijkl, ck, cl, c -> cij', M, glambda_x[..., 0], glambda_x[..., 0], cm) A_yy = bm.einsum('ijkl, ck, cl, c -> cij', M, glambda_x[..., 1], glambda_x[..., 1], cm) @@ -98,9 +133,9 @@ def fast_assembly_strain(self, space: _TS) -> TensorLike: D = self.material.elastic_matrix() if D.shape[0] != 1: raise ValueError("Elastic matrix D must have shape (NC, 1, 3, 3) or (1, 1, 3, 3).") - D00 = D[..., 0, 0, None] - D01 = D[..., 0, 1, None] - D22 = D[..., 2, 2, None] + D00 = D[..., 0, 0, None] # 2*\mu + \lambda + D01 = D[..., 0, 1, None] # \lambda + D22 = D[..., 2, 2, None] # \mu if space.dof_priority: # Fill the diagonal part @@ -124,26 +159,17 @@ def fast_assembly_strain(self, space: _TS) -> TensorLike: @assemblymethod('fast_stress') def fast_assembly_stress(self, space: _TS) -> TensorLike: - index = self.index scalar_space = space.scalar_space - mesh = getattr(scalar_space, 'mesh', None) - + ws, cm, mesh, gphi_lambda, glambda_x = self.fetch_fast_assembly(scalar_space) + if not isinstance(mesh, SimplexMesh): raise RuntimeError("The mesh should be an instance of SimplexMesh.") GD = mesh.geo_dimension() - cm = mesh.entity_measure('cell', index=index) - q = space.p+3 if self.q is None else self.q - qf = mesh.quadrature_formula(q) - bcs, ws = qf.get_quadrature_points_and_weights() - # (NQ, LDOF, BC) - gphi_lambda = scalar_space.grad_basis(bcs, index=index, variable='u') # (LDOF, LDOF, BC, BC) M = bm.einsum('q, qik, qjl->ijkl', ws, gphi_lambda, gphi_lambda) - # (NC, LDOF, GD) - glambda_x = mesh.grad_lambda() # (NC, LDOF, LDOF) A_xx = bm.einsum('ijkl, ck, cl, c -> cij', M, glambda_x[..., 0], glambda_x[..., 0], cm) A_yy = bm.einsum('ijkl, ck, cl, c -> cij', M, glambda_x[..., 1], glambda_x[..., 1], cm) @@ -152,6 +178,7 @@ def fast_assembly_stress(self, space: _TS) -> TensorLike: NC = mesh.number_of_cells() ldof = scalar_space.number_of_local_dofs() + KK = bm.zeros((NC, GD * ldof, GD * ldof), dtype=bm.float64) # TODO 只能处理 (NC, 1, 3, 3) 和 (1, 1, 3, 3) 的情况 @@ -182,6 +209,62 @@ def fast_assembly_stress(self, space: _TS) -> TensorLike: return KK + @assemblymethod('symbolic_stress') + def symbolic_assembly_stress(self, space: _TS) -> TensorLike: + scalar_space = space.scalar_space + cm, mesh, glambda_x, M = self.fetch_symbolic_assembly(scalar_space) + + if not isinstance(mesh, SimplexMesh): + raise RuntimeError("The mesh should be an instance of SimplexMesh.") + + GD = mesh.geo_dimension() + NC = mesh.number_of_cells() + ldof = scalar_space.number_of_local_dofs() + + # 计算各方向的矩阵 + A_xx = bm.einsum('ijkl, ck, cl, c -> cij', M, glambda_x[..., 0], glambda_x[..., 0], cm) + A_yy = bm.einsum('ijkl, ck, cl, c -> cij', M, glambda_x[..., 1], glambda_x[..., 1], cm) + A_xy = bm.einsum('ijkl, ck, cl, c -> cij', M, glambda_x[..., 0], glambda_x[..., 1], cm) + A_yx = bm.einsum('ijkl, ck, cl, c -> cij', M, glambda_x[..., 1], glambda_x[..., 0], cm) + + KK = bm.zeros((NC, GD * ldof, GD * ldof), dtype=bm.float64) + + # 获取材料矩阵 + D = self.material.elastic_matrix() + if D.shape[1] != 1: + raise ValueError("symbolic_assembly currently only supports elastic matrices " + "with shape (NC, 1, 3, 3) or (1, 1, 3, 3).") + + D00 = D[..., 0, 0, None] # 2μ + λ + D01 = D[..., 0, 1, None] # λ + D22 = D[..., 2, 2, None] # μ + + if space.dof_priority: + # 填充对角块 + KK = bm.set_at(KK, (slice(None), slice(0, ldof), slice(0, ldof)), + D00 * A_xx + D22 * A_yy) + KK = bm.set_at(KK, (slice(None), slice(ldof, KK.shape[1]), slice(ldof, KK.shape[1])), + D00 * A_yy + D22 * A_xx) + + # 填充非对角块 + KK = bm.set_at(KK, (slice(None), slice(0, ldof), slice(ldof, KK.shape[1])), + D01 * A_xy + D22 * A_yx) + KK = bm.set_at(KK, (slice(None), slice(ldof, KK.shape[1]), slice(0, ldof)), + D01 * A_yx + D22 * A_xy) + else: + # 类似的填充方式,但使用不同的索引方式 + KK = bm.set_at(KK, (slice(None), slice(0, KK.shape[1], GD), slice(0, KK.shape[2], GD)), + D00 * A_xx + D22 * A_yy) + KK = bm.set_at(KK, (slice(None), slice(1, KK.shape[1], GD), slice(1, KK.shape[2], GD)), + D00 * A_yy + D22 * A_xx) + + KK = bm.set_at(KK, (slice(None), slice(0, KK.shape[1], GD), slice(1, KK.shape[2], GD)), + D01 * A_xy + D22 * A_yx) + KK = bm.set_at(KK, (slice(None), slice(1, KK.shape[1], GD), slice(0, KK.shape[2], GD)), + D01 * A_yx + D22 * A_xy) + + return KK + @assemblymethod('fast_3d') def fast_assembly(self, space: _TS) -> TensorLike: index = self.index diff --git a/fealpy/fem/utils.py b/fealpy/fem/utils.py index baf062d5a..799a57f0a 100644 --- a/fealpy/fem/utils.py +++ b/fealpy/fem/utils.py @@ -1,9 +1,164 @@ -from typing import Optional +import sympy as sp -from ..backend import backend_manager as bm -from ..typing import TensorLike +from typing import Optional +from fealpy.backend import backend_manager as bm +from fealpy.typing import TensorLike +from fealpy.functionspace import LagrangeFESpace +from fealpy.fem import ScalarMassIntegrator, ScalarDiffusionIntegrator +from fealpy.mesh import TriangleMesh + +class SymbolicIntegration: + def __init__(self, space1, space2=None): + self.space1 = space1 + self.mesh = space1.mesh + self.p1 = space1.p # 第一个空间的多项式次数 + self.GD = self.mesh.geo_dimension() # 几何维度 + self.ldof1 = space1.number_of_local_dofs() # 第一个空间的局部自由度数量 + self.mi1 = self.mesh.multi_index_matrix(p=self.p1, etype=self.GD) # 第一个空间的多重指标矩阵 + + # 如果没有提供第二个空间,则假设两个空间相同 + if space2 is None: + self.space2 = space1 + self.p2 = self.p1 + self.ldof2 = self.ldof1 + self.mi2 = self.mi1 + else: + self.space2 = space2 + self.p2 = space2.p # 第二个空间的多项式次数 + self.ldof2 = space2.number_of_local_dofs() # 第二个空间的局部自由度数量 + self.mi2 = self.mesh.multi_index_matrix(p=self.p2, etype=self.GD) # 第二个空间的多重指标矩阵 + + # 定义符号重心坐标 λ_i + self.l = sp.symbols('l0:%d' % (self.GD+1), real=True) + + def basis(self, p, mi): + """计算p次拉格朗日基函数 + + 使用递推方法构造基函数,可以得到正确的系数和形式。 + 例如,对于 p=3 时: + - 顶点基函数:λᵢ(3λᵢ - 2)(3λᵢ - 1)/4 + - 边内部点基函数:9λᵢλⱼ(3λᵢ - 1)/2 + - 内部点基函数:27λ₀λ₁λ₂ + + Parameters + ---------- + p : int + 多项式次数 + mi : ndarray + 多重指标矩阵,形状为 (ldof, GD+1) + + Returns + ------- + phi : list + 基函数列表 + """ + GD = self.GD + ldof = mi.shape[0] + l = self.l + + # 初始化矩阵 A,大小为 (p+1) x (GD+1) + A = sp.ones(p+1, GD+1) + + # 递推计算 A 矩阵的元素 + for i in range(1, p+1): + for j in range(GD+1): + # 构造形如 (pλ - (i-1)) 的项 + A[i, j] = (p*l[j] - (i-1))*A[i-1, j] + for i in range(1, p+1): + # 除以阶乘 i! 来得到正确的系数 + A[i, :] /= sp.factorial(i) + + # 构造基函数 + phi = [] + for i in range(ldof): + phi_i = 1 + for j in range(GD + 1): + mi_ij = mi[i, j] + phi_i *= A[mi_ij, j] + phi.append(phi_i) + + return phi + + + def grad_basis(self, p, mi): + phi = self.basis(p, mi) # 获取基函数 + ldof = len(phi) + grad_phi = [] + + # 对每个基函数计算导数 + for i in range(ldof): + # 计算对每个重心坐标的导数 + grad_i = [sp.diff(phi[i], self.l[j]) for j in range(self.GD + 1)] + grad_phi.append(grad_i) + + return grad_phi + + def multi_index(self, monomial): + l = self.l + GD = self.GD + + m = monomial.as_powers_dict() + a = bm.zeros(GD+1, dtype=bm.int32) # 返回幂指标 + for i in range(GD+1): + a[i] = int(m.get(l[i]) or 0) + + return a + + def integrate(self, f): + GD = self.GD + + f = f.expand() + r = 0 # 积分值 + for m in f.as_coeff_add()[1]: + c = m.as_coeff_mul()[0] # 返回系数 + a = self.multi_index(m) # 返回单项式的幂指标 + temp = 1 + for i in range(GD+1): + temp *= sp.factorial(a[i]) + r += sp.factorial(GD) * c * temp / sp.factorial(sum(a) + GD) + + return r + f.as_coeff_add()[0] + + def phi_phi_matrix(self): + phi1 = self.basis(self.p1, self.mi1) + phi2 = self.basis(self.p2, self.mi2) + ldof1 = self.ldof1 + ldof2 = self.ldof2 + + M = sp.tensor.array.MutableDenseNDimArray( + sp.zeros(ldof1 * ldof2), + (1, ldof1, ldof2) + ) + + for i in range(ldof1): + for j in range(ldof2): + integrand = phi1[i] * phi2[j] + M[0, i, j] = self.integrate(integrand) + + return M + + def gphi_gphi_matrix(self): + # 计算两个空间基函数的导数 + gphi1 = self.grad_basis(self.p1, self.mi1) + gphi2 = self.grad_basis(self.p2, self.mi2) + + # 初始化结果矩阵 + S = sp.tensor.array.MutableDenseNDimArray( + sp.zeros(self.ldof1 * self.ldof2 * (self.GD+1) * (self.GD+1)), + (self.ldof1, self.ldof2, self.GD+1, self.GD+1) + ) + + # 计算所有方向导数的组合 + for i in range(self.ldof1): + for j in range(self.ldof2): + for m in range(self.GD + 1): + for n in range(self.GD + 1): + temp = gphi1[i][m] * gphi2[j][n] + S[i, j, m, n] = self.integrate(temp) + + return S def normal_strain(gphi: TensorLike, indices: TensorLike, *, out: Optional[TensorLike]=None) -> TensorLike: From 9a32d900ec864e70ceab018bba19a478c2428d87 Mon Sep 17 00:00:00 2001 From: BenHBLiu Date: Fri, 29 Nov 2024 20:37:10 +0800 Subject: [PATCH 57/63] add ROA --- fealpy/opt/__init__.py | 3 ++- fealpy/opt/rime_opt_alg.py | 51 ++++++++++++++++++++++++++++++++++++++ test/opt/test_iopt_alg.py | 3 ++- 3 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 fealpy/opt/rime_opt_alg.py diff --git a/fealpy/opt/__init__.py b/fealpy/opt/__init__.py index 1c6e3b287..114e823f9 100644 --- a/fealpy/opt/__init__.py +++ b/fealpy/opt/__init__.py @@ -22,4 +22,5 @@ from .differential_evolution import DifferentialEvolution from .differentialted_creative_search import DifferentialtedCreativeSearch from .cuckoo_quantum_particle_swarm_opt import CuckooQuantumParticleSwarmOpt -from .marine_predators_alg import MarinePredatorsAlg \ No newline at end of file +from .marine_predators_alg import MarinePredatorsAlg +from .rime_opt_alg import RimeOptAlg \ No newline at end of file diff --git a/fealpy/opt/rime_opt_alg.py b/fealpy/opt/rime_opt_alg.py new file mode 100644 index 000000000..e38a8767b --- /dev/null +++ b/fealpy/opt/rime_opt_alg.py @@ -0,0 +1,51 @@ +from ..backend import backend_manager as bm +from ..typing import TensorLike, Index, _S +from .. import logger +from .optimizer_base import Optimizer + +""" +Marine Predators Algorithm + +Reference: +~~~~~~~~~~ +Hang Su, Dong Zhao, Ali Asghar Heidari, Lei Liu, Xiaoqin Zhang, Majdi Mafarja, Huiling Chen. +RIME: A physics-based optimization. +Neurocomputing, 2023, 532: 183-214. + +""" +class RimeOptAlg(Optimizer): + def __init__(self, option) -> None: + super().__init__(option) + + + def run(self): + options = self.options + x = options["x0"] + N = options["NP"] + fit = self.fun(x)[:, None] + MaxIT = options["MaxIters"] + dim = options["ndim"] + lb, ub = options["domain"] + gbest_index = bm.argmin(fit) + gbest = x[gbest_index] + gbest_f = fit[gbest_index] + w = 5 + # curve = bm.zeros((1, MaxIT)) + for it in range(0, MaxIT): + RimeFactor = (bm.random.rand(1) - 0.5) * 2 * bm.cos(bm.array(bm.pi * it / (MaxIT / 10))) * (1 - bm.round(bm.array(it * w / MaxIT)) / w) # Parameters of Eq.(3),(4),(5) + E = (it / MaxIT) ** 0.5 # Eq.(6) + normalized_rime_rates = fit / bm.linalg.norm(fit) # Parameters of Eq.(7) + r1 = bm.random.rand(N, 1) + x_new = ((r1 < E) * (gbest + RimeFactor * ((ub - lb) * bm.random.rand(N, 1) + lb)) + # Eq.(3) + (r1 >= E) * x) + r2 = bm.random.rand(N, dim) + x_new = ((r2 < normalized_rime_rates) * (gbest)+ + (r2 >= normalized_rime_rates) * x_new) + x_new = x_new + (lb - x_new) * (x_new < lb) + (ub - x_new) * (x_new > ub) + fit_new = self.fun(x_new)[:, None] + mask = fit_new < fit + x, fit = bm.where(mask, x_new, x), bm.where(mask, fit_new, fit) + gbest_idx = bm.argmin(fit) + (gbest, gbest_f) = (x[gbest_idx], fit[gbest_idx]) if fit[gbest_idx] < gbest_f else (gbest, gbest_f) + # curve[0, it] = gbest_f + return gbest, gbest_f \ No newline at end of file diff --git a/test/opt/test_iopt_alg.py b/test/opt/test_iopt_alg.py index 7c3635e92..61c588489 100644 --- a/test/opt/test_iopt_alg.py +++ b/test/opt/test_iopt_alg.py @@ -16,7 +16,8 @@ def test_opt_alg(self, backend, data, NP): lb, ub = data['domain'] x0 = initialize(NP, data['ndim'], ub, lb) option = opt_alg_options(x0, data['objective'], data['domain'], NP) - optimizer = MarinePredatorsAlg(option) + optimizer = RimeOptAlg(option) + # optimizer = MarinePredatorsAlg(option) # optimizer = HoneybadgerAlg(option) # optimizer = CrayfishOptAlg(option) # optimizer = QuantumParticleSwarmOpt(option) From b30e31399f13360e1a87ab50e40aa4a6322ef519 Mon Sep 17 00:00:00 2001 From: AlbertZyy Date: Sun, 1 Dec 2024 16:11:41 +0800 Subject: [PATCH 58/63] fix(backend): shape mismatch in `index_add` when `axis` is negative. --- fealpy/backend/pytorch_backend.py | 1 + 1 file changed, 1 insertion(+) diff --git a/fealpy/backend/pytorch_backend.py b/fealpy/backend/pytorch_backend.py index cde6ef8c5..ca161d854 100644 --- a/fealpy/backend/pytorch_backend.py +++ b/fealpy/backend/pytorch_backend.py @@ -357,6 +357,7 @@ def add_at(a: Tensor, indices, src, /): @staticmethod def index_add(a: Tensor, index, src, /, *, axis: int=0, alpha=1): + axis = a.ndim + axis if (axis < 0) else axis src_flat_shape = a.shape[:axis] + (index.numel(), ) + a.shape[axis+1:] if isinstance(src, (int, float, complex)): From 11b774402a7cc9af6b913482401420adebab0e27 Mon Sep 17 00:00:00 2001 From: BenHBLiu Date: Sun, 1 Dec 2024 16:36:13 +0800 Subject: [PATCH 59/63] update --- fealpy/opt/__init__.py | 3 +- fealpy/opt/moss_growth_opt.py | 91 +++++++++++++++++++++++++++++++++++ fealpy/opt/rime_opt_alg.py | 2 +- test/opt/test_iopt_alg.py | 3 +- 4 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 fealpy/opt/moss_growth_opt.py diff --git a/fealpy/opt/__init__.py b/fealpy/opt/__init__.py index 114e823f9..9d7e13893 100644 --- a/fealpy/opt/__init__.py +++ b/fealpy/opt/__init__.py @@ -23,4 +23,5 @@ from .differentialted_creative_search import DifferentialtedCreativeSearch from .cuckoo_quantum_particle_swarm_opt import CuckooQuantumParticleSwarmOpt from .marine_predators_alg import MarinePredatorsAlg -from .rime_opt_alg import RimeOptAlg \ No newline at end of file +from .rime_opt_alg import RimeOptAlg +from .moss_growth_opt import MossGrowthOpt \ No newline at end of file diff --git a/fealpy/opt/moss_growth_opt.py b/fealpy/opt/moss_growth_opt.py new file mode 100644 index 000000000..bfe4e9512 --- /dev/null +++ b/fealpy/opt/moss_growth_opt.py @@ -0,0 +1,91 @@ +from ..backend import backend_manager as bm +from ..typing import TensorLike, Index, _S +from .. import logger +from .optimizer_base import Optimizer + +""" +Marine Predators Algorithm + +Reference: +~~~~~~~~~~ +Boli Zheng, Yi Chen, Chaofan Wang, Ali Asghar Heidari, Lei Liu, Huiling Chen. +The moss growth optimization (MGO): concepts and performance. +Journal of Computational Design and Engineering, 2024, 11, 184-221. + +""" +class MossGrowthOpt(Optimizer): + def __init__(self, option) -> None: + super().__init__(option) + + + def run(self): + options = self.options + x = options["x0"] + N = options["NP"] + fit = self.fun(x)[:, None] + MaxIT = options["MaxIters"] + dim = options["ndim"] + lb, ub = options["domain"] + gbest_index = bm.argmin(fit) + gbest = x[gbest_index] + gbest_f = fit[gbest_index] + + w = 2 + d1 = 0.2 + rec_num = 10 + divide_num = int(bm.ceil(bm.array(dim / 4))) + divide_num = max((divide_num, 1)) + rec = 0 + rM = bm.zeros((N, dim, rec_num)) + rM_fit = bm.zeros((1, N, rec_num)) + rM[:, :, rec] = x + rM_fit[0, :, rec] = bm.squeeze(fit) + # curve = bm.zeros((1, MaxIT)) + for it in range(0, MaxIT): + cal_zero = bm.zeros((N, dim)) + div_num = bm.random.randint(0, dim, (dim,)) + index = x[:, div_num] > gbest[div_num] + sum_index = bm.sum(index, axis=0) + for i in range(divide_num): + if sum_index[i] < N / 2: + index[:, i] = ~(index[:, i]) + cal_zero = index[:,i][:, None] * x + cal = cal_zero[cal_zero.any(axis=1)] + D = gbest - cal + D_wind = bm.sum(D, axis=0) / D.shape[0] + + beta = D.shape[0]/ N + gama = 1 / (bm.sqrt(bm.array(1 - beta ** 2)) + 2.2204e-16) + step = w * (bm.random.rand(1, dim) - 0.5) * (1 - it / MaxIT) + step2 = 0.1 * w * (bm.random.rand(1, dim) - 0.5) * (1 - it / MaxIT) * (1 + 1 / 2 * (1 + bm.tanh(beta / gama)) * (1 - it / MaxIT)) + step3 = 0.1 * (bm.random.rand(1) - 0.5) * (1 - it / MaxIT) + + act = 1 / 1 + (0.5 - 10 * bm.random.rand(1, dim)) + act[act >= 0.5] = 1 + act[act < 0.5] = 0 + + x_new = x + r1 = bm.random.rand(N, 1) + x_new = ((r1 > d1) * (x + step * D_wind) + + (r1 <= d1) * (x + step2 * D_wind)) + + r2 = bm.random.rand(N,) + r3 = bm.random.rand(N,) + x_new = ((r2[:, None] < 0.8) * ((r3[:, None] > 0.5) * (x) + + (r3[:, None] <= 0.5) * ((1 - act) * x + act * gbest)) + + (r2[:, None] >= 0.8) * x) + x_new[:, div_num[0]] = ((r2 < 0.8) * ((r3 > 0.5) * (gbest[div_num[0]] + step3 * D_wind[div_num[0]]) + (r3 <= 0.5) * x_new[:, div_num[0]]) + + (r2 >= 0.8) * x_new[:, div_num[0]]) + x_new = x_new + (lb - x_new) * (x_new < lb) + (ub - x_new) * (x_new > ub) + fit_new = self.fun(x_new)[:, None] + gbest_idx = bm.argmin(fit_new) + (gbest, gbest_f) = (x_new[gbest_idx], fit_new[gbest_idx]) if fit_new[gbest_idx] < gbest_f else (gbest, gbest_f) + + rec = rec + 1 + if rec == rec_num: + lcost, Iindex = bm.min(rM_fit, axis=2), bm.argmin(rM_fit, axis=2) + a = bm.arange(N) + x = rM[bm.arange(N), :, Iindex[0, :]] + fit = lcost.reshape(-1, 1) + rec = 0 + return gbest, gbest_f \ No newline at end of file diff --git a/fealpy/opt/rime_opt_alg.py b/fealpy/opt/rime_opt_alg.py index e38a8767b..8da6f07b6 100644 --- a/fealpy/opt/rime_opt_alg.py +++ b/fealpy/opt/rime_opt_alg.py @@ -32,7 +32,7 @@ def run(self): w = 5 # curve = bm.zeros((1, MaxIT)) for it in range(0, MaxIT): - RimeFactor = (bm.random.rand(1) - 0.5) * 2 * bm.cos(bm.array(bm.pi * it / (MaxIT / 10))) * (1 - bm.round(bm.array(it * w / MaxIT)) / w) # Parameters of Eq.(3),(4),(5) + RimeFactor = (bm.random.rand(N, 1) - 0.5) * 2 * bm.cos(bm.array(bm.pi * it / (MaxIT / 10))) * (1 - bm.round(bm.array(it * w / MaxIT)) / w) # Parameters of Eq.(3),(4),(5) E = (it / MaxIT) ** 0.5 # Eq.(6) normalized_rime_rates = fit / bm.linalg.norm(fit) # Parameters of Eq.(7) r1 = bm.random.rand(N, 1) diff --git a/test/opt/test_iopt_alg.py b/test/opt/test_iopt_alg.py index 61c588489..1fe05e1ab 100644 --- a/test/opt/test_iopt_alg.py +++ b/test/opt/test_iopt_alg.py @@ -16,7 +16,8 @@ def test_opt_alg(self, backend, data, NP): lb, ub = data['domain'] x0 = initialize(NP, data['ndim'], ub, lb) option = opt_alg_options(x0, data['objective'], data['domain'], NP) - optimizer = RimeOptAlg(option) + optimizer = MossGrowthOpt(option) + # optimizer = RimeOptAlg(option) # optimizer = MarinePredatorsAlg(option) # optimizer = HoneybadgerAlg(option) # optimizer = CrayfishOptAlg(option) From 94586c19eafbd86ce40d0eab5e8711e461e82981 Mon Sep 17 00:00:00 2001 From: "brighthe98@gmail.com" Date: Tue, 3 Dec 2024 10:20:04 +0800 Subject: [PATCH 60/63] update --- app/soptx/linear_elasticity/test_abaqus.py | 99 ++++++++++++++++--- .../linear_elasticity/test_local_assembly.py | 78 ++++++++++----- 2 files changed, 140 insertions(+), 37 deletions(-) diff --git a/app/soptx/linear_elasticity/test_abaqus.py b/app/soptx/linear_elasticity/test_abaqus.py index ed0fd667d..7ce518b7a 100644 --- a/app/soptx/linear_elasticity/test_abaqus.py +++ b/app/soptx/linear_elasticity/test_abaqus.py @@ -11,6 +11,17 @@ from fealpy.solver import cg class BoxDomainPolyLoaded3d(): + def __init__(self): + """ + flip_direction = True + 0 ------- 3 ------- 6 + | 0 | 2 | + 1 ------- 4 ------- 7 + | 1 | 3 | + 2 ------- 5 ------- 8 + """ + self.eps = 1e-12 + def domain(self): return [0, 1, 0, 1, 0, 1] @@ -58,14 +69,77 @@ def dirichlet(self, points: TensorLike) -> TensorLike: return bm.zeros(points.shape, dtype=points.dtype, device=bm.get_device(points)) + + @cartesian + def is_dirichlet_boundary_dof_x(self, points: TensorLike) -> TensorLike: + domain = self.domain() + + x = points[..., 0] + y = points[..., 1] + z = points[..., 2] + + tag1 = bm.abs(x - domain[0]) < self.eps + tag2 = bm.abs(y - domain[0]) < self.eps + tag3 = bm.abs(z - domain[0]) < self.eps + + return tag1 & tag2 &tag3 + + @cartesian + def is_dirichlet_boundary_dof_y(self, points: TensorLike) -> TensorLike: + domain = self.domain() + + x = points[..., 0] + y = points[..., 1] + z = points[..., 2] + + tag1 = bm.abs(x - domain[0]) < self.eps + tag2 = bm.abs(y - domain[0]) < self.eps + tag3 = bm.abs(z - domain[0]) < self.eps + + return tag1 & tag2 &tag3 + + @cartesian + def is_dirichlet_boundary_dof_z(self, points: TensorLike) -> TensorLike: + domain = self.domain() + + x = points[..., 0] + y = points[..., 1] + z = points[..., 2] + + tag1 = bm.abs(x - domain[0]) < self.eps + tag2 = bm.abs(y - domain[0]) < self.eps + tag3 = bm.abs(z - domain[0]) < self.eps + + return tag1 & tag2 &tag3 + + @cartesian + def threshold(self): + + temp = (self.is_dirichlet_boundary_dof_x, + self.is_dirichlet_boundary_dof_y, + self.is_dirichlet_boundary_dof_z) + + return temp + + @cartesian + def is_bc(self, p): + x = p[..., 0] + y = p[..., 1] + z = p[..., 2] + + tag1 = bm.abs(x - 0) TensorLike: phi = space.basis(bcs) # (1, NQ, ldof) gphi = space.grad_basis(bc=bcs) # (NC, NQ, ldof, GD) -J = mesh.jacobi_matrix(bcs) -detJ = bm.linalg.det(J) - -tensor_space = TensorFunctionSpace(space, shape=(3, -1)) +tensor_space = TensorFunctionSpace(space, shape=(-1, 3)) # gd_priority +# tensor_space = TensorFunctionSpace(space, shape=(3, -1)) # dof_priority tgdof = tensor_space.number_of_global_dofs() phi_tensor = tensor_space.basis(bcs) # (1, NQ, tldof, GD) cell2tdof = tensor_space.cell_to_dof() # (NC, tldof) @@ -90,7 +162,7 @@ def dirichlet(self, points: TensorLike) -> TensorLike: nu = 0.3 lam = (E * nu) / ((1.0 + nu) * (1.0 - 2.0 * nu)) mu = E / (2.0 * (1.0 + nu)) -linear_elastic_material = LinearElasticMaterial(name='lam1_mu1', +linear_elastic_material = LinearElasticMaterial(name='E_nu', lame_lambda=lam, shear_modulus=mu, hypo='3D', device=bm.get_device(mesh)) @@ -98,10 +170,6 @@ def dirichlet(self, points: TensorLike) -> TensorLike: gphi=gphi, shear_order=['xy', 'yz', 'zx']) D = linear_elastic_material.elastic_matrix(bcs) KE = bm.einsum('q, c, cqki, cqkl, cqlj -> cij', ws, cm, B, D, B) -KE = bm.einsum('c, cqki, cqkl, cqlj -> cij', cm, B, D, B) -KE2 = bm.einsum('q, cq, cqki, cqkl, cqlj -> cij', ws, detJ, B, D, B) - -error = bm.max(bm.abs(KE[0] - KE2[0])) integrator_K = LinearElasticIntegrator(material=linear_elastic_material, q=q) KE_maual = integrator_K.assembly(space=tensor_space) @@ -138,9 +206,11 @@ def dirichlet(self, points: TensorLike) -> TensorLike: nodes=node, elements=cell, fixed_nodes=fixed_node_index, load_nodes=load_node_indices, loads=F_load_nodes, young_modulus=206e3, poisson_ratio=0.3, density=7.85e-9) +isDDof = tensor_space.is_boundary_dof(threshold=pde.threshold(), method='interp') +# isDDof2 = tensor_space.is_boundary_dof(threshold=pde.is_bc, method='interp') +# isDDof = bm.zeros((24, ), dtype=bm.bool) +# isDDof[0::NN] = True - -isDDof = tensor_space.is_boundary_dof(threshold=None, method='interp') kwargs = K.values_context() # 1. 移除边界自由度相关的非零元素 indices = K.indices() @@ -161,8 +231,7 @@ def dirichlet(self, points: TensorLike) -> TensorLike: # 1. 边界插值 uh_bd = bm.zeros(tensor_space.number_of_global_dofs(), dtype=bm.float64, device=bm.get_device(mesh)) -uh_bd, isDDof = tensor_space.boundary_interpolate(gd=pde.dirichlet, uh=uh_bd, - threshold=None, method='interp') +uh_bd, _ = tensor_space.boundary_interpolate(gd=pde.dirichlet, uh=uh_bd, threshold=pde.threshold(), method='interp') # 2. 修改右端向量 F = F - K.matmul(uh_bd) F = bm.set_at(F, isDDof, uh_bd[isDDof]) diff --git a/app/soptx/linear_elasticity/test_local_assembly.py b/app/soptx/linear_elasticity/test_local_assembly.py index 0228718e4..d67182c8d 100644 --- a/app/soptx/linear_elasticity/test_local_assembly.py +++ b/app/soptx/linear_elasticity/test_local_assembly.py @@ -4,7 +4,9 @@ from fealpy.decorator import cartesian -from fealpy.mesh import TriangleMesh, TetrahedronMesh, QuadrangleMesh +from fealpy.mesh import (TriangleMesh, TetrahedronMesh, + QuadrangleMesh, HexahedronMesh, + UniformMesh2d, UniformMesh3d) from fealpy.functionspace import LagrangeFESpace, TensorFunctionSpace @@ -16,43 +18,74 @@ bm.set_backend('numpy') -nx, ny = 10, 10 -mesh_simplex = TriangleMesh.from_box(box=[0, 1, 0, 1], nx=nx, ny=ny) -mesh_tensor = QuadrangleMesh.from_box(box=[0, 1, 0, 1], nx=nx, ny=ny) +box_2d = [0, 1, 0, 1] +box_3d = [0, 1, 0, 1, 0, 1] +nx, ny, nz = 4, 4, 4 +extent_2d = [0, nx, 0, ny] +extent_3d = [0, nx, 0, ny, 0, nz] +h_2d = [(box_2d[1]-box_2d[0])/nx, (box_2d[3]-box_2d[2])/ny] +h_3d = [(box_3d[1]-box_3d[0])/nx, (box_3d[3]-box_3d[2])/ny, (box_3d[5]-box_3d[4])/nz] +origin_2d = [0, 0] +origin_3d = [0, 0, 0] +mesh_2d_simplex = TriangleMesh.from_box(box=box_2d, nx=nx, ny=ny) +mesh_3d_simplex = TetrahedronMesh.from_box(box=box_3d, nx=nx, ny=ny, nz=nx) +mesh_2d_tensor = QuadrangleMesh.from_box(box=box_2d, nx=nx, ny=ny) +mesh_3d_tensor = HexahedronMesh.from_box(box=box_3d, nx=nx, ny=ny, nz=nx) +mesh_2d_struct = UniformMesh2d(extent=extent_2d, h=h_2d, origin=origin_2d) +mesh_3d_struct = UniformMesh3d(extent=extent_3d, h=h_3d, origin=origin_3d) p = 4 -q = p+1 -qf_simplex = mesh_simplex.quadrature_formula(q) -bcs_simplex, _ = qf_simplex.get_quadrature_points_and_weights() -phi_simplex = mesh_simplex.shape_function(bcs=bcs_simplex, p=p) # (NQ, ldof) -qf_tensor = mesh_tensor.quadrature_formula(q) -bcs_tensor, _ = qf_tensor.get_quadrature_points_and_weights() -phi_tensor = mesh_tensor.shape_function(bcs=bcs_tensor, p=p) # (NQ, ldof) +space_2d = LagrangeFESpace(mesh_2d_simplex, p=p, ctype='C') +tensor_space_2d = TensorFunctionSpace(space_2d, shape=(-1, 2)) -space = LagrangeFESpace(mesh_simplex, p=p, ctype='C') -tensor_space = TensorFunctionSpace(space, shape=(-1, 2)) -tldof = tensor_space.number_of_global_dofs() -print(f"tldof: {tldof}") -linear_elastic_material = LinearElasticMaterial(name='lam1_mu1', +tldof_2d_simplex = tensor_space_2d_simplex.number_of_global_dofs() +print(f"tldof_2d_simplex: {tldof_2d_simplex}") + +linear_elastic_material_2d = LinearElasticMaterial(name='lam1_mu1', lame_lambda=1, shear_modulus=1, hypo='plane_stress') -integrator0 = LinearElasticIntegrator(material=linear_elastic_material, - q=tensor_space.p+3) +linear_elastic_material_3d = LinearElasticMaterial(name='lam1_mu1', + lame_lambda=1, shear_modulus=1, + hypo='3D') + +integrator_standard = LinearElasticIntegrator(material=linear_elastic_material_2d, q=p+3) + integrator1 = LinearElasticIntegrator(material=linear_elastic_material, - q=tensor_space.p+3, method='fast_stress') + q=p+3, method='fast_stress') integrator2 = LinearElasticIntegrator(material=linear_elastic_material, - q=tensor_space.p+3, method='symbolic_stress') + q=p+3, method='symbolic_stress') integrator1.keep_data() integrator2.keep_data() # 保留中间数据 # integrator2.keep_result() # 保留积分结果 # 创建计时器 -t = timer("Local Assembly Timing") +t = timer("2d Local Assembly Timing") +next(t) # 启动计时器 + +KE_2d_standard = integrator_standard.assembly(space=tensor_space_2d) +t.send('Assembly') + +KE_2d_fast1 = integrator1.fast_assembly_stress(space=tensor_space) +t.send('Fast Assembly1') +KE12 = integrator1.fast_assembly_stress(space=tensor_space) +t.send('Fast Assembly2') + +KE21 = integrator2.symbolic_assembly_stress(space=tensor_space) +t.send('Symbolic Assembly1') +KE22 = integrator2.symbolic_assembly_stress(space=tensor_space) +t.send('Symbolic Assembly2') +KE23 = integrator2.symbolic_assembly_stress(space=tensor_space) +t.send('Symbolic Assembly3') +# 结束计时 +t.send(None) + +t = timer("3d Local Assembly Timing") next(t) # 启动计时器 -KE0 = integrator0.assembly(space=tensor_space) +KE_2d_standard = integrator_standard.assembly(space=tensor_space) t.send('Assembly') + KE11 = integrator1.fast_assembly_stress(space=tensor_space) t.send('Fast Assembly1') KE12 = integrator1.fast_assembly_stress(space=tensor_space) @@ -66,4 +99,5 @@ t.send('Symbolic Assembly3') # 结束计时 t.send(None) + print("-------------------------------") \ No newline at end of file From af4c5a0d4e64a077d6f21b63511a05c90c91f5f4 Mon Sep 17 00:00:00 2001 From: "brighthe98@gmail.com" Date: Tue, 3 Dec 2024 21:15:50 +0800 Subject: [PATCH 61/63] update --- ...p_detail_linear_element_hexahedron_mesh.py | 3 + app/soptx/linear_elasticity/jy_box_api.py | 228 ++++++++++++++++++ .../{test_abaqus.py => jy_box_details.py} | 26 +- app/soptx/linear_elasticity/jy_gear_api.py | 152 ++++++++++++ fealpy/functionspace/tensor_space.py | 30 ++- fealpy/material/elastic_material.py | 41 +--- 6 files changed, 422 insertions(+), 58 deletions(-) create mode 100644 app/soptx/linear_elasticity/jy_box_api.py rename app/soptx/linear_elasticity/{test_abaqus.py => jy_box_details.py} (93%) create mode 100644 app/soptx/linear_elasticity/jy_gear_api.py diff --git a/app/soptx/linear_elasticity/exp_detail_linear_element_hexahedron_mesh.py b/app/soptx/linear_elasticity/exp_detail_linear_element_hexahedron_mesh.py index fa774a905..db1817be6 100644 --- a/app/soptx/linear_elasticity/exp_detail_linear_element_hexahedron_mesh.py +++ b/app/soptx/linear_elasticity/exp_detail_linear_element_hexahedron_mesh.py @@ -141,6 +141,9 @@ def dirichlet(self, points: TensorLike) -> TensorLike: uh = tensor_space.function() uh[:] = cg(K, F, maxiter=1000, atol=1e-14, rtol=1e-14) + + + u_exact = tensor_space.interpolate(pde.solution) error = mesh.error(u=uh, v=pde.solution, q=tensor_space.p+3, power=2) print("----------------------") diff --git a/app/soptx/linear_elasticity/jy_box_api.py b/app/soptx/linear_elasticity/jy_box_api.py new file mode 100644 index 000000000..920dbc328 --- /dev/null +++ b/app/soptx/linear_elasticity/jy_box_api.py @@ -0,0 +1,228 @@ +from fealpy.backend import backend_manager as bm + +from fealpy.mesh import HexahedronMesh +from fealpy.material.elastic_material import LinearElasticMaterial +from fealpy.fem.linear_elastic_integrator import LinearElasticIntegrator +from fealpy.fem.vector_source_integrator import VectorSourceIntegrator +from fealpy.fem.bilinear_form import BilinearForm +from fealpy.fem.linear_form import LinearForm +from fealpy.fem.dirichlet_bc import DirichletBC +from fealpy.functionspace import LagrangeFESpace, TensorFunctionSpace +from fealpy.typing import TensorLike +from fealpy.decorator import cartesian +from fealpy.sparse import COOTensor +from fealpy.solver import cg + +class BoxDomainPolyLoaded3d(): + def __init__(self): + """ + flip_direction = True + 0 ------- 3 ------- 6 + | 0 | 2 | + 1 ------- 4 ------- 7 + | 1 | 3 | + 2 ------- 5 ------- 8 + """ + self.eps = 1e-12 + + def domain(self): + return [0, 1, 0, 1, 0, 1] + + @cartesian + def source(self, points: TensorLike): + x = points[..., 0] + y = points[..., 1] + z = points[..., 2] + val = bm.zeros(points.shape, + dtype=points.dtype, device=bm.get_device(points)) + mu = 1 + factor1 = -400 * mu * (2 * y - 1) * (2 * z - 1) + term1 = 3 * (x ** 2 - x) ** 2 * (y ** 2 - y + z ** 2 - z) + term2 = (1 - 6 * x + 6 * x ** 2) * (y ** 2 - y) * (z ** 2 - z) + val[..., 0] = factor1 * (term1 + term2) + + factor2 = 200 * mu * (2 * x - 1) * (2 * z - 1) + term1 = 3 * (y ** 2 - y) ** 2 * (x ** 2 - x + z ** 2 - z) + term2 = (1 - 6 * y + 6 * y ** 2) * (x ** 2 - x) * (z ** 2 - z) + val[..., 1] = factor2 * (term1 + term2) + + factor3 = 200 * mu * (2 * x - 1) * (2 * y - 1) + term1 = 3 * (z ** 2 - z) ** 2 * (x ** 2 - x + y ** 2 - y) + term2 = (1 - 6 * z + 6 * z ** 2) * (x ** 2 - x) * (y ** 2 - y) + val[..., 2] = factor3 * (term1 + term2) + + return val + + @cartesian + def solution(self, points: TensorLike): + x = points[..., 0] + y = points[..., 1] + z = points[..., 2] + val = bm.zeros(points.shape, + dtype=points.dtype, device=bm.get_device(points)) + + mu = 1 + val[..., 0] = 200*mu*(x-x**2)**2 * (2*y**3-3*y**2+y) * (2*z**3-3*z**2+z) + val[..., 1] = -100*mu*(y-y**2)**2 * (2*x**3-3*x**2+x) * (2*z**3-3*z**2+z) + val[..., 2] = -100*mu*(z-z**2)**2 * (2*y**3-3*y**2+y) * (2*x**3-3*x**2+x) + + return val + + def dirichlet(self, points: TensorLike) -> TensorLike: + + result = bm.zeros(points.shape, + dtype=points.dtype, device=bm.get_device(points)) + + return result + + @cartesian + def is_dirichlet_boundary_dof_x(self, points: TensorLike) -> TensorLike: + domain = self.domain() + + x = points[..., 0] + y = points[..., 1] + z = points[..., 2] + + tag1 = bm.abs(x - domain[0]) < self.eps + tag2 = bm.abs(y - domain[0]) < self.eps + tag3 = bm.abs(z - domain[0]) < self.eps + + return tag1 & tag2 & tag3 + + @cartesian + def is_dirichlet_boundary_dof_y(self, points: TensorLike) -> TensorLike: + domain = self.domain() + + x = points[..., 0] + y = points[..., 1] + z = points[..., 2] + + tag1 = bm.abs(x - domain[0]) < self.eps + tag2 = bm.abs(y - domain[0]) < self.eps + tag3 = bm.abs(z - domain[0]) < self.eps + + return tag1 & tag2 & tag3 + @cartesian + def is_dirichlet_boundary_dof_z(self, points: TensorLike) -> TensorLike: + domain = self.domain() + + x = points[..., 0] + y = points[..., 1] + z = points[..., 2] + + tag1 = bm.abs(x - domain[0]) < self.eps + tag2 = bm.abs(y - domain[0]) < self.eps + tag3 = bm.abs(z - domain[0]) < self.eps + + return tag1 & tag2 & tag3 + + @cartesian + def threshold(self): + + temp = (self.is_dirichlet_boundary_dof_x, + self.is_dirichlet_boundary_dof_y, + self.is_dirichlet_boundary_dof_z) + + return temp + + +bm.set_backend('numpy') +nx, ny, nz = 1, 1, 1 +mesh = HexahedronMesh.from_box(box=[0, 1, 0, 1, 0, 1], + nx=nx, ny=ny, nz=nz, device=bm.get_device('cpu')) +GD = mesh.geo_dimension() +NN = mesh.number_of_nodes() +NC = mesh.number_of_cells() +cm = mesh.cell_volume() +node = mesh.entity('node') +cell = mesh.entity('cell') + +space = LagrangeFESpace(mesh, p=1, ctype='C') +cell2dof = space.cell_to_dof() + +q = 2 + +# tensor_space = TensorFunctionSpace(space, shape=(-1, 3)) # gd_priority +tensor_space = TensorFunctionSpace(space, shape=(3, -1)) # dof_priority +print(f"dofs = {tensor_space.dof_priority}") +E = 206e3 +nu = 0.3 +lam = (E * nu) / ((1.0 + nu) * (1.0 - 2.0 * nu)) +mu = E / (2.0 * (1.0 + nu)) +linear_elastic_material = LinearElasticMaterial(name='E_nu', + elastic_modulus=E, poisson_ratio=nu, + hypo='3D', device=bm.get_device(mesh)) + +integrator_K = LinearElasticIntegrator(material=linear_elastic_material, q=q) +KE = integrator_K.assembly(space=tensor_space) +bform = BilinearForm(tensor_space) +bform.add_integrator(integrator_K) +K = bform.assembly(format='csr') +# print(f"K.shape = {K.shape}:\n {K.to_dense()}, ") + +pde = BoxDomainPolyLoaded3d() + +ip1 = mesh.interpolation_points(p=1) +integrator_F = VectorSourceIntegrator(source=pde.source, q=q) +FE = integrator_F.assembly(space=tensor_space) +lform = LinearForm(tensor_space) +lform.add_integrator(integrator_F) +F = lform.assembly() +print(f"F.shape = {F.shape}:\n {F}, ") + +from app.gearx.utils import * + +if tensor_space.dof_priority == True: + F_load_nodes = bm.transpose(F.reshape(3, -1)) +else: + F_load_nodes = F.reshape(NN, GD) +print(f"F_load_nodes.shape = {F_load_nodes.shape}:\n {F_load_nodes}, ") + +load_node_indices = cell[0] +fixed_node_index = bm.tensor([0]) +export_to_inp(filename='/home/heliang/FEALPy_Development/fealpy/app/soptx/linear_elasticity/box.inp', + nodes=node, elements=cell, fixed_nodes=fixed_node_index, load_nodes=load_node_indices, loads=F_load_nodes, + young_modulus=206e3, poisson_ratio=0.3, density=7.85e-9) + + +dbc = DirichletBC(space=tensor_space, + gd=pde.dirichlet, + threshold=pde.threshold(), + method='interp') +K = dbc.apply_matrix(matrix=K, check=True) +# print(f"K.shape = {K.shape}:\n {K.to_dense()}, ") + +uh_bd = bm.zeros(tensor_space.number_of_global_dofs(), dtype=bm.float64, device=bm.get_device(mesh)) +uh_bd, isDDof = tensor_space.boundary_interpolate(gd=pde.dirichlet, uh=uh_bd, threshold=pde.threshold(), method='interp') + +# 2. 修改右端向量 +F = F - K.matmul(uh_bd) +F = bm.set_at(F, isDDof, uh_bd[isDDof]) +print(f"F.shape = {F.shape}:\n {F}, ") + +uh = tensor_space.function() +uh[:] = cg(K, F, maxiter=1000, atol=1e-14, rtol=1e-14) +uh_dof_show = uh.reshape(GD, NN).T +print(f"uh_dof_show.shape = {uh_dof_show.shape}:\n {uh_dof_show}, ") +uh_magnitude = bm.linalg.norm(uh_dof_show, axis=1) +print(f"uh_magnitude = {uh_magnitude}") + + +qf = mesh.quadrature_formula(1) +bcs, ws = qf.get_quadrature_points_and_weights() +phi = space.basis(bcs) # (1, 1, ldof) +gphi = space.grad_basis(bc=bcs) # (NC, 1, ldof, GD) +B = linear_elastic_material.strain_matrix(dof_priority=True, + gphi=gphi, shear_order=['xy', 'yz', 'zx']) # (NC, 1, 6, tldof) +print(f"B.shape = {B.shape}:\n {B}, ") +cell2tdof = tensor_space.cell_to_dof() # (NC, tldof) +tldof = tensor_space.number_of_local_dofs() +uh_cell = bm.zeros((NC, tldof)) +for c in range(NC): + uh_cell[c] = uh[cell2tdof[c]] +print(f"uh_cell.shape = {uh_cell.shape}:\n {uh_cell}, ") +strain = bm.einsum('cqij, cj -> ci', B, uh_cell) # (NC, 6) +print(f"strain.shape = {strain.shape}:\n {strain}, ") + + +print("----------------------") \ No newline at end of file diff --git a/app/soptx/linear_elasticity/test_abaqus.py b/app/soptx/linear_elasticity/jy_box_details.py similarity index 93% rename from app/soptx/linear_elasticity/test_abaqus.py rename to app/soptx/linear_elasticity/jy_box_details.py index 7ce518b7a..5c97ba290 100644 --- a/app/soptx/linear_elasticity/test_abaqus.py +++ b/app/soptx/linear_elasticity/jy_box_details.py @@ -120,25 +120,12 @@ def threshold(self): self.is_dirichlet_boundary_dof_z) return temp - - @cartesian - def is_bc(self, p): - x = p[..., 0] - y = p[..., 1] - z = p[..., 2] - - tag1 = bm.abs(x - 0) pd', load_values, target_cellnorm) # (15, GD) + +u = parameters[..., 0] +v = parameters[..., 1] +w = parameters[..., 2] + +bcs_list = [ + ( + bm.tensor([[u, 1 - u]]), + bm.tensor([[v, 1 - v]]), + bm.tensor([[w, 1 - w]]) + ) + for u, v, w in zip(u, v, w) +] + +space = LagrangeFESpace(mesh, p=1, ctype='C') + +q = 2 + +# tensor_space = TensorFunctionSpace(space, shape=(-1, 3)) # gd_priority +tensor_space = TensorFunctionSpace(space, shape=(3, -1)) # dof_priority + +tgdof = tensor_space.number_of_global_dofs() +tldof = tensor_space.number_of_local_dofs() +cell2tdof = tensor_space.cell_to_dof() + +phi_loads = [] +for bcs in bcs_list: + phi = tensor_space.basis(bcs) + phi_loads.append(phi) + +phi_loads_array = bm.concatenate(phi_loads, axis=1) # (1, NP, tldof, GD) + +FE_load = bm.einsum('pd, cpld -> pl', P, phi_loads_array) # (NP, 24) + +FE = bm.zeros((NC, tldof), dtype=bm.float64) +FE[target_cell_idx, :] = FE_load[:, :] # (NC, tldof) + +F = COOTensor(indices = bm.empty((1, 0), dtype=bm.int32, device=bm.get_device(space)), + values = bm.empty((0, ), dtype=bm.float64, device=bm.get_device(space)), + spshape = (tgdof, )) +indices = cell2tdof.reshape(1, -1) +F = F.add(COOTensor(indices, FE.reshape(-1), (tgdof, ))).to_dense() # (tgdof, ) + +E = 206e3 +nu = 0.3 +lam = (E * nu) / ((1.0 + nu) * (1.0 - 2.0 * nu)) +mu = E / (2.0 * (1.0 + nu)) +linear_elastic_material = LinearElasticMaterial(name='E_nu', + elastic_modulus=E, poisson_ratio=nu, + hypo='3D', device=bm.get_device(mesh)) + +integrator_K = LinearElasticIntegrator(material=linear_elastic_material, q=q) + +KE = integrator_K.assembly(space=tensor_space) +bform = BilinearForm(tensor_space) +bform.add_integrator(integrator_K) +K = bform.assembly(format='csr') + +from app.gearx.utils import * +F_load_nodes = bm.transpose(F.reshape(3, -1)) + +@cartesian +def dirichlet(points: TensorLike) -> TensorLike: + result = bm.zeros(points.shape, + dtype=points.dtype, device=bm.get_device(points)) + + return result + +scalar_is_bd_dof = is_inner_node +scalar_num = bm.sum(scalar_is_bd_dof) +tensor_is_bd_dof = tensor_space.is_boundary_dof( + threshold=(scalar_is_bd_dof, scalar_is_bd_dof, scalar_is_bd_dof), + method='interp') + +dbc = DirichletBC(space=tensor_space, + gd=dirichlet, + threshold=tensor_is_bd_dof, + method='interp') + +K = dbc.apply_matrix(matrix=K, check=True) + +uh_bd = bm.zeros(tensor_space.number_of_global_dofs(), dtype=bm.float64, device=bm.get_device(mesh)) +uh_bd, isDDof = tensor_space.boundary_interpolate(gd=dirichlet, + uh=uh_bd, + threshold=tensor_is_bd_dof, + method='interp') + +F = F - K.matmul(uh_bd) +F = bm.set_at(F, isDDof, uh_bd[isDDof]) + +from fealpy import logger +logger.setLevel('INFO') + +uh = tensor_space.function() +uh[:] = cg(K, F, maxiter=10000, atol=1e-8, rtol=1e-8) +# uh[:] = spsolve(K, F, solver='mumps') + +# 计算残差向量和范数 +residual = K.matmul(uh[:]) - F # 使用 CSRTensor 的 matmul 方法 +residual_norm = bm.sqrt(bm.sum(residual * residual)) +print(f"Final residual norm: {residual_norm:.6e}") + +uh = uh.reshape(GD, NN).T + +mesh.nodedata['deform'] = uh[:] +mesh.to_vtk('/home/heliang/FEALPy_Development/fealpy/app/soptx/linear_elasticity/gear.vtu') +print("-----------") \ No newline at end of file diff --git a/fealpy/functionspace/tensor_space.py b/fealpy/functionspace/tensor_space.py index 5d4646010..888618c7e 100644 --- a/fealpy/functionspace/tensor_space.py +++ b/fealpy/functionspace/tensor_space.py @@ -332,8 +332,9 @@ def boundary_interpolate(self, dof_threshold)) elif method == 'interp': assert len(threshold) == self.dof_numel - isScalarBDof = [scalar_space.is_boundary_dof(i, method=method) for i in threshold] - gd_tensor = [gd(ipoints[isScalarBDof[i]])[...,i] for i in range(self.dof_numel)] + isScalarBDof = [scalar_space.is_boundary_dof(i, method=method) for i in threshold] + + gd_tensor = [gd(ipoints[isScalarBDof[i]])[..., i] for i in range(self.dof_numel)] isTensorBDof = self.is_boundary_dof(threshold=threshold, method=method) if uh is None: uh = self.function() @@ -341,9 +342,32 @@ def boundary_interpolate(self, gd_tensor = bm.concatenate(gd_tensor) else: scalar_gdof = scalar_space.number_of_global_dofs() - gd_tensor = bm.concatenate([bm.array([j[i] for j in isScalarBDof]) for i in range(scalar_gdof)]) + # 使用列表推导式重新排列数据 + gd_values = [] + for i in range(scalar_gdof): + for j in range(self.dof_numel): + # 从 gd_tensor[j] 中获取第 i 个标量基函数的值 + gd_values.append(gd_tensor[j][i] if i < len(gd_tensor[j]) else 0.0) + gd_tensor = bm.array(gd_values) + gd_tensor = gd_tensor[isTensorBDof] + uh[:] = bm.set_at(uh[:], isTensorBDof, gd_tensor) + return uh, isTensorBDof + + # gd_tensor = [gd(ipoints[isScalarBDof[i]]) for i in range(self.dof_numel)] + # gd_tensor = bm.concatenate(gd_tensor, axis=0) + + # isTensorBDof = self.is_boundary_dof(threshold=threshold, method=method) + # index = bm.where(isTensorBDof) + # if uh is None: + # uh = self.function() + # if self.dof_priority: + # gd_tensor = gd_tensor.T.reshape(-1) + # else: + # gd_tensor = gd_tensor.reshape(-1) + # uh[index] = gd_tensor[index] + # return uh, isTensorBDof else: raise ValueError(f"Unknown method: {method}") else: diff --git a/fealpy/material/elastic_material.py b/fealpy/material/elastic_material.py index 9be1cdfe1..b8dc7a4dd 100644 --- a/fealpy/material/elastic_material.py +++ b/fealpy/material/elastic_material.py @@ -142,6 +142,11 @@ def shear_modulus(self) -> float: """获取剪切模量 μ""" return self.mu + @property + def bulk_modulus(self) -> float: + """获取体积模量 K""" + return self.calculate_bulk_modulus() + @property def hypothesis(self) -> str: """获取平面假设""" @@ -279,42 +284,6 @@ def _shear_strain(self, gphi: TensorLike, return out - # def shear_strain(self, gphi: TensorLike, - # indices: TensorLike, *, - # out: Optional[TensorLike]=None) -> TensorLike: - # """Assembly shear strain tensor. - - # Parameters: - # gphi (TensorLike): Gradient of the scalar basis functions shaped (..., ldof, GD).\n - # indices (bool, optional): Indices of DoF components in the flattened DoF, shaped (ldof, GD).\n - # out (TensorLike | None, optional): Output tensor. Defaults to None. - - # Returns: - # TensorLike: Sheared strain shaped (..., NNZ, GD*ldof) where NNZ = (GD + (GD+1))//2. - # """ - # kwargs = bm.context(gphi) - # ldof, GD = gphi.shape[-2:] - # if GD < 2: - # raise ValueError(f"The shear strain requires GD >= 2, but GD = {GD}") - # NNZ = (GD * (GD-1))//2 # 剪切应变分量的数量 - # new_shape = gphi.shape[:-2] + (NNZ, GD*ldof) # (NC, NQ, NNZ, GD*ldof) - - # if out is None: - # out = bm.zeros(new_shape, **kwargs) - # else: - # if out.shape != new_shape: - # raise ValueError(f'out.shape={out.shape} != {new_shape}') - - # cursor = 0 - - # for i in range(0, GD-1): - # for j in range(i+1, GD): - # out = bm.set_at(out, (..., cursor, indices[:, i]), gphi[..., :, j]) - # out = bm.set_at(out, (..., cursor, indices[:, j]), gphi[..., :, i]) - # cursor += 1 - - # return out - From f06077a770813fb31f1ab5654d94b88cc7cffa24 Mon Sep 17 00:00:00 2001 From: "brighthe98@gmail.com" Date: Wed, 4 Dec 2024 21:06:53 +0800 Subject: [PATCH 62/63] update --- app/soptx/linear_elasticity/jy_box_api.py | 274 +++++++++++++++++- app/soptx/linear_elasticity/jy_gear_api.py | 200 +++++++++++-- ...embly.py => test_local_assemble_method.py} | 0 fealpy/fem/linear_elastic_integrator.py | 11 +- 4 files changed, 438 insertions(+), 47 deletions(-) rename app/soptx/linear_elasticity/{test_local_assembly.py => test_local_assemble_method.py} (100%) diff --git a/app/soptx/linear_elasticity/jy_box_api.py b/app/soptx/linear_elasticity/jy_box_api.py index 920dbc328..99f4dc815 100644 --- a/app/soptx/linear_elasticity/jy_box_api.py +++ b/app/soptx/linear_elasticity/jy_box_api.py @@ -124,6 +124,80 @@ def threshold(self): self.is_dirichlet_boundary_dof_z) return temp + +def compute_equivalent_strain(strain, nu): + """ + Calculate equivalent strain for elements + + Parameters + ---------- + strain : array + Element strain with shape (NC, 6) [εxx, εyy, εzz, γxy, γyz, γxz] + nu : float + Poisson's ratio + + Returns + ------- + equiv_strain : array + Element equivalent strain (NC, 1) + """ + # Extract strain components + exx = strain[..., 0, 0] + eyy = strain[..., 1, 1] + ezz = strain[..., 2, 2] + gamma_xy = strain[..., 0, 1] + gamma_yz = strain[..., 1, 2] + gamma_xz = strain[..., 0, 2] + + # Normal strain differences + d1 = exx - eyy + d2 = eyy - ezz + d3 = ezz - exx + + # Combine all terms + equiv_strain = (d1**2 + d2**2 + d3**2 + 6.0 * (gamma_xy**2 + gamma_yz**2 + gamma_xz**2)) + + # Final computation with Poisson's ratio factor and square root + equiv_strain = bm.sqrt(equiv_strain / 2.0) / (1.0 + nu) + + return equiv_strain.reshape(-1, 1) + +def compute_element_equivalent_strain(strain, nu): + """ + Calculate equivalent strain for elements + + Parameters + ---------- + strain : array + Element strain with shape (NC, 6) [εxx, εyy, εzz, γxy, γyz, γxz] + nu : float + Poisson's ratio + + Returns + ------- + equiv_strain : array + Element equivalent strain (NC, 1) + """ + # Extract strain components + exx = strain[..., 0] + eyy = strain[..., 1] + ezz = strain[..., 2] + gamma_xy = strain[..., 3] + gamma_yz = strain[..., 4] + gamma_xz = strain[..., 5] + + # Normal strain differences + d1 = exx - eyy + d2 = eyy - ezz + d3 = ezz - exx + + # Combine all terms + equiv_strain = (d1**2 + d2**2 + d3**2 + 6.0 * (gamma_xy**2 + gamma_yz**2 + gamma_xz**2)) + + # Final computation with Poisson's ratio factor and square root + equiv_strain = bm.sqrt(equiv_strain / 2.0) / (1.0 + nu) + + return equiv_strain.reshape(-1, 1) bm.set_backend('numpy') @@ -162,7 +236,6 @@ def threshold(self): pde = BoxDomainPolyLoaded3d() -ip1 = mesh.interpolation_points(p=1) integrator_F = VectorSourceIntegrator(source=pde.source, q=q) FE = integrator_F.assembly(space=tensor_space) lform = LinearForm(tensor_space) @@ -180,7 +253,7 @@ def threshold(self): load_node_indices = cell[0] fixed_node_index = bm.tensor([0]) -export_to_inp(filename='/home/heliang/FEALPy_Development/fealpy/app/soptx/linear_elasticity/box.inp', +export_to_inp(filename='/home/heliang/FEALPy_Development/fealpy/app/soptx/linear_elasticity/box_fealpy.inp', nodes=node, elements=cell, fixed_nodes=fixed_node_index, load_nodes=load_node_indices, loads=F_load_nodes, young_modulus=206e3, poisson_ratio=0.3, density=7.85e-9) @@ -202,21 +275,161 @@ def threshold(self): uh = tensor_space.function() uh[:] = cg(K, F, maxiter=1000, atol=1e-14, rtol=1e-14) -uh_dof_show = uh.reshape(GD, NN).T -print(f"uh_dof_show.shape = {uh_dof_show.shape}:\n {uh_dof_show}, ") -uh_magnitude = bm.linalg.norm(uh_dof_show, axis=1) +print(f"uh.shape = {uh.shape}:\n {uh[:]}, ") + +if tensor_space.dof_priority: + uh_show = uh.reshape(GD, NN).T +else: + uh_show = uh.reshape(NN, GD) + +print(f"uh_show.shape = {uh_show.shape}:\n {uh_show}, ") +uh_magnitude = bm.linalg.norm(uh_show, axis=1) print(f"uh_magnitude = {uh_magnitude}") +uh_ansys = tensor_space.function() +uh_ansys2 = tensor_space.function() +ux = bm.array([0.0, 1.1348e-5, 2.0691e-6, 1.3418e-5, + -4.345e-6, 7.0035e-6, 4.2098e-7, 1.1769e-5]) + +uy = bm.array([0.0, -8.7815e-6, 7.2734e-6, -1.4257e-5, + 2.9569e-6, -1.8573e-5, -2.5185e-6, -1.13e-5]) + +uz = bm.array([0.0, 5.3937e-6, 1.9651e-5, 1.6055e-5, + -1.1348e-5, -5.9547e-6, 8.3022e-6, 4.7064e-6]) + +uh_ansys_show = bm.stack([ux, uy, uz], axis=1) # (NN, GD) +map = [0, 4, 6, 2, 1, 5, 7, 3] +uh_ansys_show2 = uh_ansys_show[map, ] # (NN, GD) + +if tensor_space.dof_priority: + uh_ansys[:] = uh_ansys_show.T.flatten() + uh_ansys2[:] = uh_ansys_show2.T.flatten() +else: + uh_ansys[:] = uh_ansys_show2.flatten() + uh_ansys2[:] = uh_ansys_show.flatten() + +print(f"uh_ansys_show = {uh_ansys_show.shape}:\n {uh_ansys_show}, ") +print(f"uh_ansys_show2 = {uh_ansys_show2.shape}:\n {uh_ansys_show2}, ") +print(f"uh_ansys = {uh_ansys.shape}:\n {uh_ansys[:]}, ") +print(f"uh_ansys2 = {uh_ansys2.shape}:\n {uh_ansys2[:]}, ") + +bcs1 = (bm.tensor([[1, 0]], dtype=bm.float64), + bm.tensor([[1, 0]], dtype=bm.float64), + bm.tensor([[1, 0]], dtype=bm.float64),) +p1 = mesh.bc_to_point(bc=bcs1) +bcs2 = (bm.tensor([[0, 1]], dtype=bm.float64), + bm.tensor([[1, 0]], dtype=bm.float64), + bm.tensor([[1, 0]], dtype=bm.float64),) +p2 = mesh.bc_to_point(bc=bcs2) +bcs3 = (bm.tensor([[0, 1]], dtype=bm.float64), + bm.tensor([[0, 1]], dtype=bm.float64), + bm.tensor([[1, 0]], dtype=bm.float64),) +p3 = mesh.bc_to_point(bc=bcs3) +bcs4 = (bm.tensor([[1, 0]], dtype=bm.float64), + bm.tensor([[0, 1]], dtype=bm.float64), + bm.tensor([[1, 0]], dtype=bm.float64),) +p4 = mesh.bc_to_point(bc=bcs4) +bcs5 = (bm.tensor([[1, 0]], dtype=bm.float64), + bm.tensor([[1, 0]], dtype=bm.float64), + bm.tensor([[0, 1]], dtype=bm.float64),) +p5 = mesh.bc_to_point(bc=bcs5) +bcs6 = (bm.tensor([[0, 1]], dtype=bm.float64), + bm.tensor([[1, 0]], dtype=bm.float64), + bm.tensor([[0, 1]], dtype=bm.float64),) +p6 = mesh.bc_to_point(bc=bcs6) +bcs7 = (bm.tensor([[0, 1]], dtype=bm.float64), + bm.tensor([[0, 1]], dtype=bm.float64), + bm.tensor([[0, 1]], dtype=bm.float64),) +p7 = mesh.bc_to_point(bc=bcs7) +bcs8 = (bm.tensor([[1, 0]], dtype=bm.float64), + bm.tensor([[0, 1]], dtype=bm.float64), + bm.tensor([[0, 1]], dtype=bm.float64),) +p8 = mesh.bc_to_point(bc=bcs8) + +gphi1 = tensor_space.grad_basis(bcs1) # (NC, 1, tldof, GD, GD) +gphi2 = tensor_space.grad_basis(bcs2) # (NC, 1, ldof, GD) +gphi3 = tensor_space.grad_basis(bcs3) # (NC, 1, ldof, GD) +gphi4 = tensor_space.grad_basis(bcs4) # (NC, 1, ldof, GD) +gphi5 = tensor_space.grad_basis(bcs5) # (NC, 1, ldof, GD) +gphi6 = tensor_space.grad_basis(bcs6) # (NC, 1, ldof, GD) +gphi7 = tensor_space.grad_basis(bcs7) # (NC, 1, ldof, GD) +gphi8 = tensor_space.grad_basis(bcs8) # (NC, 1, ldof, GD) + +cell2tdof = tensor_space.cell_to_dof() # (NC, tldof) +tldof = tensor_space.number_of_local_dofs() + +uh_ansys_cell = bm.zeros((NC, tldof)) +uh_ansys_cell2 = bm.zeros((NC, tldof)) +for c in range(NC): + uh_ansys_cell[c] = uh_ansys[cell2tdof[c]] + uh_ansys_cell2[c] = uh_ansys2[cell2tdof[c]] +print(f"uh_ansys_cell.shape = {uh_ansys_cell.shape}:\n {uh_ansys_cell}, ") +print(f"uh_ansys_cell2.shape = {uh_ansys_cell2.shape}:\n {uh_ansys_cell2}, ") + +uh_cell = bm.zeros((NC, tldof)) +for c in range(NC): + uh_cell[c] = uh[cell2tdof[c]] +print(f"uh_cell.shape = {uh_cell.shape}:\n {uh_cell}, ") + + + +grad1 = bm.einsum('cqimn, ci -> cqmn', gphi1, uh_cell) # (1, 1, GD, GD) +strain1 = (grad1 + bm.transpose(grad1, (0, 1, 3, 2))) / 2 # (1, 1, GD, GD) +# grad1_map = bm.einsum('cqimn, ci -> cqmn', gphi1, uh_cell2) # (1, 1, GD, GD) +# strain1_map = (grad1_map + bm.transpose(grad1_map, (0, 1, 3, 2))) / 2 # (1, 1, GD, GD) + +grad2 = bm.einsum('cqimn, ci -> cqmn', gphi2, uh_cell) # (1, 1, GD, GD) +strain2 = (grad2 + bm.transpose(grad2, (0, 1, 3, 2))) / 2 # (1, 1, GD, GD) +# grad2_map = bm.einsum('cqimn, ci -> cqmn', gphi1, uh_cell2) # (1, 1, GD, GD) +# strain2_map = (grad2_map + bm.transpose(grad2_map, (0, 1, 3, 2))) / 2 # (1, 1, GD, GD) + +grad3 = bm.einsum('cqimn, ci -> cqmn', gphi3, uh_cell) # (1, 1, GD, GD) +strain3 = (grad3 + bm.transpose(grad3, (0, 1, 3, 2))) / 2 # (1, 1, GD, GD) +# grad3_map = bm.einsum('cqimn, ci -> cqmn', gphi3, uh_cell2) # (1, 1, GD, GD) +# strain3_map = (grad3_map + bm.transpose(grad3_map, (0, 1, 3, 2))) / 2 # (1, 1, GD, GD) + +grad4 = bm.einsum('cqimn, ci -> cqmn', gphi4, uh_cell) # (1, 1, GD, GD) +strain4 = (grad4 + bm.transpose(grad4, (0, 1, 3, 2))) / 2 # (1, 1, GD, GD) +# grad4_map = bm.einsum('cqimn, ci -> cqmn', gphi4, uh_cell2) # (1, 1, GD, GD) +# strain4_map = (grad4_map + bm.transpose(grad4_map, (0, 1, 3, 2))) / 2 # (1, 1, GD, GD) + +grad5 = bm.einsum('cqimn, ci -> cqmn', gphi5, uh_cell) # (1, 1, GD, GD) +strain5 = (grad5 + bm.transpose(grad5, (0, 1, 3, 2))) / 2 # (1, 1, GD, GD) +# grad5_map = bm.einsum('cqimn, ci -> cqmn', gphi5, uh_cell2) # (1, 1, GD, GD) +# strain5_map = (grad5_map + bm.transpose(grad5_map, (0, 1, 3, 2))) / 2 # (1, 1, GD, GD) + +grad6 = bm.einsum('cqimn, ci -> cqmn', gphi6, uh_cell) # (1, 1, GD, GD) +strain6 = (grad6 + bm.transpose(grad6, (0, 1, 3, 2))) / 2 # (1, 1, GD, GD) +# grad6_map = bm.einsum('cqimn, ci -> cqmn', gphi6, uh_cell2) # (1, 1, GD, GD) +# strain6_map = (grad6_map + bm.transpose(grad6_map, (0, 1, 3, 2))) / 2 # (1, 1, GD, GD) + +grad7 = bm.einsum('cqimn, ci -> cqmn', gphi7, uh_cell) # (1, 1, GD, GD) +strain7 = (grad7 + bm.transpose(grad7, (0, 1, 3, 2))) / 2 # (1, 1, GD, GD) +# grad7_map = bm.einsum('cqimn, ci -> cqmn', gphi7, uh_cell2) # (1, 1, GD, GD) +# strain7_map = (grad7_map + bm.transpose(grad7_map, (0, 1, 3, 2))) / 2 # (1, 1, GD, GD) + +grad8 = bm.einsum('cqimn, ci -> cqmn', gphi8, uh_cell) # (1, 1, GD, GD) +strain8 = (grad8 + bm.transpose(grad8, (0, 1, 3, 2))) / 2 # (1, 1, GD, GD) +# grad8_map = bm.einsum('cqimn, ci -> cqmn', gphi8, uh_cell2) # (1, 1, GD, GD) +# strain8_map = (grad8_map + bm.transpose(grad8_map, (0, 1, 3, 2))) / 2 # (1, 1, GD, GD) + +# strain3 = bm.einsum('cqimn, ci -> cqmn', gphi3, uh_cell) # (1, 1, GD, GD) +# strain4 = bm.einsum('cqimn, ci -> cqmn', gphi4, uh_cell) # (1, 1, GD, GD) +# strain5 = bm.einsum('cqimn, ci -> cqmn', gphi5, uh_cell) # (1, 1, GD, GD) +# strain6 = bm.einsum('cqimn, ci -> cqmn', gphi6, uh_cell) # (1, 1, GD, GD) +# strain7 = bm.einsum('cqimn, ci -> cqmn', gphi7, uh_cell) # (1, 1, GD, GD) +# strain8 = bm.einsum('cqimn, ci -> cqmn', gphi8, uh_cell) # (1, 1, GD, GD) + +strian_test = bm.stack([strain1, strain2, strain3, strain4, strain5, strain6, strain7, strain8], axis=1) # (1, 8, GD, GD) + +equiv_strain1= compute_equivalent_strain(strian_test, nu) +print(f"equiv_strain1.shape = {equiv_strain1.shape}:\n {equiv_strain1}, ") -qf = mesh.quadrature_formula(1) -bcs, ws = qf.get_quadrature_points_and_weights() -phi = space.basis(bcs) # (1, 1, ldof) -gphi = space.grad_basis(bc=bcs) # (NC, 1, ldof, GD) B = linear_elastic_material.strain_matrix(dof_priority=True, gphi=gphi, shear_order=['xy', 'yz', 'zx']) # (NC, 1, 6, tldof) print(f"B.shape = {B.shape}:\n {B}, ") -cell2tdof = tensor_space.cell_to_dof() # (NC, tldof) -tldof = tensor_space.number_of_local_dofs() + + uh_cell = bm.zeros((NC, tldof)) for c in range(NC): uh_cell[c] = uh[cell2tdof[c]] @@ -225,4 +438,41 @@ def threshold(self): print(f"strain.shape = {strain.shape}:\n {strain}, ") -print("----------------------") \ No newline at end of file +uh_ansys_cell = bm.zeros((NC, tldof)) +for c in range(NC): + uh_ansys_cell[c] = uh_ansys[cell2tdof[c]] +print(f"uh_ansys_cell.shape = {uh_ansys_cell.shape}:\n {uh_ansys_cell}, ") +strain_ansys = bm.einsum('cqij, cj -> ci', B, uh_ansys_cell) # (NC, 6) +print(f"strain_ansys.shape = {strain_ansys.shape}:\n {strain_ansys}, ") + +nodal_strain_ansys = bm.zeros((NN, 6)) +weights_ansys = bm.zeros((NN, 1)) + +for c in range(NC): + for n in range(8): + node_id = cell[c, n] + for i in range(6): + nodal_strain_ansys[node_id, i] += strain_ansys[c, i] + + weights_ansys[node_id, 0] += 1.0 + +# Compute averages at nodes +for n in range(NN): + if weights_ansys[n, 0] > 0: + for i in range(6): + nodal_strain_ansys[n, i] /= weights_ansys[n, 0] + +print(f"ANSYS nodal strain shape = {nodal_strain_ansys.shape}") +print(f"ANSYS nodal strain = \n{nodal_strain_ansys}") + +element_equiv_strain_ansys = compute_element_equivalent_strain(strain_ansys, nu) +print(f"ANSYS element equivalent strain shape = {element_equiv_strain_ansys.shape}") +print(f"ANSYS element equivalent strain : \n{element_equiv_strain_ansys}") + +nodal_equiv_strain_ansys = compute_element_equivalent_strain(nodal_strain_ansys, nu) +print(f"ANSYS nodal equivalent strain shape = {nodal_equiv_strain_ansys.shape}") +print(f"ANSYS nodal equivalent strain : \n{nodal_equiv_strain_ansys}") + + +print("----------------------") + diff --git a/app/soptx/linear_elasticity/jy_gear_api.py b/app/soptx/linear_elasticity/jy_gear_api.py index 772e3d5ca..eea6188fa 100644 --- a/app/soptx/linear_elasticity/jy_gear_api.py +++ b/app/soptx/linear_elasticity/jy_gear_api.py @@ -15,10 +15,33 @@ from fealpy.mesh import HexahedronMesh +def compute_equivalent_strain(strain, nu): + # Extract strain components + exx = strain[..., 0, 0] + eyy = strain[..., 1, 1] + ezz = strain[..., 2, 2] + gamma_xy = strain[..., 0, 1] + gamma_yz = strain[..., 1, 2] + gamma_xz = strain[..., 0, 2] + + # Normal strain differences + d1 = exx - eyy + d2 = eyy - ezz + d3 = ezz - exx + + # Combine all terms + equiv_strain = (d1**2 + d2**2 + d3**2 + 6.0 * (gamma_xy**2 + gamma_yz**2 + gamma_xz**2)) + + # Final computation with Poisson's ratio factor and square root + # equiv_strain = bm.sqrt(equiv_strain / 2.0) / (1.0 + nu) + equiv_strain = bm.sqrt(equiv_strain / 2.0) / (1.0) + + return equiv_strain with open('/home/heliang/FEALPy_Development/fealpy/app/soptx/linear_elasticity/external_gear_data_part.pkl', 'rb') as f: data = pickle.load(f) +external_gear = data['external_gear'] hex_mesh = data['hex_mesh'] helix_node = data['helix_node'] target_cell_idx = data['target_cell_idx'] @@ -32,17 +55,36 @@ GD = mesh.geo_dimension() NC = mesh.number_of_cells() +print(f"NC: {NC}") NN = mesh.number_of_nodes() +print(f"NN: {NN}") node = mesh.entity('node') cell = mesh.entity('cell') load_values = bm.array([50.0, 60.0, 79.0, 78.0, 87.0, 95.0, 102.0, 109.0, 114.0, 119.0, 123.0, 127.0, 129.0, 130.0, 131.0], dtype=bm.float64) -cellnorm = mesh.cell_normal() # (NC, GD) +n = 15 +helix_d = bm.linspace(external_gear.d, external_gear.effective_da, n) +helix_width = bm.linspace(0, external_gear.tooth_width, n) +helix_node = external_gear.cylindrical_to_cartesian(helix_d, helix_width) -target_cellnorm = cellnorm[target_cell_idx] # (15, GD) -P = bm.einsum('p, pd -> pd', load_values, target_cellnorm) # (15, GD) +target_cell_idx = bm.zeros(n, bm.int32) +face_normal = bm.zeros((n, 3), bm.float64) +parameters = bm.zeros((n, 3), bm.float64) +for i, t_node in enumerate(helix_node): + target_cell_idx[i], face_normal[i], parameters[i] = external_gear.find_node_location_kd_tree(t_node) + +average_normal = bm.mean(face_normal, axis=0) +average_normal /= bm.linalg.norm(average_normal) + +threshold = 0.1 +for i in range(len(face_normal)): + deviation = np.linalg.norm(face_normal[i] - average_normal) + if deviation > threshold: + face_normal[i] = average_normal + +P = bm.einsum('p, pd -> pd', load_values, face_normal) # (15, GD) u = parameters[..., 0] v = parameters[..., 1] @@ -58,6 +100,8 @@ ] space = LagrangeFESpace(mesh, p=1, ctype='C') +scalar_gdof = space.number_of_global_dofs() +cell2dof = space.cell_to_dof() q = 2 @@ -65,17 +109,23 @@ tensor_space = TensorFunctionSpace(space, shape=(3, -1)) # dof_priority tgdof = tensor_space.number_of_global_dofs() +print(f"tgdof: {tgdof}") tldof = tensor_space.number_of_local_dofs() cell2tdof = tensor_space.cell_to_dof() +load_node_indices = cell[target_cell_idx].flatten() # (15*8, ) +# 带有载荷的节点对应的全局自由度编号 +dof_indices = bm.stack([scalar_gdof * d + + load_node_indices for d in range(GD)], axis=1) # (15*8, GD) + phi_loads = [] for bcs in bcs_list: phi = tensor_space.basis(bcs) phi_loads.append(phi) -phi_loads_array = bm.concatenate(phi_loads, axis=1) # (1, NP, tldof, GD) +phi_loads_array = bm.concatenate(phi_loads, axis=1) # (1, 15, tldof, GD) -FE_load = bm.einsum('pd, cpld -> pl', P, phi_loads_array) # (NP, 24) +FE_load = bm.einsum('pd, cpld -> pl', P, phi_loads_array) # (15, 24) FE = bm.zeros((NC, tldof), dtype=bm.float64) FE[target_cell_idx, :] = FE_load[:, :] # (NC, tldof) @@ -86,6 +136,15 @@ indices = cell2tdof.reshape(1, -1) F = F.add(COOTensor(indices, FE.reshape(-1), (tgdof, ))).to_dense() # (tgdof, ) +# 从全局载荷向量中提取有载荷节点处的值 +F_load_nodes = F[dof_indices] # (15*8, GD) + +fixed_node_index = bm.where(is_inner_node)[0] +export_to_inp(filename='/home/heliang/FEALPy_Development/fealpy/app/soptx/linear_elasticity/gear_fealpy.inp', + nodes=node, elements=cell, + fixed_nodes=fixed_node_index, load_nodes=load_node_indices, loads=F_load_nodes, + young_modulus=206e3, poisson_ratio=0.3, density=7.85e-9) + E = 206e3 nu = 0.3 lam = (E * nu) / ((1.0 + nu) * (1.0 - 2.0 * nu)) @@ -96,57 +155,140 @@ integrator_K = LinearElasticIntegrator(material=linear_elastic_material, q=q) -KE = integrator_K.assembly(space=tensor_space) +# KE = integrator_K.assembly(space=tensor_space) bform = BilinearForm(tensor_space) bform.add_integrator(integrator_K) K = bform.assembly(format='csr') +values = K.values() +K_norm = bm.sqrt(bm.sum(values * values)) +F_norm = bm.sqrt(bm.sum(F * F)) +print(f"Matrix norm after dc: {K_norm:.6f}") +print(f"Load vector norm after dc: {F_norm:.6f}") -from app.gearx.utils import * -F_load_nodes = bm.transpose(F.reshape(3, -1)) - -@cartesian -def dirichlet(points: TensorLike) -> TensorLike: - result = bm.zeros(points.shape, - dtype=points.dtype, device=bm.get_device(points)) - - return result scalar_is_bd_dof = is_inner_node -scalar_num = bm.sum(scalar_is_bd_dof) tensor_is_bd_dof = tensor_space.is_boundary_dof( threshold=(scalar_is_bd_dof, scalar_is_bd_dof, scalar_is_bd_dof), method='interp') - dbc = DirichletBC(space=tensor_space, - gd=dirichlet, + gd=0, threshold=tensor_is_bd_dof, method='interp') - K = dbc.apply_matrix(matrix=K, check=True) - uh_bd = bm.zeros(tensor_space.number_of_global_dofs(), dtype=bm.float64, device=bm.get_device(mesh)) -uh_bd, isDDof = tensor_space.boundary_interpolate(gd=dirichlet, - uh=uh_bd, - threshold=tensor_is_bd_dof, - method='interp') - +# uh_bd, isDDof = tensor_space.boundary_interpolate(gd=0, +# uh=uh_bd, +# threshold=tensor_is_bd_dof, +# method='interp') +isDDof = tensor_is_bd_dof F = F - K.matmul(uh_bd) F = bm.set_at(F, isDDof, uh_bd[isDDof]) +values = K.values() +K_norm = bm.sqrt(bm.sum(values * values)) +F_norm = bm.sqrt(bm.sum(F * F)) +print(f"Matrix norm after dc: {K_norm:.6f}") +print(f"Load vector norm after dc: {F_norm:.6f}") from fealpy import logger logger.setLevel('INFO') uh = tensor_space.function() uh[:] = cg(K, F, maxiter=10000, atol=1e-8, rtol=1e-8) -# uh[:] = spsolve(K, F, solver='mumps') # 计算残差向量和范数 residual = K.matmul(uh[:]) - F # 使用 CSRTensor 的 matmul 方法 residual_norm = bm.sqrt(bm.sum(residual * residual)) print(f"Final residual norm: {residual_norm:.6e}") -uh = uh.reshape(GD, NN).T +if tensor_space.dof_priority: + uh_show = uh.reshape(GD, NN).T +else: + uh_show = uh.reshape(NN, GD) + +uh_magnitude = bm.linalg.norm(uh_show, axis=1) + +mesh.nodedata['uh'] = uh_show[:] +mesh.nodedata['uh_magnitude'] = uh_magnitude[:] + +uh_cell = bm.zeros((NC, tldof)) +for c in range(NC): + uh_cell[c] = uh[cell2tdof[c]] + +bcs1 = (bm.tensor([[1, 0]], dtype=bm.float64), + bm.tensor([[1, 0]], dtype=bm.float64), + bm.tensor([[1, 0]], dtype=bm.float64),) +bcs2 = (bm.tensor([[0, 1]], dtype=bm.float64), + bm.tensor([[1, 0]], dtype=bm.float64), + bm.tensor([[1, 0]], dtype=bm.float64),) +bcs3 = (bm.tensor([[0, 1]], dtype=bm.float64), + bm.tensor([[0, 1]], dtype=bm.float64), + bm.tensor([[1, 0]], dtype=bm.float64),) +bcs4 = (bm.tensor([[1, 0]], dtype=bm.float64), + bm.tensor([[0, 1]], dtype=bm.float64), + bm.tensor([[1, 0]], dtype=bm.float64),) +bcs5 = (bm.tensor([[1, 0]], dtype=bm.float64), + bm.tensor([[1, 0]], dtype=bm.float64), + bm.tensor([[0, 1]], dtype=bm.float64),) +bcs6 = (bm.tensor([[0, 1]], dtype=bm.float64), + bm.tensor([[1, 0]], dtype=bm.float64), + bm.tensor([[0, 1]], dtype=bm.float64),) +bcs7 = (bm.tensor([[0, 1]], dtype=bm.float64), + bm.tensor([[0, 1]], dtype=bm.float64), + bm.tensor([[0, 1]], dtype=bm.float64),) +bcs8 = (bm.tensor([[1, 0]], dtype=bm.float64), + bm.tensor([[0, 1]], dtype=bm.float64), + bm.tensor([[0, 1]], dtype=bm.float64),) + +tgphi1 = tensor_space.grad_basis(bcs1) # (NC, 1, tldof, GD, GD) +tgphi2 = tensor_space.grad_basis(bcs2) # (NC, 1, tldof, GD, GD) +tgphi3 = tensor_space.grad_basis(bcs3) # (NC, 1, tldof, GD, GD) +tgphi4 = tensor_space.grad_basis(bcs4) # (NC, 1, tldof, GD, GD) +tgphi5 = tensor_space.grad_basis(bcs5) # (NC, 1, tldof, GD, GD) +tgphi6 = tensor_space.grad_basis(bcs6) # (NC, 1, tldof, GD, GD) +tgphi7 = tensor_space.grad_basis(bcs7) # (NC, 1, tldof, GD, GD) +tgphi8 = tensor_space.grad_basis(bcs8) # (NC, 1, tldof, GD, GD) + +tgphi = bm.concatenate([tgphi1, tgphi2, tgphi3, tgphi4, + tgphi5, tgphi6, tgphi7, tgphi8], axis=1) # (NC, 8, tldof, GD, GD) + +tgrad = bm.einsum('cqimn, ci -> cqmn', tgphi, uh_cell) # (NC, 8, GD, GD) +strain = (tgrad + bm.transpose(tgrad, (0, 1, 3, 2))) / 2 # (NC, 8, GD, GD) +strain_xx = strain[..., 0, 0] # (NC, 8) +strain_yy = strain[..., 1, 1] # (NC, 8) +strain_zz = strain[..., 2, 2] # (NC, 8) +strain_xy = strain[..., 0, 1] # (NC, 8) +strain_yz = strain[..., 1, 2] # (NC, 8) +strain_xz = strain[..., 0, 2] # (NC, 8) + +equiv_strain = compute_equivalent_strain(strain, nu) # (NC, 8) + +nodal_equiv_strain = bm.zeros(NN, dtype=bm.float64) +num_count = bm.zeros(NN, dtype=bm.int32) +bm.add_at(nodal_equiv_strain, cell2dof.flatten(), equiv_strain.flatten()) +bm.add_at(num_count, cell2dof.flatten(), 1) +nodal_equiv_strain = nodal_equiv_strain / num_count + +nodal_strain_xx = bm.zeros(NN, dtype=bm.float64) +bm.set_at(nodal_strain_xx, cell2dof.flatten(), strain_xx.flatten()) +nodal_strain_yy = bm.zeros(NN, dtype=bm.float64) +bm.set_at(nodal_strain_yy, cell2dof.flatten(), strain_yy.flatten()) +nodal_strain_zz = bm.zeros(NN, dtype=bm.float64) +bm.set_at(nodal_strain_zz, cell2dof.flatten(), strain_zz.flatten()) +nodal_strain_xy = bm.zeros(NN, dtype=bm.float64) +bm.set_at(nodal_strain_xy, cell2dof.flatten(), strain_xy.flatten()) +nodal_strain_yz = bm.zeros(NN, dtype=bm.float64) +bm.set_at(nodal_strain_yz, cell2dof.flatten(), strain_yz.flatten()) +nodal_strain_xz = bm.zeros(NN, dtype=bm.float64) +bm.set_at(nodal_strain_xz, cell2dof.flatten(), strain_xz.flatten()) + +mesh.nodedata['nodal_equiv_strain'] = nodal_equiv_strain[:] +mesh.nodedata['nodal_strain_xx'] = nodal_strain_xx[:] +mesh.nodedata['nodal_strain_yy'] = nodal_strain_yy[:] +mesh.nodedata['nodal_strain_zz'] = nodal_strain_zz[:] +mesh.nodedata['nodal_strain_xy'] = nodal_strain_xy[:] +mesh.nodedata['nodal_strain_yz'] = nodal_strain_yz[:] +mesh.nodedata['nodal_strain_xz'] = nodal_strain_xz[:] +mesh.to_vtk('/home/heliang/FEALPy_Development/fealpy/app/soptx/linear_elasticity/gear_fealpy.vtu') + -mesh.nodedata['deform'] = uh[:] -mesh.to_vtk('/home/heliang/FEALPy_Development/fealpy/app/soptx/linear_elasticity/gear.vtu') print("-----------") \ No newline at end of file diff --git a/app/soptx/linear_elasticity/test_local_assembly.py b/app/soptx/linear_elasticity/test_local_assemble_method.py similarity index 100% rename from app/soptx/linear_elasticity/test_local_assembly.py rename to app/soptx/linear_elasticity/test_local_assemble_method.py diff --git a/fealpy/fem/linear_elastic_integrator.py b/fealpy/fem/linear_elastic_integrator.py index d2025b817..5ef2ad79e 100644 --- a/fealpy/fem/linear_elastic_integrator.py +++ b/fealpy/fem/linear_elastic_integrator.py @@ -107,7 +107,6 @@ def assembly(self, space: _TS) -> TensorLike: @assemblymethod('fast_strain') def fast_assembly_strain(self, space: _TS) -> TensorLike: scalar_space = space.scalar_space - ws, cm, mesh, gphi_lambda, glambda_x = self.fetch_fast_assembly(scalar_space) if not isinstance(mesh, SimplexMesh): @@ -133,9 +132,9 @@ def fast_assembly_strain(self, space: _TS) -> TensorLike: D = self.material.elastic_matrix() if D.shape[0] != 1: raise ValueError("Elastic matrix D must have shape (NC, 1, 3, 3) or (1, 1, 3, 3).") - D00 = D[..., 0, 0, None] # 2*\mu + \lambda - D01 = D[..., 0, 1, None] # \lambda - D22 = D[..., 2, 2, None] # \mu + D00 = D[..., 0, 0, None] # E / (1-\nu^2) * 1 + D01 = D[..., 0, 1, None] # E / (1-\nu^2) * \nu + D22 = D[..., 2, 2, None] # E / (1-\nu^2) * (1-nu)/2 if space.dof_priority: # Fill the diagonal part @@ -178,8 +177,8 @@ def fast_assembly_stress(self, space: _TS) -> TensorLike: NC = mesh.number_of_cells() ldof = scalar_space.number_of_local_dofs() - - KK = bm.zeros((NC, GD * ldof, GD * ldof), dtype=bm.float64) + tldof = space.number_of_local_dofs() + KK = bm.zeros((NC, tldof, tldof), dtype=bm.float64) # TODO 只能处理 (NC, 1, 3, 3) 和 (1, 1, 3, 3) 的情况 D = self.material.elastic_matrix() From 011d93a64c29a62a1053cd74f93d005164fff70b Mon Sep 17 00:00:00 2001 From: Mualani Date: Thu, 5 Dec 2024 11:53:53 +0800 Subject: [PATCH 63/63] update version to 3.0.2 --- fealpy/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fealpy/__init__.py b/fealpy/__init__.py index 26eb49433..adadbe579 100644 --- a/fealpy/__init__.py +++ b/fealpy/__init__.py @@ -1,6 +1,6 @@ import logging -__version__ = '3.0.0' +__version__ = '3.0.2' logger = logging.getLogger('fealpy') logger.setLevel(logging.WARNING)