许可优化
许可优化
产品
产品
解决方案
解决方案
服务支持
服务支持
关于
关于
软件库
当前位置:服务支持 >  软件文章 >  ABAQUS中使用扩展有限元(XFEM)的局限性

ABAQUS中使用扩展有限元(XFEM)的局限性

阅读数 3
点赞 0
article_banner


本代码是针对 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




      复制成功
     
     
     
     



免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删

相关文章
技术文档
QR Code
微信扫一扫,欢迎咨询~
customer

online

联系我们
武汉格发信息技术有限公司
湖北省武汉市经开区科技园西路6号103孵化器
电话:155-2731-8020 座机:027-59821821
邮件:tanzw@gofarlic.com
Copyright © 2023 Gofarsoft Co.,Ltd. 保留所有权利
遇到许可问题?该如何解决!?
评估许可证实际采购量? 
不清楚软件许可证使用数据? 
收到软件厂商律师函!?  
想要少购买点许可证,节省费用? 
收到软件厂商侵权通告!?  
有正版license,但许可证不够用,需要新购? 
联系方式 board-phone 155-2731-8020
close1
预留信息,一起解决您的问题
* 姓名:
* 手机:

* 公司名称:

姓名不为空

姓名不为空

姓名不为空
手机不正确

手机不正确

手机不正确
公司不为空

公司不为空

公司不为空