
简介这套Abaqus脚本面向需要频繁创建球体模型并完成多球体装配的仿真工程师、科研人员及相关专业学生专注解决参数化建模与位置控制效率低下的问题。通过修改半径与坐标参数即可自动生成任意尺寸的球体并按指定位置装配省去重复手动操作尤其适用于多球体接触分析、颗粒材料模拟、结构简化建模等场景。包体非常精简仅含1个py脚本文件整体大小约1KB无需复杂配置即可在Abaqus中运行适合作为二次开发入门或日常建模的辅助工具。已有307人学习下载脚本支持批量生成可在一次运行中创建多个不同半径、不同位置的球体显著提升模型搭建与装配流程的自动化程度。读者拿到后可快速套用参数根据需求扩展为更多球体或复杂几何的生成逻辑也可直接嵌入现有Abaqus脚本工作流减少重复性劳动。 先说个需求在Abaqus里建30个随机半径的球体放置位置随机两两不能重叠后面还要作为颗粒嵌进基体做有限元分析。这种需求放在GUI里手动操作点Part、建球体、开Assembly、逐个translate半小时能建完10个就算运气好关键是参数一改就得全部重来。我自己第一次做类似事情的时候就是在Assembly里一个个输坐标输到手酸改一次半径又得从头检查效率低到让人怀疑人生。后来我彻底转向Abaqus脚本建模把建任意半径球体任意位置装配这套流程写成了Python脚本跑一遍几分钟出结果参数随便改。这篇就把完整的思路、脚本、踩坑经验整理出来适合刚接触Abaqus脚本的工程师也适合那些被重复建模工作折磨到想自动化的人。1. 为什么我放弃GUI改用脚本建球体装配1.1 手动操作的真实痛点很多人刚开始用Abaqus习惯了鼠标点菜单觉得脚本没必要学。但当你面对批量生成球体这类需求时GUI的短板暴露得非常明显。首先创建每个球体都要跑一遍Part模块的完整流程即便是复制Part再编辑特征也很繁琐。其次装配时每个Instance都要手动指定位置Abaqus的装配模块里没有输入绝对坐标直接摆放的傻瓜式操作你得先Translate再填三个方向的增量球多了容易搞混位置算错了还不好检查。更麻烦的是如果球体之间存在不能重叠的约束GUI完全没有帮你自动检测或调整的能力只能靠肉眼判断。做10个球你还能勉强盯过来做50个、100个肉眼检查基本等于自欺欺人。这种情况下手工操作的时间成本已经高到不合理的程度。1.2 脚本建模的适用场景和优势脚本建模不是要完全替代GUI而是解决批量、参数化、随机化、可复现这类问题。什么场景适合脚本我归纳了一下需要批量创建几何体数量超过20个时脚本优势明显几何参数需要频繁调整比如改变球体半径范围、改变装配位置脚本改几个变量就能重新跑需要引入随机性比如随机球体、随机颗粒分布GUI里根本没法随机只能靠脚本需要把同一套建模流程反复使用比如做成模板换个参数就给下个项目用脚本建模本质上是用代码描述几何和装配逻辑。好处是每次运行的逻辑完全一致不会因为鼠标点偏或者手抖产生误差参数化之后想改哪个数就改哪个数跑一遍就重新生成。我通常把脚本建模比喻成用模板批量打印文件而GUI操作是手写每一封信前者适合规模化后者适合单件精细操作。2. 脚本建模的核心思路先搞懂Abaqus对象模型2.1 mdb、Part、Instance到底是怎么组织的写Abaqus脚本之前必须先理解Abaqus里对象模型的层级关系否则很容易对着文档一头雾水。Abaqus的Python接口把整个CAE环境抽象成了几个核心对象最常用的是mdb和session。mdb是模型数据库可以理解成工程文件本身所有模型、部件、材料、装配、作业都存在这个对象树里。session是当前会话对应你打开的Abaqus/CAE窗口视口、视图操作都在这里。建模阶段我们主要操作mdb。mdb下面挂着一堆模型每个模型是一个Model对象。一个Model内部又有parts、materials、sections、rootAssembly等子对象。我们创建球体就是在某个Model里创建Part装配时是在这个Model的rootAssembly下创建Instance。要特别注意Part和Instance的区别。Part是几何模板描述几何形状本身Instance是几何模板的摆放引用它挂在一个绝对坐标系下有自己的位置、朝向、缩放。Abaqus的装配对象树里part本身不参与装配只有instance参与。我在脚本里经常用这样几行代码来定位对象myModel mdb.models[Model-1] myPart myModel.parts[Part-1] myInstance myModel.rootAssembly.instances[Part-1-1]这就像找文件路径一样先进入模型库再进模型再进part目录或者装配目录。刚开始不习惯这种层级写多了就很自然。2.2 从.rpy文件到第一条脚本最省力的入门路径很多新手问脚本该怎么学我的建议很直接利用Abaqus自带的.rpy文件。你在Abaqus/CAE界面里做的每一个操作软件都会实时记录到工作目录下的abaqus.rpy文件里这个文件就是一份自动生成的Python脚本。第一次接触脚本时最有效的练习方法是打开Abaqus/CAE手动创建一个球体Part再手动装配平移一次然后打开工目录下的abaqus.rpy文件看看Abaqus自己生成了什么代码。你会看到类似于mdb.models[Model-1].Part(...)和mdb.models[Model-1].rootAssembly.Instance(...)这样的命令。把关键命令摘出来就是你的第一条脚本。我自己写脚本的流程也是这样先在GUI里手动做一遍打开.rpy对照把可复用的部分提取出来加上参数化变量和循环就成了自己的工具脚本。这种方式比单纯看文档学快得多因为你能直接看到操作和代码的对应关系。3. 生成任意半径球体的完整脚本实操3.1 用单位球体实例缩放实现任意半径生成任意半径球体有两种常见思路。第一种是每建一个球体就创建一个独立Part调用BaseSolidSphere(radiusr)第二种是只创建一个单位半径球体Part然后在装配时用实例缩放来改变半径。这两种方式各有优劣。独立Part的好处是每个球体对应一个独立的几何对象后续如果想对某个球单独赋予材料、划分网格、加边界条件操作起来很直接。缺点是当球体数量很多时模型树里会挤满几十上百个Part管理和显示都变吃力。实例缩放scale的方式更加轻量所有球体共享同一个Part几何只是在装配成Instance时通过instance.scale(scaleFactorr)把单位球放大到目标半径。这样Part树里永远只有一个球体Part非常干净。缺点是如果你需要区分每个球体的独立网格属性这种方式会有点绕。我个人的习惯是单纯做几何模型展示和简单装配用单位球实例缩放如果后续要对每个颗粒做独立的材料属性或网格控制就老老实实每个半径建独立Part。下面这段脚本我以独立Part的方式展示因为它更通用也比较容易理解。from abaqus import * from abaqusConstants import * import part import assembly modelName SphereModel if modelName in mdb.models.keys(): del mdb.models[modelName] myModel mdb.Model(namemodelName) # 定义球体参数 spheres [ {radius: 2.0, position: (10.0, 0.0, 0.0)}, {radius: 3.0, position: (0.0, 10.0, 0.0)}, {radius: 1.5, position: (0.0, 0.0, 10.0)}, ] myAssembly myModel.rootAssembly for i, sphere in enumerate(spheres): partName Sphere-%d % (i 1) if partName in myModel.parts.keys(): del myModel.parts[partName] myPart myModel.Part(namepartName, dimensionalityTHREE_D, typeDEFORMABLE_BODY) myPart.BaseSolidSphere(radiussphere[radius]) instName Sphere-%d-1 % (i 1) myInst myAssembly.Instance(nameinstName, partmyPart, dependentON) myInst.translate(vectorsphere[position])这段脚本执行完后模型里就有三个球体Part和三个装配后的Instance。跑脚本的方法很简单在Abaqus/CAE里菜单File - Run Script选择.py文件即可也可以用命令行abaqus cae -noGUI scriptname.py直接批量执行适合无人值守跑任务。3.2 随机位置装配translate增量平移的正确用法上面脚本里的translate(vectorsphere[position])是装配中最常用的定位方式。这里有一个容易踩坑的细节translate不是设定绝对坐标而是在当前坐标基础上增加一个偏移量。实例刚创建时默认位于原点位置。所以这个时候调用translate并传入目标坐标效果等同于设定绝对位置。但如果你对一个已经移动过的实例再次调用translate它会在当前位置上继续偏移而不是回到你指定的坐标。比如某实例先translate到(10,0,0)再translate到(10,0,0)它最终位置是(20,0,0)。这个特性在批量循环里特别容易出问题。我见过有人写循环生成球体每个球都translate同一个vector结果所有球堆在了同一个位置。正确做法是确保每个实例的初始坐标都是原点然后一次性给到位或者记录实例当前坐标手动计算目标位置与实际位置的差值再更新。随机位置装配的关键在于生成不重叠的球心坐标。工程上常用随机生成 重叠判断的思路import random sphereCenters [] sphereRadii [] for i in range(30): success False while not success: r random.uniform(0.5, 3.0) x random.uniform(-10.0, 10.0) y random.uniform(-10.0, 10.0) z random.uniform(-10.0, 10.0) ok True for j in range(len(sphereCenters)): dx x - sphereCenters[j][0] dy y - sphereCenters[j][1] dz z - sphereCenters[j][2] dist (dx * dx dy * dy dz * dz) ** 0.5 if dist r sphereRadii[j]: ok False break if ok: sphereCenters.append((x, y, z)) sphereRadii.append(r) success True这段逻辑是每次随机生成半径和球心坐标然后和已生成的球逐一比较距离如果距离小于两个半径之和说明重叠了就重新生成。30个球一般几秒钟就能找到一组可行解。需要注意的是如果球体数量太多、区域太小会出现找不到可行解的死循环这时候要在循环里加一个最大尝试次数到了上限就提示用户减小半径或扩大区域。3.3 生成随机球体并装配的完整可运行脚本把上面的逻辑整合起来我贴一段可以直接运行的完整脚本。这段代码我在Abaqus 2020和2022上都跑过逻辑没问题。# -*- coding: utf-8 -*- from abaqus import * from abaqusConstants import * import part import assembly import random modelName RandomSpherePack if modelName in mdb.models.keys(): del mdb.models[modelName] myModel mdb.Model(namemodelName) numSpheres 20 radiusMin 0.5 radiusMax 2.5 boxHalfSize 8.0 maxAttempts 2000 sphereCenters [] sphereRadii [] for i in range(numSpheres): success False attempts 0 while not success and attempts maxAttempts: attempts 1 r random.uniform(radiusMin, radiusMax) x random.uniform(-boxHalfSize, boxHalfSize) y random.uniform(-boxHalfSize, boxHalfSize) z random.uniform(-boxHalfSize, boxHalfSize) ok True for j in range(len(sphereCenters)): dx x - sphereCenters[j][0] dy y - sphereCenters[j][1] dz z - sphereCenters[j][2] if dx * dx dy * dy dz * dz (r sphereRadii[j]) ** 2: ok False break if ok: sphereCenters.append((x, y, z)) sphereRadii.append(r) success True if not success: raise RuntimeError(Failed to place sphere %d after %d attempts % (i, maxAttempts)) myAssembly myModel.rootAssembly for i in range(numSpheres): partName SpherePart-%d % (i 1) myPart myModel.Part(namepartName, dimensionalityTHREE_D, typeDEFORMABLE_BODY) myPart.BaseSolidSphere(radiussphereRadii[i]) instName SphereInst-%d % (i 1) myInst myAssembly.Instance(nameinstName, partmyPart, dependentON) myInst.translate(vectorsphereCenters[i]) print(Successfully created %d spheres in %s % (numSpheres, modelName))这里解释几个细节。if modelName in mdb.models.keys(): del mdb.models[modelName]这句非常重要因为脚本重复运行时如果模型名已存在Abaqus会报KeyError所以先删旧建新保证脚本可重复执行。maxAttempts限制是为了防止随机算法陷入死循环这个值根据球体数量和区域大小调整比如20个球在20x20x20的盒子区域里2000次足够。4. 装配细节与位置控制的关键参数4.1 平移、旋转、阵列三种常用定位手段装配时除了translate还经常会用到rotate和阵列模式。rotate方法需要三个参数分别是旋转轴上的点、旋转轴方向向量和旋转角度。比如让一个球体绕着Z轴旋转45度myInst.rotate(axis_point(0, 0, 0), axis_direction(0, 0, 1), angle45.0)axis_point和axis_direction组合起来定义了一条旋转轴angle是旋转角度单位是度。这个方式在做周期性结构或者需要指定朝向的颗粒时非常有用。阵列方面Abaqus的Instance支持LinearInstancePattern和RadialInstancePattern分别生成线性阵列和环形阵列。不过在我们的球体场景里阵列很少用因为球体是各向同性的旋转也不改变外观主要靠translate控制位置即可。如果涉及非球形颗粒比如椭球、圆柱rotate和阵列的价值就体现出来了。还有一个容易被忽视的点translate、rotate和scale是可以叠加使用的并且顺序会影响最终结果。比如先scale再translate和先translate再scale效果完全不同。实际项目中如果你的球体既要放大又要挪位置我建议先scale再translate因为scale是以实例原始坐标为基准进行的缩放先平移再缩放会导致位置也一起被放大很容易超出预期边界。4.2 dependentON还是OFF装配模式的选择创建Instance时有个dependent参数取值为ON或OFF。这个参数决定了实例的几何数据是共享Part还是独立存储。当dependentON时实例只是Part的影子几何信息完全来自Part实例自身不保存几何数据。好处是模型数据量小网格可以在Part级别统一划分所有实例共享网格缺点是每个实例不能单独划分网格或做局部特征修改。当dependentOFF时每个实例都保存一份独立的几何数据可以单独划分网格、单独编辑。缺点是数据量大实例多了之后模型会非常臃肿操作也变慢。对于球体随机装配这种场景我一般用dependentON。原因很简单球体是简单几何体不需要单独编辑每个实例而且后续如果要给球体划分网格在Part级别一次搞定就行。只有当你需要对某一个球单独加密网格或者单独切割几何时才考虑OFF模式。4.3 脚本执行与批量运行的两种方式Abaqus的Python脚本有两种常见运行方式看你的需求来选择。一种是在Abaqus/CAE图形界面里执行好处是脚本跑完你可以直接看到模型方便检查另一种是命令行无GUI执行适合批量处理和多组参数扫描。无GUI执行的命令是abaqus cae -noGUI create_spheres.py这条命令不需要打开CAE窗口在后台直接跑脚本跑完后可以保存.cae模型文件或者继续提交作业。如果你要对很多组参数跑模型写一个外层循环脚本每次调用abaqus cae -noGUI就能实现自动化批量建模加仿真。我实际做参数扫描时喜欢在脚本末尾把模型保存下来mdb.saveAs(pathNameC:/tmp/sphere_model.cae)这样即使脚本运行过程中CAE界面出现问题模型也已经保存了不会白跑。5. 踩坑实录与排查技巧5.1 高频报错对照表写Abaqus脚本的过程中有几个报错几乎是所有人都会遇到的。我把它们列在下面每个都附带解决办法。报错信息原因解决办法ImportError: No module named abaqus在系统自带Python环境里运行了Abaqus脚本必须用Abaqus内置Python环境在Abaqus/CAE里Run Script或通过abaqus cae启动KeyError: Model-1或Part-1引用了不存在的模型或Part对象先检查mdb.models.keys()或myModel.parts.keys()中是否有该对象The instance xxx already exists重复运行脚本同名Instance已经存在在创建前用if instName in myAssembly.instances.keys(): del ...预处理AttributeError: Part object has no attribute translatetranslate是Instance的方法不是Part的确保调用对象是rootAssembly.instances[xxx]而不是part对象TypeError: translate() takes exactly 2 argumentstranslate的vector参数没有用元组或列表包裹写成translate(vector(x, y, z))The number of cpus (20) exceeds the number of cpus available提交作业时指定的CPU数量超过机器可用核心数在作业提交参数里改用当前机器实际的CPU核心数第6条虽然不直接属于建模脚本范围但很多人建模脚本跑完后提交作业时遇到这个错。我自己也踩过一次当时一台16核的机器上指定20个CPU跑作业Abaqus直接报错。解决方法是先查机器可用核心数再设置作业参数。5.2 调试技巧与性能优化经验脚本调试最有效的三板斧第一利用.rpy文件对照第二在CAE命令行接口里逐步执行第三用print打日志。Abaqus/CAE底部有一个Python命令行输入框类似提示符在这里可以逐行输入命令每行立即看到结果。我调试随机球体脚本时会先在这个命令行里手动执行一部分循环确认逻辑无误后再写进.py文件批量跑。这样能避免一次性跑大脚本时找不到具体错误位置。print语句也很实用。脚本在CAE里运行时print输出会显示在命令行区域或信息区。我习惯在关键步骤后打印对象数量和位置信息方便快速确认脚本执行到哪一步。性能方面生成大量球体时最需要注意的一点是尽量少的对象访问次数。每次写mdb.models[Model-1].parts[...]都要沿对象树查找一次循环次数一多累积时间就明显。更高效的做法是把频繁访问的对象先赋给局部变量比如myModel mdb.models[modelName] myAssembly myModel.rootAssembly然后在循环里直接用myModel和myAssembly省去重复查找。另外如果球体数量超过几百个每一颗球都建独立Part会让模型非常庞大。这时候建议改用单位球Part 实例scale的方式或者使用Abaqus的Orphan Mesh方式直接生成网格绕过实体几何。不过后者属于网格建模的范畴这里就不展开了。写在最后做Abaqus脚本建模这件事最大的门槛其实不是Python语法而是换一种思考方式从点击拖拽的鼠标思维变成对象方法的代码思维。一旦你看出mdb、Part、Instance这几个对象之间的关系后面的材料定义、边界条件、甚至作业提交全都是同一套逻辑。根据我个人的经验最值得投入时间的不是死记API而是封装一套自己的工具脚本库。把创建随机球体、创建长方体盒子、批量装配平移这类高频操作写成函数每个项目直接调用改参数就能复用。这比每次开GUI重新画一遍效率高出不止一个量级。本文还有配套的精品资源点击获取