本代码是针对 Abaqus/CAE 的自动化辅助脚本,实现主节点(控制点)与从面的智能匹配及 MPC 约束批量创建。适用于大规模模型中 “单个主节点需连接指定数量从面” 的场景(如网格划分后原始大面切割为多个小面,需主节点同时连接所有小面的部分场景),可替代手动筛选面、创建约束的繁琐操作,大幅提升建模效率并保证匹配逻辑的一致性。
代码块
Python
自动换行
from abaqus import *
from abaqusConstants import *
import numpy as np
import regionToolset
import heapq
# ==================================================
# ================== 【用户必改参数】 ==================
# ==================================================
MODEL_NAME = "down wind" # 模型名称(与Abaqus模型树一致)
MASTER_SET_NAME = "Prestress-End-1" # 主节点集合(类型:顶点)
SLAVE_FACE_SET_NAME = "YJM-S-1" # 从面集合(类型:面)
MAX_MATCH_DISTANCE = 300.0 # 最大匹配距离(面不足时可增大)
TOP_N_FACES = 4 # 每个主节点目标连接面数量(1,2,3...)
# ==================================================
# ================== 【优化/调试参数】 ==================
# ==================================================
GRID_SIZE = MAX_MATCH_DISTANCE * 1.0 # 网格尺寸(建议0.8-1.2×MAX_DIST)
VERTEX_LIMIT = 3 # 每个面用前3个顶点计算信息
EARLY_EXIT_THRESHOLD = 0 # 关闭提前终止(确保遍历所有候选面)
PRINT_DEBUG_INFO = 0 # 0=关闭调试输出,1=开启(查看匹配细节)
# ==================================================
# --------------------------
# 1. 初始化变量(含统计和调试存储)
# --------------------------
instance_data = {} # 实例基本信息
instance_vert_coords = {} # 预缓存实例顶点坐标
face_info_list = [] # 面信息列表
grid = {} # 空间网格:{网格键: {faces: [], center: [], max_radius: }}
master_nodes_by_inst = {} # {实例名: [主节点列表]}
match_pairs_by_inst = {} # {实例名: [(主节点, [匹配到的面]), ...]}
match_statistics = [] # 匹配结果统计:[(主节点索引, 实例名, 实际面数量), ...]
# --------------------------
# 2. 辅助函数(必须在FaceInfo类之前定义,因FaceInfo依赖)
# --------------------------
def get_face_instance_name(face):
return face.instanceName # 直接通过face属性获取实例名
# --------------------------
# 3. 面信息预计算类(必须在引用前定义,修复NameError的核心)
# --------------------------
class FaceInfo:
def __init__(self, face):
self.face = face
self.inst_name = get_face_instance_name(face) # 依赖get_face_instance_name函数
self.is_valid = self._check_validity()
if not self.is_valid:
self.center = np.zeros(3, dtype=np.float64)
self.bbox = (0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
self.bbox_radius = 0.0
self.vert_coords = np.zeros((0, 3), dtype=np.float64)
return
# 预缓存有效顶点索引
raw_vert_indices = face.getVertices()
self.vert_indices = []
for idx in raw_vert_indices[:VERTEX_LIMIT]:
if 0 <= idx < instance_data[self.inst_name]['vert_count']:
self.vert_indices.append(idx)
self.vert_indices = np.array(self.vert_indices, dtype=np.int32)
# 正确获取顶点坐标(二维数组索引)
if self.inst_name in instance_vert_coords and instance_vert_coords[self.inst_name].size > 0:
self.vert_coords = instance_vert_coords[self.inst_name][self.vert_indices, :]
else:
self.vert_coords = np.zeros((len(self.vert_indices), 3), dtype=np.float64)
# 计算核心信息
self.center = np.mean(self.vert_coords, axis=0) if len(self.vert_coords) > 0 else np.zeros(3)
self.bbox = self._get_bounding_box()
self.bbox_radius = np.max(np.linalg.norm(self.vert_coords - self.center, axis=1)) if len(self.vert_coords) > 0 else 0.0
def _check_validity(self):
# 检查实例是否存在+面顶点数量是否≥3
if self.inst_name is None or self.inst_name not in instance_data:
return False
try:
return len(face.getVertices()) >= 3
except:
return False
def _get_bounding_box(self):
if len(self.vert_coords) == 0:
return (0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
xmin, xmax = self.vert_coords[:, 0].min(), self.vert_coords[:, 0].max()
ymin, ymax = self.vert_coords[:, 1].min(), self.vert_coords[:, 1].max()
zmin, zmax = self.vert_coords[:, 2].min(), self.vert_coords[:, 2].max()
return (xmin, xmax, ymin, ymax, zmin, zmax)
# --------------------------
# 4. 预加载数据(按实例分组,此时FaceInfo已定义,可安全引用)
# --------------------------
assembly = mdb.models[MODEL_NAME].rootAssembly
# 4.1 缓存实例顶点坐标+按实例分组主节点
for inst in assembly.instances.values():
instance_data[inst.name] = {
'vertices': inst.vertices,
'vert_count': len(inst.vertices),
'faces': inst.faces
}
# 预缓存顶点坐标(二维数组 [n_vertices, 3])
vert_coords = []
for vert in inst.vertices:
vert_coords.append(list(vert.pointOn[0]))
instance_vert_coords[inst.name] = np.array(vert_coords, dtype=np.float64)
# 按实例筛选主节点
inst_master_nodes = []
for master_node in assembly.sets[MASTER_SET_NAME].vertices:
if master_node.instanceName == inst.name:
inst_master_nodes.append(master_node)
if inst_master_nodes:
master_nodes_by_inst[inst.name] = inst_master_nodes
# 4.2 按实例分组从面
slave_faces_by_inst = {}
for inst in assembly.instances.values():
inst_slave_faces = []
for face in assembly.sets[SLAVE_FACE_SET_NAME].faces:
if face.instanceName == inst.name:
inst_slave_faces.append(face)
if inst_slave_faces:
slave_faces_by_inst[inst.name] = inst_slave_faces
# 4.3 预缓存从面信息+初始化空间网格(此处引用FaceInfo,已提前定义)
all_slave_faces = assembly.sets[SLAVE_FACE_SET_NAME].faces
for face in all_slave_faces:
face_info_list.append(FaceInfo(face)) # 修复:此时FaceInfo已定义,无NameError
valid_face_infos = [info for info in face_info_list if info.is_valid and len(info.vert_coords) > 0]
# 空间网格分区(所有从面统一分区)
for info in valid_face_infos:
grid_x = int(info.center[0] // GRID_SIZE)
grid_y = int(info.center[1] // GRID_SIZE)
grid_z = int(info.center[2] // GRID_SIZE)
grid_key = (grid_x, grid_y, grid_z)
if grid_key not in grid:
grid[grid_key] = {
'faces': [],
'center': np.array([(grid_x + 0.5)*GRID_SIZE, (grid_y + 0.5)*GRID_SIZE, (grid_z + 0.5)*GRID_SIZE], dtype=np.float64),
'max_radius': 0.0
}
grid[grid_key]['faces'].append(info)
if info.bbox_radius > grid[grid_key]['max_radius']:
grid[grid_key]['max_radius'] = info.bbox_radius
# --------------------------
# 5. 距离计算函数
# --------------------------
def point_to_bbox_distance(p_coord, bbox):
xmin, xmax, ymin, ymax, zmin, zmax = bbox
dx = max(xmin - p_coord[0], 0.0, p_coord[0] - xmax)
dy = max(ymin - p_coord[1], 0.0, p_coord[1] - ymax)
dz = max(zmin - p_coord[2], 0.0, p_coord[2] - zmax)
return np.sqrt(dx**2 + dy**2 + dz**2)
def precise_point_to_face(p_coord, face_info):
if len(face_info.vert_coords) == 0:
return float('inf')
vert_diffs = face_info.vert_coords - p_coord
return np.linalg.norm(vert_diffs, axis=1).min()
# --------------------------
# 6. 主节点匹配(核心修复:等值面处理+强制补全)
# --------------------------
global_master_idx = 0 # 全局主节点索引(用于统计)
for inst_name, master_nodes in master_nodes_by_inst.items():
master_coords = []
for node in master_nodes:
master_coords.append(list(node.pointOn[0]))
master_coords = np.array(master_coords, dtype=np.float64)
# 遍历当前实例的每个主节点
for idx, master_node in enumerate(master_nodes):
p_coord = master_coords[idx] if idx < len(master_coords) else np.zeros(3)
heap = [] # 存储(-距离, 面),用负距离实现最大堆
all_candidate_faces = [] # 缓存所有符合条件的面(含距离)
# 3×3×3网格范围(确保候选面充足)
grid_x = int(p_coord[0] // GRID_SIZE)
grid_y = int(p_coord[1] // GRID_SIZE)
grid_z = int(p_coord[2] // GRID_SIZE)
nearby_grids = [
(grid_x + dx, grid_y + dy, grid_z + dz)
for dx in (-1, 0, 1) for dy in (-1, 0, 1) for dz in (-1, 0, 1)
]
# 第一步:收集所有符合距离条件的候选面(含距离信息)
for g in nearby_grids:
if g not in grid:
continue
grid_info = grid[g]
# 网格级提前过滤
grid_dist = np.linalg.norm(p_coord - grid_info['center'])
if grid_dist > MAX_MATCH_DISTANCE + grid_info['max_radius']:
continue
# 遍历网格内的面,筛选符合条件的候选面
for info in grid_info['faces']:
bbox_dist = point_to_bbox_distance(p_coord, info.bbox)
if bbox_dist > MAX_MATCH_DISTANCE:
continue
precise_dist = precise_point_to_face(p_coord, info)
if precise_dist > MAX_MATCH_DISTANCE:
continue
# 缓存候选面(距离+面对象)
all_candidate_faces.append((precise_dist, info.face))
# 第二步:处理候选面,确保纳入等值面并补全数量
if all_candidate_faces:
# 按距离升序排序(便于优先选择最近面,处理等值面)
all_candidate_faces.sort(key=lambda x: x[0])
# 填充优先堆(允许等值面加入)
for dist, face in all_candidate_faces:
if len(heap) < TOP_N_FACES:
# 堆未满:直接加入
heapq.heappush(heap, (-dist, face))
else:
# 堆已满:当前距离<=堆顶距离(等值也加入)
if dist <= -heap[0][0]:
heapq.heappop(heap)
heapq.heappush(heap, (-dist, face))
# 强制补全:若堆未满,从等值面中补充至TOP_N_FACES
if len(heap) < TOP_N_FACES:
# 确定当前堆中最小距离(或候选面最小距离)
if heap:
current_min_dist = -heap[0][0]
else:
current_min_dist = all_candidate_faces[0][0] # 无堆时取候选面最小距离
# 筛选所有距离等于最小距离的等值面
equal_dist_faces = [face for dist, face in all_candidate_faces if dist == current_min_dist]
# 计算需要补充的数量
need_count = TOP_N_FACES - len(heap)
# 从等值面中补充(避免重复)
added_faces = []
for face in equal_dist_faces:
if need_count <= 0:
break
# 检查面是否已在堆中(避免重复添加)
in_heap = any(face == existing_face for _, existing_face in heap)
if not in_heap:
heapq.heappush(heap, (-current_min_dist, face))
added_faces.append(face)
need_count -= 1
# 调试输出:打印当前主节点的匹配细节(可选开启)
if PRINT_DEBUG_INFO:
print("="*60)
print(f"主节点{global_master_idx}(实例:{inst_name})匹配详情:")
print(f" 控制点坐标:({p_coord[0]:.2f}, {p_coord[1]:.2f}, {p_coord[2]:.2f})")
print(f" 候选面总数:{len(all_candidate_faces)} 个")
print(f" 最终匹配面数量:{len(heap)} 个(目标:{TOP_N_FACES}个)")
print(" 匹配结果(距离+面实例):")
for neg_dist, face in sorted(heap, key=lambda x: x[0]):
dist = -neg_dist
print(f" 距离:{dist:.6f},面实例:{face.instanceName}")
# 存储匹配结果(无论数量是否达标,均保留)
if heap:
matched_faces = [face for (neg_dist, face) in sorted(heap, key=lambda x: -x[0])]
if inst_name not in match_pairs_by_inst:
match_pairs_by_inst[inst_name] = []
match_pairs_by_inst[inst_name].append((master_node, matched_faces))
match_statistics.append((global_master_idx, inst_name, len(matched_faces)))
global_master_idx += 1
# --------------------------
# 7. 创建约束(按实例分组调用findAt,新增全局MPC序号)
# --------------------------
# 新增:全局MPC序号计数器(从1开始,直观反映总数)
global_constraint_idx = 1
for inst_name, match_pairs in match_pairs_by_inst.items():
inst = assembly.instances[inst_name]
inst_master_vertices = inst.vertices # 当前实例的顶点序列(支持findAt)
inst_slave_faces = inst.faces # 当前实例的面序列(支持findAt)
for master_idx, (master_node, matched_faces) in enumerate(match_pairs):
# 1. 创建主节点区域(单个实例内,避免跨实例错误)
try:
master_geom = inst_master_vertices.findAt(master_node.pointOn)
master_region = regionToolset.Region(vertices=master_geom)
except Exception as e:
print(f"警告:主节点{global_master_idx}(实例:{inst_name})区域创建失败,跳过:{str(e)}")
continue
# 2. 合并匹配到的所有面(支持跨实例)
combined_faces = None
for face in matched_faces:
face_inst_name = face.instanceName
try:
# 跨实例面处理:用面所属实例的序列findAt
if face_inst_name != inst_name:
face_inst = assembly.instances[face_inst_name]
face_geom = face_inst.faces.findAt(face.pointOn)
else:
face_geom = inst_slave_faces.findAt(face.pointOn)
if face_geom:
combined_faces = face_geom if combined_faces is None else combined_faces + face_geom
except Exception as e:
print(f"警告:面合并失败(实例:{face_inst_name}),跳过:{str(e)}")
continue
# 3. 创建MPC约束(名称含【全局序号】+实例+主节点索引,直观清晰)
if combined_faces is not None:
try:
slave_region = regionToolset.Region(faces=combined_faces)
actual_face_count = len(matched_faces)
# 约束名称格式:主集合名_MPC_全局序号(如Press-fix-1_MPC_1)
constraint_name = f"MPC_{MASTER_SET_NAME}_{global_constraint_idx}"
mdb.models[MODEL_NAME].MultipointConstraint(
name=constraint_name,
controlPoint=master_region,
surface=slave_region,
mpcType=BEAM_MPC,
userMode=DOF_MODE_MPC,
csys=None
)
# 序号递增(仅成功创建时递增)
global_constraint_idx += 1
except Exception as e:
print(f"警告:约束创建失败(主节点{global_master_idx}),跳过:{str(e)}")
continue
from abaqus import *
from abaqusConstants import *
import numpy as np
import regionToolset
import heapq
# ==================================================
# ================== 【用户必改参数】 ==================
# ==================================================
MODEL_NAME = "down wind" # 模型名称(与Abaqus模型树一致)
MASTER_SET_NAME = "Prestress-End-1" # 主节点集合(类型:顶点)
SLAVE_FACE_SET_NAME = "YJM-S-1" # 从面集合(类型:面)
MAX_MATCH_DISTANCE = 300.0 # 最大匹配距离(面不足时可增大)
TOP_N_FACES = 4 # 每个主节点目标连接面数量(1,2,3...)
# ==================================================
# ================== 【优化/调试参数】 ==================
# ==================================================
GRID_SIZE = MAX_MATCH_DISTANCE * 1.0 # 网格尺寸(建议0.8-1.2×MAX_DIST)
VERTEX_LIMIT = 3 # 每个面用前3个顶点计算信息
EARLY_EXIT_THRESHOLD = 0 # 关闭提前终止(确保遍历所有候选面)
PRINT_DEBUG_INFO = 0 # 0=关闭调试输出,1=开启(查看匹配细节)
# ==================================================
# --------------------------
# 1. 初始化变量(含统计和调试存储)
# --------------------------
instance_data = {} # 实例基本信息
instance_vert_coords = {} # 预缓存实例顶点坐标
face_info_list = [] # 面信息列表
grid = {} # 空间网格:{网格键: {faces: [], center: [], max_radius: }}
master_nodes_by_inst = {} # {实例名: [主节点列表]}
match_pairs_by_inst = {} # {实例名: [(主节点, [匹配到的面]), ...]}
match_statistics = [] # 匹配结果统计:[(主节点索引, 实例名, 实际面数量), ...]
# --------------------------
# 2. 辅助函数(必须在FaceInfo类之前定义,因FaceInfo依赖)
# --------------------------
def get_face_instance_name(face):
return face.instanceName # 直接通过face属性获取实例名
# --------------------------
# 3. 面信息预计算类(必须在引用前定义,修复NameError的核心)
# --------------------------
class FaceInfo:
def __init__(self, face):
self.face = face
self.inst_name = get_face_instance_name(face) # 依赖get_face_instance_name函数
self.is_valid = self._check_validity()
if not self.is_valid:
self.center = np.zeros(3, dtype=np.float64)
self.bbox = (0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
self.bbox_radius = 0.0
self.vert_coords = np.zeros((0, 3), dtype=np.float64)
return
# 预缓存有效顶点索引
raw_vert_indices = face.getVertices()
self.vert_indices = []
for idx in raw_vert_indices[:VERTEX_LIMIT]:
if 0 <= idx < instance_data[self.inst_name]['vert_count']:
self.vert_indices.append(idx)
self.vert_indices = np.array(self.vert_indices, dtype=np.int32)
# 正确获取顶点坐标(二维数组索引)
if self.inst_name in instance_vert_coords and instance_vert_coords[self.inst_name].size > 0:
self.vert_coords = instance_vert_coords[self.inst_name][self.vert_indices, :]
else:
self.vert_coords = np.zeros((len(self.vert_indices), 3), dtype=np.float64)
# 计算核心信息
self.center = np.mean(self.vert_coords, axis=0) if len(self.vert_coords) > 0 else np.zeros(3)
self.bbox = self._get_bounding_box()
self.bbox_radius = np.max(np.linalg.norm(self.vert_coords - self.center, axis=1)) if len(self.vert_coords) > 0 else 0.0
def _check_validity(self):
# 检查实例是否存在+面顶点数量是否≥3
if self.inst_name is None or self.inst_name not in instance_data:
return False
try:
return len(face.getVertices()) >= 3
except:
return False
def _get_bounding_box(self):
if len(self.vert_coords) == 0:
return (0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
xmin, xmax = self.vert_coords[:, 0].min(), self.vert_coords[:, 0].max()
ymin, ymax = self.vert_coords[:, 1].min(), self.vert_coords[:, 1].max()
zmin, zmax = self.vert_coords[:, 2].min(), self.vert_coords[:, 2].max()
return (xmin, xmax, ymin, ymax, zmin, zmax)
# --------------------------
# 4. 预加载数据(按实例分组,此时FaceInfo已定义,可安全引用)
# --------------------------
assembly = mdb.models[MODEL_NAME].rootAssembly
# 4.1 缓存实例顶点坐标+按实例分组主节点
for inst in assembly.instances.values():
instance_data[inst.name] = {
'vertices': inst.vertices,
'vert_count': len(inst.vertices),
'faces': inst.faces
}
# 预缓存顶点坐标(二维数组 [n_vertices, 3])
vert_coords = []
for vert in inst.vertices:
vert_coords.append(list(vert.pointOn[0]))
instance_vert_coords[inst.name] = np.array(vert_coords, dtype=np.float64)
# 按实例筛选主节点
inst_master_nodes = []
for master_node in assembly.sets[MASTER_SET_NAME].vertices:
if master_node.instanceName == inst.name:
inst_master_nodes.append(master_node)
if inst_master_nodes:
master_nodes_by_inst[inst.name] = inst_master_nodes
# 4.2 按实例分组从面
slave_faces_by_inst = {}
for inst in assembly.instances.values():
inst_slave_faces = []
for face in assembly.sets[SLAVE_FACE_SET_NAME].faces:
if face.instanceName == inst.name:
inst_slave_faces.append(face)
if inst_slave_faces:
slave_faces_by_inst[inst.name] = inst_slave_faces
# 4.3 预缓存从面信息+初始化空间网格(此处引用FaceInfo,已提前定义)
all_slave_faces = assembly.sets[SLAVE_FACE_SET_NAME].faces
for face in all_slave_faces:
face_info_list.append(FaceInfo(face)) # 修复:此时FaceInfo已定义,无NameError
valid_face_infos = [info for info in face_info_list if info.is_valid and len(info.vert_coords) > 0]
# 空间网格分区(所有从面统一分区)
for info in valid_face_infos:
grid_x = int(info.center[0] // GRID_SIZE)
grid_y = int(info.center[1] // GRID_SIZE)
grid_z = int(info.center[2] // GRID_SIZE)
grid_key = (grid_x, grid_y, grid_z)
if grid_key not in grid:
grid[grid_key] = {
'faces': [],
'center': np.array([(grid_x + 0.5)*GRID_SIZE, (grid_y + 0.5)*GRID_SIZE, (grid_z + 0.5)*GRID_SIZE], dtype=np.float64),
'max_radius': 0.0
}
grid[grid_key]['faces'].append(info)
if info.bbox_radius > grid[grid_key]['max_radius']:
grid[grid_key]['max_radius'] = info.bbox_radius
# --------------------------
# 5. 距离计算函数
# --------------------------
def point_to_bbox_distance(p_coord, bbox):
xmin, xmax, ymin, ymax, zmin, zmax = bbox
dx = max(xmin - p_coord[0], 0.0, p_coord[0] - xmax)
dy = max(ymin - p_coord[1], 0.0, p_coord[1] - ymax)
dz = max(zmin - p_coord[2], 0.0, p_coord[2] - zmax)
return np.sqrt(dx**2 + dy**2 + dz**2)
def precise_point_to_face(p_coord, face_info):
if len(face_info.vert_coords) == 0:
return float('inf')
vert_diffs = face_info.vert_coords - p_coord
return np.linalg.norm(vert_diffs, axis=1).min()
# --------------------------
# 6. 主节点匹配(核心修复:等值面处理+强制补全)
# --------------------------
global_master_idx = 0 # 全局主节点索引(用于统计)
for inst_name, master_nodes in master_nodes_by_inst.items():
master_coords = []
for node in master_nodes:
master_coords.append(list(node.pointOn[0]))
master_coords = np.array(master_coords, dtype=np.float64)
# 遍历当前实例的每个主节点
for idx, master_node in enumerate(master_nodes):
p_coord = master_coords[idx] if idx < len(master_coords) else np.zeros(3)
heap = [] # 存储(-距离, 面),用负距离实现最大堆
all_candidate_faces = [] # 缓存所有符合条件的面(含距离)
# 3×3×3网格范围(确保候选面充足)
grid_x = int(p_coord[0] // GRID_SIZE)
grid_y = int(p_coord[1] // GRID_SIZE)
grid_z = int(p_coord[2] // GRID_SIZE)
nearby_grids = [
(grid_x + dx, grid_y + dy, grid_z + dz)
for dx in (-1, 0, 1) for dy in (-1, 0, 1) for dz in (-1, 0, 1)
]
# 第一步:收集所有符合距离条件的候选面(含距离信息)
for g in nearby_grids:
if g not in grid:
continue
grid_info = grid[g]
# 网格级提前过滤
grid_dist = np.linalg.norm(p_coord - grid_info['center'])
if grid_dist > MAX_MATCH_DISTANCE + grid_info['max_radius']:
continue
# 遍历网格内的面,筛选符合条件的候选面
for info in grid_info['faces']:
bbox_dist = point_to_bbox_distance(p_coord, info.bbox)
if bbox_dist > MAX_MATCH_DISTANCE:
continue
precise_dist = precise_point_to_face(p_coord, info)
if precise_dist > MAX_MATCH_DISTANCE:
continue
# 缓存候选面(距离+面对象)
all_candidate_faces.append((precise_dist, info.face))
# 第二步:处理候选面,确保纳入等值面并补全数量
if all_candidate_faces:
# 按距离升序排序(便于优先选择最近面,处理等值面)
all_candidate_faces.sort(key=lambda x: x[0])
# 填充优先堆(允许等值面加入)
for dist, face in all_candidate_faces:
if len(heap) < TOP_N_FACES:
# 堆未满:直接加入
heapq.heappush(heap, (-dist, face))
else:
# 堆已满:当前距离<=堆顶距离(等值也加入)
if dist <= -heap[0][0]:
heapq.heappop(heap)
heapq.heappush(heap, (-dist, face))
# 强制补全:若堆未满,从等值面中补充至TOP_N_FACES
if len(heap) < TOP_N_FACES:
# 确定当前堆中最小距离(或候选面最小距离)
if heap:
current_min_dist = -heap[0][0]
else:
current_min_dist = all_candidate_faces[0][0] # 无堆时取候选面最小距离
# 筛选所有距离等于最小距离的等值面
equal_dist_faces = [face for dist, face in all_candidate_faces if dist == current_min_dist]
# 计算需要补充的数量
need_count = TOP_N_FACES - len(heap)
# 从等值面中补充(避免重复)
added_faces = []
for face in equal_dist_faces:
if need_count <= 0:
break
# 检查面是否已在堆中(避免重复添加)
in_heap = any(face == existing_face for _, existing_face in heap)
if not in_heap:
heapq.heappush(heap, (-current_min_dist, face))
added_faces.append(face)
need_count -= 1
# 调试输出:打印当前主节点的匹配细节(可选开启)
if PRINT_DEBUG_INFO:
print("="*60)
print(f"主节点{global_master_idx}(实例:{inst_name})匹配详情:")
print(f" 控制点坐标:({p_coord[0]:.2f}, {p_coord[1]:.2f}, {p_coord[2]:.2f})")
print(f" 候选面总数:{len(all_candidate_faces)} 个")
print(f" 最终匹配面数量:{len(heap)} 个(目标:{TOP_N_FACES}个)")
print(" 匹配结果(距离+面实例):")
for neg_dist, face in sorted(heap, key=lambda x: x[0]):
dist = -neg_dist
print(f" 距离:{dist:.6f},面实例:{face.instanceName}")
# 存储匹配结果(无论数量是否达标,均保留)
if heap:
matched_faces = [face for (neg_dist, face) in sorted(heap, key=lambda x: -x[0])]
if inst_name not in match_pairs_by_inst:
match_pairs_by_inst[inst_name] = []
match_pairs_by_inst[inst_name].append((master_node, matched_faces))
match_statistics.append((global_master_idx, inst_name, len(matched_faces)))
global_master_idx += 1
# --------------------------
# 7. 创建约束(按实例分组调用findAt,新增全局MPC序号)
# --------------------------
# 新增:全局MPC序号计数器(从1开始,直观反映总数)
global_constraint_idx = 1
for inst_name, match_pairs in match_pairs_by_inst.items():
inst = assembly.instances[inst_name]
inst_master_vertices = inst.vertices # 当前实例的顶点序列(支持findAt)
inst_slave_faces = inst.faces # 当前实例的面序列(支持findAt)
for master_idx, (master_node, matched_faces) in enumerate(match_pairs):
# 1. 创建主节点区域(单个实例内,避免跨实例错误)
try:
master_geom = inst_master_vertices.findAt(master_node.pointOn)
master_region = regionToolset.Region(vertices=master_geom)
except Exception as e:
print(f"警告:主节点{global_master_idx}(实例:{inst_name})区域创建失败,跳过:{str(e)}")
continue
# 2. 合并匹配到的所有面(支持跨实例)
combined_faces = None
for face in matched_faces:
face_inst_name = face.instanceName
try:
# 跨实例面处理:用面所属实例的序列findAt
if face_inst_name != inst_name:
face_inst = assembly.instances[face_inst_name]
face_geom = face_inst.faces.findAt(face.pointOn)
else:
face_geom = inst_slave_faces.findAt(face.pointOn)
if face_geom:
combined_faces = face_geom if combined_faces is None else combined_faces + face_geom
except Exception as e:
print(f"警告:面合并失败(实例:{face_inst_name}),跳过:{str(e)}")
continue
# 3. 创建MPC约束(名称含【全局序号】+实例+主节点索引,直观清晰)
if combined_faces is not None:
try:
slave_region = regionToolset.Region(faces=combined_faces)
actual_face_count = len(matched_faces)
# 约束名称格式:主集合名_MPC_全局序号(如Press-fix-1_MPC_1)
constraint_name = f"MPC_{MASTER_SET_NAME}_{global_constraint_idx}"
mdb.models[MODEL_NAME].MultipointConstraint(
name=constraint_name,
controlPoint=master_region,
surface=slave_region,
mpcType=BEAM_MPC,
userMode=DOF_MODE_MPC,
csys=None
)
# 序号递增(仅成功创建时递增)
global_constraint_idx += 1
except Exception as e:
print(f"警告:约束创建失败(主节点{global_master_idx}),跳过:{str(e)}")
continue
复制成功
免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删