概述
随着 CAE 的广泛应用,CAE 用户的水平日渐提高,新需求日渐增多,CAE 软件的二次开发逐渐成为工程师的必备技能。Python 语言简单易懂、开源包众多,是非常流行的编程语言。Abaqus 作为一款以 Python 语言为前后处理内核的 CAE 软件,兼具 CAD 软件基于特征建模的方法,还提供了内核与 GUI 二次开发接口,所以基于 Abaqus 进行二次开发的自由度非常大。
Abaqus 的内核脚本二次开发可以从学习日志文件 abaqus.rpy 起步,GUI 脚本二次开发可以从 GUI 用户手册起步。熟悉之后,简单问题可以直接用脚本编写。但是 Abaqus 毕竟不是专业的集成开发环境(IDE),代码补全、参数提示功能较弱,借助专业 IDE 可提高开发效率。由于 Abaqus 的 Python 解释器无法被专业 IDE 直接调用,需要创建代理模块模拟 Abaqus 各种方法的接口。
本文从学习研究的角度探讨了创建 Abaqus 代理接口的方法,提供了有关脚本及生成的代理文件下载链接(欢迎打赏下载币)。
Abaqus内核及GUI代理的应用效果
在pyCharm中,内核脚本的快捷文档(光标在方法后、括号前,按Ctrl+Q)如下:
在 pyCharm 中,GUI 脚本的方法提示如下:
在 pyCharm 中,内核脚本的参数提示(Ctrl+P)如下:
pyCharm 配置说明
- 下载上述文件,从“推荐使用的代理组合”目录下,复制 abaqus.py, abaqusConstants.py, abaqusGui.py 复制到 Python 解释器的 Lib/site-packages目录下,例如: C:\ProgramData\Anaconda2\Lib\site-packages
- 修改 pyCharm 设置,Help–>Edit Custom Properties,在 idea.properties 文件中添加一行
idea.max.intellisense.filesize=99999【#默认是2500kb】,解决 pyCharm 中 File size exceeds configured limit 文件大小不够的问题。原文链接 - 重启 pyCharm即可
开发相关背景
Abaqus 自身的 CLI (command line interface) 窗口具有代码补全功能,补全快捷键为 Tab,其核心模块为 completerABQ.pyc(实现自动补全功能) 和 docstringLookup.pyc(多个字典,保存了方法的路径、参数及说明)。自动补全只支持 CLI 窗口的内核脚本,不支持 GUI 脚本。
Abaqus 的 GUI 二次开发较为繁琐,无法直接调试,查找方法非常不方便。pyCham 的代码补全、参数提示功能很棒,Ctrl+P 参数提示, Ctrl+Q 查看函数、方法文档,很方便。Abaqus 的 python 2.x 解释器经过订制,无法(未找到方法)用于 pyCharm 调试 Abaqus 脚本。
通过生成 Abaqus 代理模块,模拟 Abaqus 各种方法的接口定义,可在通用 Python 解释器中“简单调试”(参数提示、查看文档、语法检查) Abaqus 内核脚本及 GUI 脚本。
开发脚本说明
1. gen_Abaqus_Agent_from_2020_docs.py
运行环境:Python3
脚本说明:通过解析 htm 文件,将 Abauqs 2020 的脚本参考手册转化为代理模块 abaqus.py,输入参数、返回值都有类型及说明。因为部分 htm 文件的编码为 utf-8,为便于处理,选用 python3。
2. gen_AbaqusConstants_Agent.py
运行环境:Abaqus CAE
脚本说明:将 abaqusConstants 模块转为代理模型 abaqusConstants.py。调用内置模块,必须在 Abaqus 环境执行。
3. getAbaquStructure.py
运行环境:Abaqus CAE
脚本说明:(不推荐)借助 docstringLookup 模块生成代理 abaqus.py, xyPlot.py 等,输入参数无类型,无返回值。 调用内置模块,必须在 Abaqus 环境执行。
4. transferAbaquGUIFunctionFromDocumentation.py
运行环境:Python
脚本说明:GUI 参考手册的结构比较复杂(或者说比较混乱),很多类型没有介绍,无法程序自动识别。此脚本只起辅助作用,从 GUI 参考手册的网页中手动复制内容到文本文件,然后用脚本解析文本文件,生成单个类型,然后手动添加到 abaqusGui.py 中,需要注意类型的创建顺序。
脚本内容
1.gen_Abaqus_Agent_from_2020_docs.py
#-*- coding:gbk -*-
from bs4 import BeautifulSoup
import re,os,pickle
'''This script transfers "Abaqus 2020 Scripting Reference Documentatation" to an Agent Model File : abaqus.py
The generated agent "abaqus.py" can be used in pycham for code completion
1.replace pathDir in __main__ eg:
pathDir = r'C:\Program Files\Dassault Systemes\SIMULIA2020doc\English\SIMACAEKERRefMap'
2. run this scipt in python3 get "abaqus.py"
3. run gen_AbaqusConstants_Agent.py in abaqus to get 'abaqusConstants.py'
by alienwxy 2020-05-05
'''
def cutInfo(info):
info = info.strip()
return info
cutLen = 50
rst = re.findall('[A-Z][A-Z_0-9]+',info)
if rst and info.startswith('A SymbolicConstant'):
return ', '.join(rst)
else:
if len(info)>cutLen*2:
return info[:cutLen]+' ... '+info[-cutLen:]
else:
return info
#解析目录所有htm文件
def partseDir(dirPath):
modules = {} #'moduleName': methods, constructs, members
objects = {}
failed = []
# 先更新所有方法
for fname in os.listdir(dirPath):
if fname.endswith('.pkl'):
try:
with open(os.path.join(dirPath, fname), 'rb') as fp:
titleList, methodDict, constructDict, memberList = pickle.load(fp)
titletype,titlename,titleintro = titleList
if titletype == 'object':
if titlename in objects: #对象已经存在,忽略标题,合并方法,合并成员变量
dic =objects[titlename] #对象字典
if methodDict:
mtddic = dic['methodDict']
for name,body in methodDict.items():
if name in mtddic: #方法名已存在
if mtddic[name] == body:
continue
else:
pname = name[:-1] if name[-1].isdigit() else name #不含结尾数字的名称
name = pname+str(len([n for n in mtddic.keys() if n.startswith(pname)])) #新名称
mtddic[name] = body
if memberList:
mmblst = dic['memberList']
mmblst.extend(memberList)
else: #对象不存在,创建标题,方法字典、成员变量列表
objects[titlename] = {
'intro':titleintro, #''
'methodDict':methodDict,#intro reqargs, optArgs, returns
'memberList':memberList, #[[n,i,v],[n,i,v]] []
}
elif titletype =='module':
if titlename in modules: # 对象已经存在,忽略标题,合并方法,合并成员变量
dic = modules[titlename] # 对象字典
if methodDict:
mtddic = dic['methodDict']
for name, body in methodDict.items():
if name in mtddic: # 方法名已存在
if mtddic[name] == body:
continue
else:
pname = name[:-1] if name[-1].isdigit() else name # 不含结尾数字的名称
name = pname + str(len([n for n in mtddic.keys() if n.startswith(pname)])) # 新名称
mtddic[name] = body
if memberList:
mmblst = dic['memberList']
mmblst.extend(memberList)
else: # 对象不存在,创建标题,方法字典、成员变量列表
modules[titlename] = {
'intro': titleintro, # ''
'methodDict': methodDict, # intro reqargs, optArgs, returns
'memberList': memberList, # [[n,i,v],[n,i,v]] []
}
else:
continue
except:
print(fname)
failed.append(os.path.join(dirPath,fname))
# 更新所有构造器--只针对 objects
for fname in os.listdir(dirPath):
if fname.endswith('.pkl'):
# try:
with open(os.path.join(dirPath, fname), 'rb') as fp:
titleList, methodDict, constructDict, memberList = pickle.load(fp)
titletype, titlename, titleintro = titleList
if constructDict:
for name,body in constructDict.items():
if body['paths']:
for path in body['paths']:
objectName = recoverObjectFromPathString(path)
if not objectName: continue
if objectName in objects: # 对象已经存在,添加方法
dic = objects[objectName] # 对象字典
mtddic = dic['methodDict']
if name in mtddic: # 方法名已存在
if mtddic[name] == body:
pass
else:
pname = name[:-1] if name[-1].isdigit() else name # 不含结尾数字的名称
name = pname + str(len([n for n in mtddic.keys() if n.startswith(pname)])) # 新名称
mtddic[name] = body
else: # 对象不存在,创建标题,方法字典、成员变量列表
objects[objectName] = {
'intro': '', # ''
'methodDict': {name:body}, # intro reqargs, optArgs, returns
'memberList': [], # [[n,i,v],[n,i,v]] []
}
# except:
# pass
modules = list(modules.items())
objects = list(objects.items())
modules.sort()
objects.sort()
writeObjects(objects)
# writeModules(modules)
if failed:
with open('failed.txt','w') as fp:
for i in failed:
fp.write('%s\n' % i)
#写入函数
def writeFunction2file(fp, funcName,funcBody):
intro = funcBody['intro']
reqArgs = funcBody['reqArgs']
optArgs = funcBody['optArgs']
returns = funcBody['returns']
if reqArgs:
reqstr = ', '.join([i[0] for i in reqArgs])
if optArgs:
optstr = ', '.join(['%s=%s' % (i[0], i[2]) for i in optArgs])
argstr = ', '.join([reqstr, optstr])
else:
argstr = reqstr
else:
if optArgs:
optstr = ', '.join(['%s=%s' % (i[0], i[2]) for i in optArgs])
argstr = optstr
else:
argstr = ''
if funcName in ('print','import','class','def'):
funcName = funcName + '_'
fp.write('def %s(%s):\n' % (funcName, argstr))
fp.write(' """\n')
fp.write(' %s\n\n' % cutInfo(intro))
if reqArgs:
for arg, info in reqArgs:
fp.write(' :param %s: %s\n' % (arg, cutInfo(info)))
if optArgs:
for arg, info, value in optArgs:
fp.write(' :param %s: %s\n' % (arg, cutInfo(info)))
if returns:
value, info = returns
fp.write(' :return %s: %s\n' % (value, cutInfo(info)))
fp.write(' """\n')
if returns:
value, info = returns
fp.write(' return %s\n' % (value,))
#写入方法
def writeMethod2file(fp,funcName,funcBody):
intro = funcBody['intro']
reqArgs = funcBody['reqArgs']
optArgs = funcBody['optArgs']
returns = funcBody['returns']
if reqArgs:
reqstr = ', '.join([i[0] for i in reqArgs])
if optArgs:
optstr = ', '.join(['%s=%s' % (i[0], i[2]) for i in optArgs])
argstr = ', '.join(['self',reqstr, optstr])
else:
argstr = ', '.join(['self',reqstr])
else:
if optArgs:
optstr = ', '.join(['%s=%s' % (i[0], i[2]) for i in optArgs])
argstr = ', '.join(['self', optstr])
else:
argstr = 'self'
if funcName in ('print','import','class','def'):
funcName = funcName + '_'
fp.write(' def %s(%s):\n' % (funcName, argstr))
fp.write(' """\n')
fp.write(' %s\n\n' % cutInfo(intro))
if reqArgs:
for arg, info in reqArgs:
fp.write(' :param %s: %s\n' % (arg, cutInfo(info)))
if optArgs:
for arg, info, value in optArgs:
fp.write(' :param %s: %s\n' % (arg, cutInfo(info)))
if returns:
value, info = returns
fp.write(' :return %s: %s\n' % (value, cutInfo(info)))
fp.write(' """\n')
if returns:
value, info = returns
fp.write(' return %s\n' % (value,))
def _writeRepositoryClass2file(fp,className,classBody,objects):
intro = classBody['intro']
methodDict = classBody['methodDict']
memberList = classBody['memberList']
fp.write('class %s(tuple):\n' % className)
fp.write(' def __init__(self, rtype="", *args, **kwargs):\n')
fp.write(' tuple.__init__(self,*args,**kwargs)\n')
fp.write(' self.rtype=rtype\n')
fp.write(' def items(self):\n')
fp.write(' return (("key",self.__getitem__(0)),)\n')
fp.write(' def keys(self):\n')
fp.write(' return ("key",)\n')
fp.write(' def values(self):\n')
fp.write(' return (self.__getitem__(0),)\n')
fp.write(' def __getitem__(self,key):\n')
fp.write(' if not self.rtype:\n')
fp.write(' return None\n')
for name, dic in objects:
fp.write(' elif rtype=="%s":\n' % (name,))
fp.write(' return %s()\n' % (name,))
funcNames = list(methodDict.keys())
funcNames.sort()
for funName in funcNames:
funBody = methodDict[funName]
writeMethod2file(fp,funName,funBody)
def _writeArrayClass2file(fp,className,classBody):
intro = classBody['intro']
methodDict = classBody['methodDict']
memberList = classBody['memberList']
fp.write('class %s(tuple):\n' % className)
fp.write(' def __init__(self, *args, **kwargs):\n')
fp.write(' tuple.__init__(self,*args,**kwargs)\n')
fp.write(' """%s"""\n' % cutInfo(intro))
fp.write(' def __getitem__(self,key):\n')
fp.write(' return %s()\n' % className[:-5])
if memberList:
memberList.sort()
for name,info,value in memberList:
fp.write(' self.%s = %s # %s\n' % (name.strip(),value,cutInfo(info)))
funcNames = list(methodDict.keys())
funcNames.sort()
for funName in funcNames:
funBody = methodDict[funName]
writeMethod2file(fp,funName,funBody)
#写入类
def writeClass2file(fp,className,classBody):
intro = classBody['intro']
methodDict = classBody['methodDict']
memberList = classBody['memberList']
fp.write('class %s(object):\n' % className)
fp.write(' def __init__(self, *args, **kwargs):\n')
fp.write(' """%s"""\n' % cutInfo(intro))
if memberList:
memberList.sort()
for name,info,value in memberList:
fp.write(' self.%s = %s # %s\n' % (name.strip(),value,cutInfo(info)))
funcNames = list(methodDict.keys())
funcNames.sort()
for funName in funcNames:
funBody = methodDict[funName]
writeMethod2file(fp,funName,funBody)
#写入abauqs对象
def writeObjects(objects,subdir='agent'):
if not os.path.exists(subdir):
os.mkdir(subdir)
with open(os.path.join(subdir,'abaqus.py'),'w',encoding='utf-8') as fp:
fp.write('# -*- coding:utf-8 -*-\n')
fp.write('from abaqusConstants import *\n')
for objectName, objectDict in objects:
if objectName == 'Abaqus':
methodDict = objectDict['methodDict']
funNames = list(methodDict.keys())
funNames.sort()
for funName in funNames:
if funName[0].islower():
writeFunction2file(fp, funName, methodDict[funName])
elif objectName == 'Repository':
_writeRepositoryClass2file(fp,objectName,objectDict,objects)
elif objectName.endswith('Array'):
_writeArrayClass2file(fp,objectName,objectDict)
else:
writeClass2file(fp,objectName,objectDict)
fp.write('mdb = Mdb()\n')
fp.write('session = Session()\n')
#写入模块
def writeModules(modules,subdir='agent'):
if not os.path.exists(subdir):
os.mkdir(subdir)
for modulename,modulebody in modules:
with open(os.path.join(subdir,modulename+'.py'),'w',encoding='utf-8') as fp:
fp.write('# -*- coding:utf-8 -*-\n')
info = modulebody['intro']
fp.write('"""\n%s\n"""\n' % cutInfo(info))
methodDict =modulebody['methodDict']
funNames = list(methodDict.keys())
funNames.sort()
for funName in funNames:
writeFunction2file(fp, funName, methodDict[funName])
#无成员
pass
#解析单个html文件
def parseHtmlFile(fileName):
soup = BeautifulSoup(open(fileName,'r',encoding='utf-8'), 'lxml')
titleList = getTitle(soup)
methodDict = getMethods(soup)
constructDict = getConstructs(soup)
memberList = getMembers(soup)
return titleList, methodDict, constructDict, memberList
#写入单个文件--调试
def writeHtmlFile(fileName):
titleList, methodDict, constructDict, memberList = parseHtmlFile(fileName)
if not titleList[0]: return
with open(fileName+'.txt','w',encoding='utf-8') as fp:
fp.write('===========================titleList:\n')
fp.write('titletype:%s\n' % titleList[0])
fp.write('titlename:%s\n' % titleList[1])
fp.write('titleintro:%s\n' % titleList[2])
fp.write('===========================methodDict:\n')
if methodDict:
for methodName,body in methodDict.items():
fp.write('--------------%s--------------\n' % methodName)
fp.write('intro:%s\n' % body['intro'])
fp.write('++++reqArgs:\n')
if body['reqArgs']:
for arg,info in body['reqArgs']:
fp.write('%-20s %s\n' % (arg,info))
fp.write('++++optArgs:\n')
if body['optArgs']:
for arg,info,value in body['optArgs']:
fp.write('%-20s = %-20s %s\n' % (arg,value,info))
fp.write('++++returns\n')
fp.write('%s %s\n' % tuple(body['returns']))
fp.write('===========================constructDict:\n')
if constructDict:
for name,body in constructDict.items():
fp.write('--------------%s--------------\n' % name)
fp.write('intro:%s\n' % body['intro'])
fp.write('++++paths:\n')
if body['paths']:
for path in body['paths']:
fp.write('%s\n' % path)
fp.write('++++reqArgs:\n')
if body['reqArgs']:
for arg,info in body['reqArgs']:
fp.write('%-20s %s\n' % (arg,info))
fp.write('++++optArgs:\n')
if body['optArgs']:
for arg,info,value in body['optArgs']:
fp.write('%-20s = %s %s\n' % (arg,value,info))
fp.write('++++returns\n')
value,info = body['returns']
fp.write('%s %s\n' % (value,info))
fp.write('===========================members:\n')
if memberList:
for name,info,value in memberList:
fp.write('%-20s %s %s\n' % (name, value, info))
with open(fileName+'.pkl','wb') as fp:
pickle.dump([titleList, methodDict, constructDict, memberList], fp)
#写入文件夹--调试
def writeDir(dirPath):
cnt = 0
for fname in os.listdir(dirPath):
lfname = fname.lower()
if lfname.endswith('cpp.htm') or lfname.endswith('cpp.html'):
continue
if lfname.endswith('htm') or lfname.endswith('html'):
cnt+=1
if cnt%10 == 0: print('%d' % cnt)
try:
writeHtmlFile(os.path.join(dirPath,fname))
except:
print(os.path.join(dirPath,fname))
raise
#查看所有路径
def writePaths(dirPath):
paths = []
titles = []
for fname in os.listdir(dirPath):
if fname.endswith('.pkl'):
with open(os.path.join(dirPath,fname),'rb') as fp:
titleList, methodDict, constructDict, memberList = pickle.load(fp)
titles.append(titleList[:2])
if constructDict:
for name,body in constructDict.items():
if body['paths']:
paths.extend(body['paths'])
upaths=list(set(paths))
upaths.sort()
with open('paths.txt', 'w') as fp:
for path in upaths:
fp.write('%s\n' % path)
with open('paths.remap.txt','w') as fp:
for path in upaths:
fp.write('%s %s\n' % (recoverObjectFromPathString(path),path))
with open('titles.txt','w') as fp:
for t,n in titles:
fp.write('type:%s - name:%s\n' % (t,n))
#从访问路径恢复对象类型
def recoverObjectFromPathString(path):
if ' ' in path: #模块
return 'Abaqus'
if '.' not in path: #单个对象,全部归入 abaqus 模块,自身就是构造器
#Leaf displayGrooupMDBToolset
#Leaf displayGrooupODBToolset
#ElemType mesh
#Region regiontoolset
# caePrefsAccess caePrefsAccess
return 'Abaqus'
txt = path.split('.')[-2].strip()
txt = re.search('\w+',txt).group() #清理中括号
# hes -->h
# les --> le
# des --> de
# mes --> me
# ses -> s
# ies --> y
# res --> re
if txt == 'iterations': return 'AdaptivityIteration'
if txt == 'viscous': return 'Viscous'
if txt == 'eos': return 'Eos'
if txt == 'odbAccess': return 'OdbAccess'
if txt == 'sketches': return 'ConstrainedSketch'
if txt.endswith('hes'): return txt[0].upper() + txt[1:-2]
if txt.endswith('les'): return txt[0].upper() + txt[1:-1]
if txt.endswith('des'): return txt[0].upper() + txt[1:-1]
if txt.endswith('mes'): return txt[0].upper() + txt[1:-1]
if txt.endswith('ses'): return txt[0].upper() + txt[1:-2]
if txt.endswith('ies'): return txt[0].upper() + txt[1:-3] + 'y'
if txt.endswith('res'): return txt[0].upper() + txt[1:-1]
if txt.endswith('s'): return txt[0].upper() + txt[1:-1]
if txt == 'rootAssembly': return 'Assembly'
return txt[0].upper()+txt[1:]
# print(path)
# return txt
#获取标题
def getTitle(soup): #'', '','' type,name,intro
# title = soup.title.text
try:
title = soup.h1.text
if not title: return '','',''
except:
return '', '', ''
if 'object' in title:
titleType = 'object'
titleName = re.search('\w+', title).group()
elif 'module' in title:
titleType = 'module'
titleName = re.search('\w+', title).group()
else:
titleType = ''
titleName = ''
a = soup.find(name='table', class_='DocThemeIntro')
# titleIntro = re.sub('\s+', ' ', a.text) if a else ''
lst = []
for i in a.strings:
if 'topics' in i:
break
else:
lst.append(i)
txt = '\n'.join(lst)
titleIntro = re.sub('\s+', ' ', txt)
return titleType, titleName, titleIntro
#获取返回值
def getRtnArgs(rtndiv): #value, valueinfo
if rtndiv.attrs['class'][-1] == 'nopyreturnsect':
return [None, '']
else: #pyreturnsect
lst = []
for i in rtndiv.contents:
if i.name != 'h3':
try:
lst.append(i.text)
except:
pass
valueinfo = '\n'.join(lst)
if valueinfo:
valueinfo = re.sub('\s+',' ',valueinfo)
return [getMemberType(valueinfo), valueinfo ]
else:
return [None, '']
#获得变量类型
def getMemberType(instr): #变量或成员的说明
# 默认值的表述方式
# The whole assembly is queried by default.
# The default value is LOW.
# The default value is False.
# The default value is an integer.
# The user-specified density should be greater than 0.
# By default if the moments of inertia are not being evaluated about the center of mass, they will be evaluated about the origin.
# 参数类型的说明
# A MeshElementArray, CellArray, FaceArray, Ed
# A SymbolicConstant specifying 符号
# A String specifying the name 字符串
# A Boolean specifying whether 布尔值
# A positive Integer specifying 正整数
# An Int specifying that feature 整数
# A Float specifying which gives a 浮点数
# A double value specifying the 浮点数
# A tuple of three floats specifying th 浮点数元组 list List Tuple
# A tuple of dictionary objects 字典元组
# A sequence of Ints. 序列
# A sequence of Strings
# A sequence of PartInstance objects
# A sequence of PartInstance objects
# A ConnectorOrientationArray object.
# A Dictionary object with the following items 字典
# A VertexArray object specifyi
# An EdgeArray object specifyin
# A MeshElementArray object spe
# A MeshNodeArray object specif
# A PartInstance object specify
# An EngineeringFeature object.
# A ConnectorOrientationArray o
# A SectionAssignmentArray obje
# A repository of PartInstance objects
# A repository of Datum objects
# A repository of Feature objec
# A repository of Surface objec
# A repository of Set objects.
# A repository of Skin objects
# A repository of Stringer obje
# A repository of ReferencePoin
# A repository of ModelInstance
instr = re.sub('\s+',' ',instr)
# 有默认值的情况,直接返回取值
rst = ''
if 'Possible values' in instr:
r = re.findall('Possible values are (\w+)', instr)
if r: rst = r[0]
if 'The default value is' in instr:
r = re.findall('The default value is (\w+)', instr)
if r: rst = r[0]
if rst.isupper() or rst.isdigit(): #符号常量 数字
return rst
try:
eval(rst)
return rst
except:
# return None #抑制定义顺序不同导致的出错
pass
ptn = re.compile('\w+')
rst = ptn.findall(instr)
if len(rst) < 2:
# print(instr)
# raise(ValueError, instr)
return None
#无默认值或无法识别默认值,用对象表示
mType = rst[1]
lmType = mType.lower()
#内建类型
if lmType in ('int',): return 0
if lmType in ('float', 'double'): return 0.0
if lmType in ('string', ): return 'str()'
if lmType in ('boolean', 'bool','abaqusboolean'): return True
if lmType in ('dictionary', 'dict'): return 'dict()'
if lmType in ('tuple', 'list', 'sequence'): return 'tuple()'
if lmType in ('positive', 'negative'):
if rst[2] == 'Integer' :
return 1
else:
return 0.0
#Abaqus类型
if mType[0].isupper():
return mType+'()'
elif lmType == 'repository':
return 'Repository("%s")' % rst[3]
# return 'Repository(%s)' % rst[3]
else:
# print mType, instr
# raise TypeError
return None
#获取所有方法
def getMethods(soup): #{} {xxx}
methodDict = {}
for mtd in soup.find_all(class_="pymethodsect"):
#方法名称
methodName = re.search('\w+',mtd.h2.text).group() #方法名及参数
#方法主体
# methodBody = mtd.div #pymethodsect-body
methodBody = mtd.find(class_='pymethodsect-body') #pymethodsect-body
if not methodBody:
print('Method %s is invalid!' % methodName)
# for parent in soup.parents:
# print(parent.text)
# raise
continue
#方法介绍
lst = []
for i in methodBody.contents:
if i.name != 'div':
try:
lst.append(i.text)
except:
pass
else:
break
methodIntro = '\n'.join(lst)
methodIntro = re.sub('\s+', ' ', methodIntro)
#无参数
# rst = methodBody.find(class_='nopymethodarglist')
# rst = methodBody.find(class_=['nopymethodarglistreq', 'pymethodarglistreq', 'nopymethodarglistopt', 'pymethodarglistopt'])
# if rst:
reqArgs = []
optArgs = []
#必须参数
#无 nopymethodarglistreq
#有 pymethodarglistreq
reqdiv = methodBody.find(class_='nopymethodarglistreq')
if reqdiv: reqArgs = []
reqdiv = methodBody.find(class_='pymethodarglistreq')
if reqdiv:
dl = reqdiv.dl
if dl:
names = [i.text.strip() for i in dl.find_all(name='dt')]
infos = [re.sub('\s+', ' ', i.text) for i in dl.find_all(name='dd')]
reqArgs = list(zip(names, infos))
#可选参数
#无 nopymethodarglistopt
#有 pymethodarglistopt
optdiv = methodBody.find(class_='nopymethodarglistopt')
if optdiv: optArgs = []
optdiv = methodBody.find(class_='pymethodarglistopt')
if optdiv:
dl = optdiv.dl
if dl:
names = [i.text.strip() for i in dl.find_all(name='dt')]
infos = [re.sub('\s+', ' ', i.text) for i in dl.find_all(name='dd')]
values = [getMemberType(v) for v in infos] # 默认值
optArgs = list(zip(names, infos, values))
#返回值
# 有 pyreturnsect
# 无 nopyreturnsect
rtndiv = methodBody.find(class_='pyreturnsect')
if rtndiv:
returns = getRtnArgs(rtndiv)
else: # nopyreturnsect
returns = [None, ''] # value, info
if methodName in methodDict:
print('%s already exists' % methodName)
lst = [key for key in methodDict.keys() if key.startswith(methodName)]
methodDict[methodName + str(len(lst))] = {
'intro': methodIntro, # 方法介绍 str
'reqArgs': reqArgs, # 必须参数表 [] [[arg1,info1],[arg2,info2]]
'optArgs': optArgs, # 可选参数表 [] [[arg1,info1,value1],[arg2,info2,value2]]
'returns': returns, # 返回值列表 [None, ''] [Value, intor]
}
else:
methodDict[methodName] = {
'intro': methodIntro, # 方法介绍 str
'reqArgs': reqArgs, # 必须参数表 [] [[arg1,info1],[arg2,info2]]
'optArgs': optArgs, # 可选参数表 [] [[arg1,info1,value1],[arg2,info2,value2]]
'returns': returns, # 返回值列表 [None, ''] [Value, intor]
}
return methodDict
#获取构造器
def getConstructs(soup): #{} {xxx}
constructDict = {}
for mtd in soup.find_all(class_="pyconstructsect"):
#构造器名称
constructName = re.search('\w+',mtd.h2.text).group() #方法名及参数
#构造器主体
# constructBody = mtd.div #pyconstructsect-body
constructBody = mtd.find(class_='pyconstructsect-body') #pyconstructsect-body
if not constructBody:
print('Construct %s is invalid!' % constructName)
continue
#构造器介绍
lst = []
for i in constructBody.contents:
if i.name != 'div':
try:
lst.append(i.text)
except:
pass
else:
break
structIntro = '\n'.join(lst)
structIntro = re.sub('\s+', ' ', structIntro)
#构造器访问路径 列表
path = constructBody.find(class_='pypath')
try:
paths = []
for ppre in path.find_all(name='pre'):
if ppre is None: continue
txt = re.sub('\\\\\n','',ppre.text)
# paths = re.findall('[\.\[\]\w]+',txt)
paths.extend([i.strip() for i in txt.split('\n')])
for ppre in path.find_all(name='p'):
if ppre is None: continue
txt = re.sub('\\\\\n','',ppre.text)
# paths = re.findall('[\.\[\]\w]+',txt)
paths.append(txt)
except:
paths = []
#无参数
# rst = constructBody.find(class_='nopyctorarglist')
# rst = constructBody.find(class_=['nopyctorarglistreq', 'pyctorarglistreq', 'nopyctorarglistopt', 'pyctorarglistopt'])
# if rst:
reqArgs = []
optArgs = []
#必须参数
# 无参数 nopyctorarglistreq
# 1个 pyctorarglistreq
# 多个 pyctorarglistreq
reqdiv = constructBody.find(class_='nopyctorarglistreq')
if reqdiv: reqArgs = []
reqdiv = constructBody.find(class_='pyctorarglistreq')
if reqdiv:
dl = reqdiv.dl
if dl:
names = [i.text.strip() for i in dl.find_all(name='dt')]
infos = [re.sub('\s+', ' ', i.text) for i in dl.find_all(name='dd')]
reqArgs = list(zip(names, infos))
#可选参数
# 无参数 nopyctorarglistopt
# 1个 pyctorarglistopt
# 多个 pyctorarglistopt
optdiv = constructBody.find(class_='nopyctorarglistopt')
if optdiv: optArgs = []
optdiv = constructBody.find(class_='pyctorarglistopt')
if optdiv:
dl = optdiv.dl
if dl:
names = [i.text.strip() for i in dl.find_all(name='dt')]
infos = [re.sub('\s+', ' ', i.text) for i in dl.find_all(name='dd')]
values = [getMemberType(v) for v in infos] #默认值
optArgs = list(zip(names, infos, values))
# 返回值
# 有 pyreturnsect
# 无 nopyreturnsect
rtndiv = constructBody.find(class_='pyreturnsect')
if rtndiv:
returns = getRtnArgs(rtndiv)
else: #nopyreturnsect
returns = [None,''] #value, info
if constructName in constructDict:
print('%s already exists' % constructName)
# print(str(paths))
lst = [key for key in constructDict.keys() if key.startswith(constructName)]
constructDict[constructName+str(len(lst))] = {
'intro': structIntro, # 构造器介绍 str
'paths': paths, # 路径列表 [path1, path2] []
'reqArgs': reqArgs, # 必须参数表 [] [[arg1,info1],[arg2,info2]]
'optArgs': optArgs, # 可选参数表 [] [[arg1,info1,value1],[arg2,info2,value2]]
'returns': returns, # 返回值列表 [None, ''] [Value, intor]
}
else:
constructDict[constructName] = {
'intro':structIntro, #构造器介绍 str
'paths':paths, #路径列表 [path1, path2]
'reqArgs':reqArgs, #必须参数表 [] [[arg1,info1],[arg2,info2]]
'optArgs':optArgs, #可选参数表 [] [[arg1,info1,value1],[arg2,info2,value2]]
'returns':returns, #返回值列表 [None, ''] [Value, intor]
}
return constructDict
#获取所有成员
def getMembers(soup): #[] [[name,info,value],[name,info,value]]
mmb = soup.find(class_="pymembersect")
if mmb is None: return []
body = mmb.find(class_='pymembersect-body')
if body is None: return []
dl = body.find(class_='pymemberdatalist')
if dl is None: return []
names = [i.text for i in dl.find_all(name='dt')]
infos = [re.sub('\s+', ' ', i.text) for i in dl.find_all(name='dd')]
values = [getMemberType(s) for s in infos]
return list(zip(names, infos, values))
#清理临时文件
def clean(pathDir):
for fname in os.listdir(pathDir):
lfname = fname.lower()
if lfname.endswith('.htm.txt') or lfname.endswith('.html.txt') or lfname.endswith('.pkl'):
os.remove(os.path.join(pathDir,fname))
for fname in ('failed.txt', 'paths.txt','titles.txt', 'paths.remap.txt'):
if os.path.exists(fname):
os.remove(fname)
if __name__ == '__main__':
pathDir = r'C:\Program Files\Dassault Systemes\SIMULIA2020doc\English\SIMACAEKERRefMap'
writeDir(pathDir)
writePaths(pathDir)
partseDir(pathDir)
clean(pathDir)
2. gen_AbaqusConstants_Agent.py
# -*- coding:utf-8 -*-
'''
run this file in abaqus cae or abaqus python ,generate abaqusConstants agent.
by alienwxy 2020-05-05
'''
import types,os
import abaqusConstants
subdir = 'agent'
if not os.path.exists(subdir):
os.mkdir(subdir)
with file(os.path.join(subdir,'abaqusConstants.py'),'w') as fp:
fp.write('class SymbolicConstant(object):\n')
fp.write(' def __init__(self,instr=""):\n')
fp.write(' self.text = instr\n')
for name in dir(abaqusConstants):
if name.isupper():
fp.write('%s = SymbolicConstant("%s")\n' % (name,name))
fp.write('NO = OFF = False\n')
fp.write('ON = YES = True\n')
3. getAbaquStructure.py
# -*- coding:utf-8 -*-
'''
abaqus class method agent for pyCharm
1. run this file in abaqus cae
2. copy the files in abaqusAgent folder to xxx/Lib/site-packages
3. enjoy!
by alienwxy 2020-04-27
Usage:
from abaqus import *
from abaqusConstants import *
import mesh
'''
import types,os
import abaqusConstants
def getMethodObjectList():
import docstringLookup as dlk
methodDict = {}
objectDict = {}
#get diction
for name in dir(dlk):
module = getattr(dlk,name)
if type(module) is types.ModuleType:
if hasattr(module,'objectDocstringTable'):
dic = getattr(module,'objectDocstringTable')
objectDict.update(dic)
if hasattr(module,'methodDocstringTable'):
dic = getattr(module,'methodDocstringTable')
methodDict.update(dic)
methodList = [(k,v) for k,v in methodDict.items()]
objectList = [(k,v) for k,v in objectDict.items()]
methodList.sort()
objectList.sort()
return methodList,objectList
def write2Txt(methodList,objectList,subDir='abaqusAgent'):
if not os.path.exists(subDir):
os.mkdir(subDir)
with file(os.path.join(subDir,'abaqusMethodList.txt'),'w') as fp:
for k,v in methodList:
fp.write('%-60s\t%s\n' % (k,v))
with file(os.path.join(subDir,'abaqusObjectList.txt'),'w') as fp:
for k,v in objectList:
fp.write('%-60s\t%s\n' % (k,v))
def getMethodArgs(argstr):
# 'name, edges <, startPoint, flipDirection, pointCreationMethod>'
# 'stepName <, loadCaseNames, magnitude, amplitude>'
# 'stepName <, allowGrowth>'
# '<useCut, printResults>'
# '<useCut>'
# 'center, radius'
# 'mask'
# 'keyword argument not implemented'
# ''
argstr = argstr.strip()
if argstr == '' or 'not implemented' in argstr:
return ''
if '<' not in argstr:
return argstr
#optional
if argstr.startswith('<'):
optargs = argstr.lstrip('<').rstrip('>').strip()
return ', '.join(['%s=None' % (arg.strip(),) for arg in optargs.split(',')])
keyargs,optargs = argstr.split('<,')
keyargs = keyargs.strip()
optargs = optargs.strip().rstrip('>').strip()
if ',' in optargs: #multi
optargs = ', '.join(['%s=None' % (arg.strip(),) for arg in optargs.split(',')])
else:
optargs = '%s=None' % optargs
return '%s, %s' % (keyargs,optargs)
def selfMethodArgs(argstr):
s = getMethodArgs(argstr)
if s:
return 'self, ' + s
else:
return 'self'
def write2Python(methodList,subDir='abaqusAgent'):
# XYPlot.previous
# sys.modules['odbAccess'].openOdb odbAccess 的函数
# Abaqus.Mdb abaqus 模块函数
# Odb.Part odbAccess 模块 的类 以Odb开头
# OdbStep.setDefaultField
modules = {} #moduleName: [ [functionName,functionArgs] ]
abaqusFunctions = [] #[ [functionName,functionArgs]]
odbAccesFunctions = [] #[ [functionName,functionArgs]]
abaqusConstantsFunctions = []#[ [functionName,functionArgs]]
odbAccesClasses = {} #className: [[methodName,methodArgs]]
classes = {} #className: [[methodName,methodArgs]]
for name,lst in methodList:
if 'modules' in name:
if 'odbAccess' in name:
odbAccesFunctions.append([name.split('.')[-1], getMethodArgs(lst[0])])
elif 'abaqusConstants' in name:
abaqusConstantsFunctions.append([name.split('.')[-1], getMethodArgs(lst[0])])
else:
modName = name.split("'")[1]
if modName not in modules:
modules[modName] = []
modules[modName].append([name.split('.')[-1], getMethodArgs(lst[0])])
elif name.startswith('Odb'): #odbAccesClasses
clsName,methodName = name.split('.')
if clsName not in odbAccesClasses:
odbAccesClasses[clsName] = []
odbAccesClasses[clsName].append([methodName, selfMethodArgs(lst[0])])
elif name.startswith('Abaqus'): #abaqusFunctions
funcName = name.split('.')[-1]
if funcName != 'Mdb': #Mbd('<pathName>')
abaqusFunctions.append([name.split('.')[-1], getMethodArgs(lst[0])])
else:
clsName,methodName = name.split('.')
if clsName not in classes:
classes[clsName] = []
classes[clsName].append([methodName, selfMethodArgs(lst[0])])
if not os.path.exists(subDir):
os.mkdir(subDir)
def writeAbaqusConstants():
with file(os.path.join(subDir,'abaqusConstants.py'),'w') as fp:
for fname,fargs in abaqusConstantsFunctions:
fp.write('def %s(%s):\n' % (fname,fargs))
fp.write(' pass\n')
for name in dir(abaqusConstants):
if name.isupper():
fp.write('%s = "%s"\n' % (name,name))
def writeOdbAccess():
with file(os.path.join(subDir,'odbAccess.py'),'w') as fp:
writeClasses(fp,odbAccesClasses)
for fn,fa in odbAccesFunctions:
writeFunction(fp,fn,fa)
def writeModules():
for module in modules.keys():
with file(os.path.join(subDir,module+'.py'),'w') as fp:
for n,a in modules[module]:
writeFunction(fp,n,a)
def writeAbaqus():
with file(os.path.join(subDir,'abaqus.py'),'w') as fp:
writeClasses(fp,classes)
writeClasses(fp,odbAccesClasses)
for n,a in abaqusFunctions:
writeFunction(fp,n,a)
fp.write('mdb = Mdb()\n')
fp.write('session = Session()\n')
writeAbaqusConstants()
writeOdbAccess()
writeModules()
writeAbaqus()
with file(os.path.join(subDir,'__init__.py'),'w') as fp:
fp.write('\n')
with file(os.path.join(subDir,'deleteAbaqusAgent.bat'),'w') as fp:
for name in modules.keys():
fp.write('del %s.py; %s.pyc\n' % (name,name))
fp.write('del abaqus.py;abaqus.pyc;abaqusConstants.py;abaqusConsta.pyc;odbAccess.py;odbAccess.pyc\n')
fp.write('del deleteAbaqusAgent.bat\n')
def writeFunction(fp,functionName,functionArgs):
if functionName in ('print','as','if','class','def','None','True','False','for'):
functionName += '_confilictWithPythonReserveVars'
fp.write('def %s(%s):\n' % (functionName,functionArgs))
fp.write(' pass\n')
def writeClass(fp,clsName,clsMethods):
fp.write('class %s(object):\n' % clsName)
fp.write(' def __init__(self, *args, **kwargs): pass\n')
for mtdname,mtdarg in clsMethods:
if mtdname in ('print','as','if','elif','else','class','def','None','True','False','for','while'):
mtdname += '_confilictWithPythonReserveVars'
fp.write(' def %s(%s):\n' % (mtdname, mtdarg))
fp.write(' return %s(%s)\n' % (mtdname, mtdarg))
fp.write(' pass\n')
def writeClasses(fp,classesDict):
keys = classesDict.keys()
keys.sort()
for key in keys:
value = classesDict[key]
writeClass(fp,key,value)
if __name__ == '__main__':
m,o = getMethodObjectList()
write2Txt(m,o)
write2Python(m)
4.transferAbaquGUIFunctionFromDocumentation.py
# -*- coding:gbk -*-
import re
def handleFlags(fileName='flags.txt'):
'''
DEF_SWATCH_CURSOR
Color swatch drag cursor.
DEF_MOVE_CURSOR
Move cursor.
DEF_DRAGH_CURSOR
Resize horizontal edge.
DEF_DRAGV_CURSOR
Resize vertical edge.
DEF_DRAGTL_CURSOR
Resize upper-leftcorner.
DEF_DRAGBR_CURSOR
Resize bottom-right corner.
DEF_DRAGTR_CURSOR
Resize upper-right corner.
'''
with file(fileName,'r') as fp:
dat = [l.strip() for l in fp.readlines() if len(l.strip()) > 0]
nums = int(len(dat)/2)
with file(fileName+'.py','w') as fp:
for i in range(nums):
fp.write('%-40s = %2d # %s\n' % (dat[2*i], i, dat[2*i+1]))
def handleFunctions(fileName='temp.txt'):
'''不含尾部flags
AFXApp
This class is responsible forproviding some high-level GUI controlmethods.
AFXApp(appName=Abaqus/CAE, vendorName=SIMULIA,productName='', majorNumber=-1, minorNumber=-1,updateNumber=-1, prerelease=False)
Constructor.
Argument Type Default Description
appName String Abaqus/CAE Application registry key.
vendorName String SIMULIA Vendor registry key.
productName String '' Product name.
majorNumber Int -1 Version number.
minorNumber Int -1 Release number.
updateNumber Int -1 Update number.
prerelease Bool False Official/Prerelease flag.
create()
Creates windows for theapplication.
Reimplemented fromFXApp.
getAFXMainWindow()
Returns a pointer to theAFXMainWindow.
getBasePrerelease()
Returns True if the base product isa prerelease.
getBaseProductName()
Returns the base productname.
getBaseVersionNumbers(majorNumber, minorNumber,updateNumber)
Returns the base product's major,minor, and update numbers.
Argument Type Default Description
majorNumber Int Version number.
minorNumber Int Release number.
updateNumber Int Update number.
getKernelInitializationCommand()
Returns the command string thatwill be issued upon application startup.
getPrerelease()
Returns True if this is aprerelease.
getProductName()
Returns the productname.
getVersionNumbers()
Returns the major, minor, andupdate numbers.
init(argc,argv)
Initializes the application andconnects to the kernel.
Argument Type Default Description
argc Int
argv String
isLocked()
Returns True if the GUI is lockedor False if otherwise.
Reimplemented fromFXApp.
isProductCAE()
Returns True if the base product isAbaqus/CAE.
isProductViewer()
Returns True if the base product isAbaqus/Viewer.
isStudentEdition()
Returns True if the base product isa student edition.
lock()
Locks the GUI (normally used duringcommand and mode processing).
run()
Runs the main application eventloop until stop() is called,.
Reimplemented fromFXApp.
runUntil(condition)
Run an event loop till some flagbecomes non-zero.
Reimplemented fromFXApp.
Argument Type Default Description
condition Int
unlock()
Unlocks theGUI.
'''
#清理空行
with file(fileName,'r') as fp:
dat = [l.strip() for l in fp.readlines() if len(l.strip())>0]
def isMethodDefinitionLine(instr):
if instr.endswith(')'):
return True
else:
return False
with file(fileName+'.py','w') as fp:
isWritingFunction = False
isAddParam = False
fp.write('class %s(object):\n' % dat[0])
fp.write(' """\n')
fp.write(' %s\n' % dat[1])
fp.write(' """\n')
for l in dat[2:]:
if isMethodDefinitionLine(l):
isAddParam = False
if isWritingFunction:
fp.write(' """\n')
isWritingFunction = True
id = l.index('(')
name = l[:id]
arg = l[id+1:]
# name,arg = l.split('(')
if arg == ')':
fp.write(' def %s(self):\n' % (name,))
else:
fp.write(' def %s(self, %s:\n' % (name,arg))
fp.write(' """\n')
else:
if isWritingFunction:
if l.startswith('Argument'):
isAddParam = True
fp.write(' %s\n\n' % (l,))
continue
if isAddParam:
fp.write(' @param %s\n\n' % (l,))
continue
fp.write(' %s\n\n' % (l,))
else:
fp.write('%s\n' % (l,))
fp.write(' """\n')
# handleFunctions()
# handleFlags()
def getUltiMate():
import re
with file('abaqusGui.py','r') as fp:
d = fp.read()
ptn = re.compile('[A-Z]\w+')
rst = ptn.findall(d)
rs = [r for r in rst if r.isupper()]
rs = list(set(rs))
rs.sort()
# with file('consts.txt','w') as fp:
# for i,r in enumerate(rs):
# fp.write('%-40s = %4d\n' % (r,i))
return set(rs)
# getUltiMate()
def getConstants2():
import re
with file('abaqusGui.py','r') as fp:
d = fp.readlines()
lsts = [] #name, value, info
for l in d:
if '=' in l and '#' in l and l.startswith(' '):
lsts.append(l.lstrip())
ptn = re.compile('[A-Z]\w+')
clst = set([ptn.findall(l)[0] for l in lsts])
aset = getUltiMate()
aset = aset.difference(clst)
with file('consts.txt','w') as fp:
for i,v in enumerate(list(aset)):
if len(v) >1:
fp.write('%-40s = %4d\n' % (v,i))
fp.write('\n'*4)
for i,r in enumerate(lsts):
fp.write(r)
# getConstants2()
def transferParam():
''' @param owner FXWindow
改为
:param owner: FXWindow'''
with file('abaqusGui.py','r') as fp:
d = fp.readlines()
ptn = re.compile('\w+')
with file('abaqusGui_new.py','w') as fp:
for l in d:
if '@param' in l:
l = l.replace('@',':')
varName = ptn.findall(l)[1]
l = l.replace(varName,varName+':')
fp.write(l)
# transferParam()