3月14日,我收到了仿真秀专栏作者陈开伟老师编著,机械工业出版社发行的专业图书《Abaqus二次开发:内核与GUI应用实例详解》,非常欣喜。该书籍得到了SIMULINA仿真技术总监白锐和Abaqus应用专家/仿真秀优秀讲师曹金凤和江丙云博士的作序推荐。
该书涵盖了Abaqus内核与GUI二次开发的各个方面。内核开发部分包括Abaqus内核的基本结构、数据类型、函数接口以及常用命令等内容,通过不同复杂程度的实例演示了如何使用 Abaqus内核脚本开发实现功能扩展。GUI开发部分包括AbaqusCU的基础知识、控件、布局、模式和关键字等内容,也给出众多实例教给读者如何使用AbaqusCUIToolkit 定制插件界面和主窗口界面。本书提供了大量实例源代码和示例代码,带领读者更加深入地了解 Abaqus的二次开发过程和实现方法。同时,书中也注重帮助读者养成良好的规范化编程习惯,以提高代码的可读性和可移植性。
经作者授权同意,我把该书第四章《实例:批量化施加螺栓力》全部内容分享到这里,让读者朋友亲身感受:本书不仅是一本关于Abaqus二次开发的实用指南,对读者来说更是不可多得的学习宝典。欢迎订阅和分享,以下是正文:
本章介绍一个批量施加螺栓力的前处理内核脚本,它能够对装配体中所有的螺栓一键施加螺栓力载荷。演示模型如图4-1所示,装配体中有四个螺栓,它们的位置和方向都不同。两个较细的螺栓来自一个螺栓部件实例,另外两个较粗的来自另一个部件。模型文件保存为chapter 4\4 bolts.cae,版本是Abaqus 2017,脚本AutoBoltLoad.py也一并保存在该目录中。
打开模型,运行脚本AutoBoltLoad.py。切换到Load模块,可以看到这四个螺栓都加载了螺栓力载荷,如图4-2所示。
该脚本是个相对通用的程序,从整体来看,实现过程分为两个部分:对部件操作和对装配体操作。之所以要在部件中切割,是因为装配体中的螺栓方向和位置都不相同,不容易精确把控,而在部件则相对容易很多。而螺栓力的施加只能在装配状态中进行,包括获取切割面和基准轴,所以这部分要在遍历所有部件实例时进行。
图4-1未施加螺栓力的装配体
图4-2 施加螺栓力的装配体
AutoBoltLoad.py
1 #
-*-coding:utf-8-*-
2 '''此脚本用于批量施加螺栓力荷载'''
3 from
abaqus import *
4 from abaqusConstants import *
5 from caeModules import *
6
7 def displayDatum(flag):
8 '''设置是否显示基准'''
9 vp
= session.viewports[session.currentViewportName]
10 if flag == 'on':
11 vp.assemblyDisplay.geometryOptions.setValues(datumPointLabels=ON,
\
12 datumAxes=ON, datumPoints=ON)
13 if flag == 'off':
14 vp.assemblyDisplay.geometryOptions.setValues(datumPointLabels=OFF,
\
15 datumAxes=OFF, datumPoints=OFF)
16
17 def bolt(boltIns):
18 '''遍历螺栓部件实例,获得螺栓部件列表'''
19 boltPart= []
20 for bolt in boltIns:
21 # 通过部件实例得到部件,并形成列表
22 if bolt.part not in boltPart:
23 boltPart.append(bolt.part)
24 # 若部件为Lock状态,则解锁
25 if bolt.part.isLocked() == 1:
26 bolt.part.Unlock(reportWarnings=False)
27 # 返回部件列表
28 return boltPart
29
30 def cutBolt(bolts):
31 '''在螺栓部件的中心位置切割螺栓'''
32 # 遍历全部螺栓部件
33 for bolt in bolts:
34 # 删除已有基准
35 fts= bolt.features
36 for ft in fts.keys():
37 # 判断特征关键字的前5个字符是否为'Datum',符合则删除该特征
38 if ft[:5] ==
'Datum':
39 bolt.deleteFeatures((ft, ))
40 bolt.regenerate()
41 # 获取螺栓实体BoundingBox的中心点坐标
42 c = bolt.cells
43 coor= c.getBoundingBox()
44 coorX= (coor['high'][0] + coor['low'][0])
/ 2
45 coorY= (coor['high'][1] + coor['low'][1])
/ 2
46 coorZ= (coor['high'][2] + coor['low'][2])
/ 2
47 # 在中心点处创建基准点
48 datumPoint= bolt.DatumPointByCoordinate(coords = (coorX,coorY,coorZ))
49 # 在中心点处以Y轴创建基准轴
50 datumAxisPart= bolt.DatumAxisByPrincipalAxis(principalAxis =
YAXIS)
51 # 在中心点处垂直于基准轴切割螺栓
52 d = bolt.datums
53 bolt.PartitionCellByPlanePointNormal(cells
= c, point = d[datumPoint.id], \
54 normal
= d[datumAxisPart.id])
55
56 def getLoadFace(oneIns):
57 '''利用基准点获取螺栓力加载截面'''
58 ds = oneIns.datums
59 for d in ds.values():
60 # 属性数量为1的是基准点,属性数量为2的是基准轴
61 if len(d.__members__) ==
1:
62 # 获取基准点的坐标,以元组的形式赋值给coor
63 coor = d.pointOn
64 # 使用findAt()查找切面
65 fs = oneIns.faces
66 f1 = fs.findAt((coor,),)
67 # 为切面创建Region对象,并返回
68 region = regionToolset.Region(side1Faces=f1)
69 return region
70
71 def getAxis(oneIns):
72 '''在基准中获取螺栓基准轴'''
73 ds = oneIns.datums
74 # 基准轴才有属性direction
75 for d in ds.values():
76 if hasattr(d,'direction'):
77 datumAxisIns = d
78 # 返回基准轴
79 return datumAxisIns
80
81 if __name__ == '__main__':
82 '''主程序'''
83 # 通用代码,获取一系列视口和模型对象
84 vpName= session.currentViewportName
85 vp = session.viewports[vpName]
86 modelName= session.sessionState[vpName]['modelName']
87 m = mdb.models[modelName]
88 a = m.rootAssembly
89 ins = a.instances
90 stps= m.steps
91
92 insList= []
93 # 将所有部件实例并入列表insList中
94 for i in ins.values():
95 insList.append(i)
96 # 如果部件实例列表为空,弹出警告对话框
97 if insList== []:
98 getWarningReply(message='当前模型中没有部件实例', buttons
= (YES, NO))
99 # 如果没有分析步,弹出警告对话框。有分析步则获取名称
100 if len(stps)
== 1:
101 getWarningReply(message='当前模型中没有分析步', buttons = (YES, NO))
102 else:
103 stepName= stps.keys()[1]
104 # 如果装配体处于Lock状态,解锁
105 if a.isLocked== 1:
106 a.unlock()
107
108 # 获取螺栓部件
109 boltPart= bolt(insList)
110 # 切割螺栓部件
111 cutBolt(boltPart)
112 # 重新生成模型
113 try:
114 a.regenerate()
115 except BaseException:
116 a.restore()
117
118 i = 1
119 # 遍历所有螺栓部件实例
120 for eachInsin insList:
121 # 获取切割面和基准轴
122 region = getLoadFace(eachIns)
123 datumAxisIns= getAxis(eachIns)
124 # 施加螺栓力
125 m.BoltLoad(name='boltLoad-' + str(i), createStepName=stepName, region=region,
126 magnitude=1450, boltMethod=APPLY_FORCE, datumAxis=datumAxisIns)
127 i += 1
128 # 关闭基准显示
129 displayDatum('off')
如果想实现的目标需要由几个步骤配合才能完成,可以在编写脚本之前先将过程梳理清楚。具体来说,本脚本的流程主要有以下四个步骤:
第四步:施加螺栓力载荷。
前三个步骤以自定义函数实现。第一步,获取所有螺栓部件,此功能由函数bolt()实现。函数体中遍历所有部件实例,第23行由部件实例反向获取部件,将它们添加到一个列表中,列表在第28行由return语句返回。由于要对模型进行操作,如果部件处于Lock状态,需要通过第26行解锁。
第二步,切割螺栓,该功能由cutBolt()完成。函数以上一步返回的部件列表为参数,对它进行遍历。为防止误操作,首先在第39行删除已有的基准。接着用getBoundingBox()获取边界坐标,尽管装配体中螺栓摆放的位置和方向都不一样,但它们都由Abaqus创建,作为部件,它们的方向是一致的,因此边界坐标具有规律性。第44行~第46行,获取部件中心点处的坐标,第48行在中心点处创建一个基准点。通过该基准点和Y方向基准轴,第53行~第54行将螺栓实体切割,以便施加螺栓力。
前两步是对部件进行操作,后两步则是对部件实例进行处理。
第三步,由getLoadFace()提取切割产生的载荷面。函数体中,利用部件实例中基准属性的数量来获取切割面,如果基准属性的数量为1,表示只有一个pointOn属性,且该pointOn即为上一步在中心位置创建的基准点,第63行获取该点的坐标。目前切割面已经存在,第66行利用findAt()即可获取此切割面,将它创建为Region对象后,以return语句返回。
到目前为止,施加螺栓力还缺基准轴对象,该基准轴不是部件中的基准轴,而是部件实例中的基准轴。函数getAxis()可以获取螺栓基准轴,函数体中第76行通过hasattr()判断基准对象中是否包含属性direction,direction即为基准轴。如包含,则获取它,并以return语句返回。
第四步,施加螺栓力载荷。这个功能由Abaqus自带的BoltLoad()完成。主程序中第120行对所有的部件实例做遍历,第122行~第123行获取切割面和基准轴后,第125行~第126行用BoltLoad()即可对螺栓实例施加螺栓力载荷,此处的载荷值统一设为1450N,也可以使用if语句,判断螺栓的规格,施加不同的载荷值。
作为一个较为完善的脚本,除了实现主体功能,还需要考虑到其他情况,比如模型中不存在部件实例、没有创建分析步和模型处于Lock状态等,出现这些问题都会直接终止脚本的运行。因此,需要提前做出防范语句,脚本中第97行~第106行,使用if语句做判断,如果出现问题,可以用getWarningReply()弹出警告对话框,提示相应的错误信息。此外,还要考虑重复执行脚本可能会出现的问题,这些代码可以在完成主体结构后再编写。
脚本的操作对象为所有装配件。如果装配体中包含其他非螺栓部件,需要修改第92行~第95行,将螺栓部件实例的名称添加到列表insList中。这是纯内核脚本的局限之处,如果内核脚本与GUI搭配使用,可以通过光标选择螺栓,这样能避免手动修改内核脚本,使用起来更加友好。
编写较长或较为复杂的脚本时,可以适当使用多个def函数。函数可以将具有独立功能的代码段封装起来,方便调用和管理,本脚本定义了5个函数。通过自定义函数,可以将大段代码分解成若干个小的、可重用的模块,既可以避免重复编写相似的代码,还能提高代码的可读性和可维护性,使程序更加简洁。在编写自定义函数时,需要注意以下几点。
4)函数应有清晰简明的注释,以方便后期维护或其他开发人员的理解。
各个函数编写完成后,将它们以一定的顺序组合起来,放在主程序中,从而完成整个流程。
本章介绍了一个批量施加螺栓力的前处理内核脚本。演示的装配体中,螺栓的规格并不一致,各个螺栓的摆放位置和方向也不一样。脚本先通过部件实例获取螺栓的部件,对各部件进行遍历,自动获取螺栓实体的中心点,切割螺栓实体,再在部件实例中获取切割面和基准轴,从而施加螺栓力。此外,脚本中还加入了一些防错语句,对可能出现的问题做出提示。