目 录
Blog Links
一、前言
物理世界通常可以用偏微分方程来完整描述,对于复杂问题,偏微分方程很难且往往不能求得解析解。采用有限单元法便可以离散求解,求解的精度取决于离散的程度,求解域被离散的越细小,求解精度越高,得到的离散方程组中方程的个数就越多。为了满足必要的解答精度,方程组的规模往往是相当巨大的,这就为手动求解带来挑战。随着计算机技术的迅速发展,使大型方程组的求解变得更加高效、快捷,许多有限元软件由此应运而生,Abaqus 就是其中较为成功的一款。
有限单元法的诞生,将现实世界 “ 时空离散化、数字化 ” ,构造出这些偏微分方程组的近似数值方程,并用数值方法求解。从此,我们可以在数位世界模拟现实物理世界了。
在 Abaqus 中,一个物理问题的最终解答,主要包括前处理、求解计算和后处理三个过程。前、后处理主要依靠 Abaqus/CAE 实现,求解计算主要依靠 Abaqus 的求解器完成。
前处理的终极目标是生成 .inp 文件,.inp文件是求解器的输入文件,其中包含了对分析模型的完整描述, .inp 文件有其自身的特定格式。所有关于前处理方面的二次开发,最终目的都是为了方便、快速地生成 .inp 文件。.inp 文件确定了方程组的规模,规定了求解方式,定义了输出的物理量等,求解器根据 .inp 的请求,采用各种数学方法,在不显著降低求解精度的前提下,尽可能快速的求解方程,并将计算结果存储在 .odb 文件中。
inp 文件不是脚本文件,书写 inp 文件也不是编程,它只是按照 Abaqus 求解器的计算要求,而形成的文本文件。它是前处理 (Abaqus/Pre) 和求解器 (Abaqus/Standard) 之间的交流工具,它包含了对分析模型的完整描述。常见的文本编辑器如 UltraEdit 等均可打开并编辑 inp 文件,inp 文件内几乎记录着 Abaqus 模型的全部信息,如网格信息、材料属性、接触关系以及分析类型、场输出等等。(用户子程序通常不包含在inp文件内)
通常,我们没必要直接创建 inp 文件,但它体积小巧且为纯文本格式,方便修改和编辑。另外,各版本间的兼容性好,这就打破了 Abaqus 版本的限制,使模型数据能在各版本间有效传递,同时也为方便与其他数值模拟软件进行数据交换。我们还可以将 inp 文件模块化,将常用的数据信息提前存储到 inp 文件中,方便后续重复调用或不同模型共享通用数据等等。
本文主要利用 Python 语言,将 inp 文件内主要信息的创建程序化,以实现模型创建的自动化。
二、inp文件
2.1 帮助文档
Abaqus inp 文件内数据的书写规则由官方帮助文档给出,详见:Abaqus Keywords Reference Guide.
2.2 inp文件的构成
inp 文件是前处理 (Abaqus/Pre) 和求解器 (Abaqus/Standard等) 之间的交流工具,它包含了对分析模型的完整描述。inp 文件是一个直观的、基于由关键字格式组成的字符文件。inp 文件主要由注释行、关键字行和数据行构成。
- 注释行:两个星号 ( ** ) 开始的行为注释行。
- 关键字行:一个星号 ( * ) 开始的行为关键字行。
- 数据行:关键字行下面紧接着的通常为数据行。
inp 文件中的每一行不能超过 80 个字符。
采用 Python 语言创建 inp 文件的目的是提高数值模型创建的效率,我们的主要目的是在已有的网格信息文件(csv文件)的基础上,快速复建出 Abaqus 模型。因此,只需要在 inp 文件中写入模型的基本信息即可。典型的 inp 文件组成如下图所示,主要由,标题数据块(*Heading)、打印输出数据块(*Preprint)、部件数据块(*Part)、装配数据块(*Assembly)
2.3 inp文件数据库
inpDatabase = {"Title": ["*Heading", ' CurrUnits="N, m, kg"', "** Job name: Job-20210916 Model name: Model--20210916",
"** Generated by: Abaqus/CAE 6.14-1"],
"Printout": ["*Preprint, echo=NO, model=NO, history=NO, contact=NO"],
"Parts": ["**", "** PARTS", "**", "N/A", "**"],
"Assembly": ["**", "** ASSEMBLY", "**", "*Assembly, name=Assembly", "**", "N/A", "*End Assembly"],
"Materials": ["**", "** MATERIALS", "**", "N/A"],
"Amplitudes": ["**", "** AMPLITUDE CURVES", "**", "N/A", ],
"Steps": {"N/A"}, } # inp文件数据库
parts = [] # 部件信息列表
materials = [] # 线弹性材料信息列表
assembly = [] # 装配信息列表
amplitudes = [] # 幅值曲线信息列表
三、分析标题
inp 文件的开始必须冠以 *HEADING,在 *HEADING 下面的数据是用字符来说明所模拟的问题,它将提供对 inp 文件的准确描述,以便以后对该文件进行识别。此外,最好明确地说明量纲系统和总体坐标系的方向等。
*HEADING 下面的字符块仅第一行出现在 odb 文件显示界面的 title block 中,其余均不显示,如下图:
由上可知,创建作业时 Description 里输入的内容、inp 文件中 *HEADING 下的第一行字符和 odb 文件显示界面中 title block 的第一行为同一内容。
四、部件/Part
在 inp 文件中的部件数据块内,主要定义部件的结点、单元、截面属性、结点集合和单元集合等等,而在 Abaqus/CAE 中,这些主要是由 Part、Proerty 和 Mesh 三个模块来共同实现的。
当我们已知部件的网格信息如结点编号和坐标、单元编号以及组成单元的各结点编号时,通过直接创建 Abaqus 的 inp 文件的方式来创建有限元模型可能更为方便,基于 Abaqus 的关键字帮助文档,我们定义 Python 函数 create_part_data_lines ,用于创建生成部件的数据行,Python 函数如下:
def create_part_data_lines(prtname, nodes=[], elements=[], nodesets=[], elemsets=[]):
# 作者:DalNur;邮箱:liyang@alu.hit.edu.cn
# 功能:创建定义部件的数据行
# 参数:partname为部件名称;nodes为结点信息列表;element为单元信息列表
# 参数:nodesets为部件内结点集合信息列表;elemsets为部件内单元集合信息列表
# 返回:inp文件的数据行(data lines)
line1 = "*Part, name=%s" % prtname
datalines = [line1, ]
nodedatalines = ["*Node", ] # 定义结点的数据行
elemdatalines = [] # 定义单元的数据行
for node in nodes:
nlabel, x, y, z = node[0], node[1], node[2], node[3]
# 结点标签、x坐标值、y坐标值、z坐标值
s1 = "%s," % nlabel
s2 = "%s," % float(x)
s3 = "%s," % float(y)
s4 = "%s," % float(z)
s1 = s1.rjust(8, " ")
s2 = s2.rjust(14, " ")
s3 = s3.rjust(14, " ")
s4 = s4.rjust(13, " ")
line2 = s1 + s2 + s3 + s4
nodedatalines.append(line2)
elemInfos = {"SOLID": {"C3D4": [], "C3D6": [], "C3D8R": [], "C3D10": [], "C3D15": [], "C3D20R": [], },
"BEAM": {"B31": [], "B32": [], },
"SHELL": {"S3": [], "S4R": [], "S8R": [], }}
for elem in elements:
elabel, etype, nodelabels = elem[0], elem[1], elem[2:]
nodenum = len(nodelabels)
if etype == "SOLID":
if nodenum == 4:
elemInfos["SOLID"]["C3D4"].append([elabel, nodelabels])
if nodenum == 6:
elemInfos["SOLID"]["C3D6"].append([elabel, nodelabels])
if nodenum == 8:
elemInfos["SOLID"]["C3D8R"].append([elabel, nodelabels])
if nodenum == 10:
elemInfos["SOLID"]["C3D10"].append([elabel, nodelabels])
if nodenum == 15:
elemInfos["SOLID"]["C3D15"].append([elabel, nodelabels])
if nodenum == 20:
elemInfos["SOLID"]["C3D20R"].append([elabel, nodelabels])
if etype == "SHELL":
if nodenum == 3:
elemInfos["SHELL"]["S3"].append([elabel, nodelabels])
if nodenum == 4:
elemInfos["SHELL"]["S4R"].append([elabel, nodelabels])
if nodenum == 8:
elemInfos["SHELL"]["S8R"].append([elabel, nodelabels])
if etype == "BEAM":
if nodenum == 2:
elemInfos["BEAM"]["B31"].append([elabel, nodelabels])
if nodenum == 3:
elemInfos["BEAM"]["B32"].append([elabel, nodelabels])
# solidelems = elemInfos["SOLID"] # 实体单元
# shellelems = elemInfos["SHELL"] # 壳单元
# beamelems = elemInfos["BEAM"] # 梁单元
for etype in elemInfos.keys():
# 单元类型为单元的几何类型/实体单元/梁单元/壳单元等
for etname in elemInfos[etype].keys():
# 单元名称为C3D8、C3D20等等
elems = elemInfos[etype][etname]
if len(elems) > 0:
line3 = "*Element, type=%s" % etname
elemdatalines.append(line3)
for elem in elems:
elabel, nodelabels = elem
s1 = "%s," % elabel
line4 = s1
for nodelabel in nodelabels:
s3 = "%s," % nodelabel
s3 = s3.rjust(8, " ")
line4 = line4 + s3
if len(nodelabels) <= 8:
elemdatalines.append(line4[:72])
else:
elemdatalines.append(" " + line4[72:])
for nodeset in nodesets:
setname, nodelabels = nodeset[0], nodeset[1]
line5 = "*Nset, nset=%s" % setname
elemdatalines.append(line5)
rst1, rst2 = len(nodelabels) // 8, len(nodelabels) % 8
for k in range(rst1):
i, j = int(k * 10), int(k * 10 + 10)
line6 = ""
for nodelabel in nodelabels[i:j]:
s4 = "%s," % nodelabel
s5 = s4.rjust(8, " ")
line6 = line6 + s5
line6 = line6[:-1]
elemdatalines.append(line6)
k = rst1 * 10
line7 = ""
for nodelabel in nodelabels[k:]:
s6 = "%s," % nodelabel
s7 = s6.rjust(8, " ")
line7 = line7 + s7
if len(line7) > 0:
line7 = line7[:-1]
elemdatalines.append(line7)
for elemset in elemsets:
setname, elemlabels = elemset[0], elemset[1]
line8 = "*Elset, elset=%s" % setname
elemdatalines.append(line8)
rst1, rst2 = len(elemlabels) // 8, len(elemlabels) % 8
for k in range(rst1):
i, j = int(k * 10), int(k * 10 + 10)
line9 = ""
for elemlabel in elemlabels[i:j]:
s8 = "%s," % elemlabel
s9 = s8.rjust(8, " ")
line9 = line9 + s9
line9 = line9[:-1]
elemdatalines.append(line9)
k = rst1 * 10
line1 = ""
for elemlabel in elemlabels[k:]:
s1 = "%s," % elemlabel
s2 = s1.rjust(8, " ")
line1 = line1 + s2
if len(line1) > 0:
line1 = line1[:-1]
elemdatalines.append(line1)
datalines = datalines + nodedatalines + elemdatalines + ["*End Part", ]
try:
global parts # 声明全局变量
parts = parts + datalines
inpDatabase["Parts"][3] = parts
except:
pass
return datalines
函数 create_part_data_lines 需要三个输入参数,分别为 partname,nodes 和 elements 。其中,partname 为将要创建的部件名称;nodes 为组成部件的结点信息列表;elements 部件的单元信息列表。在此,我们以创建一 8 结点实体单元为例,示例代码如下:
n1 = ["11", 0, 0, 0] # 结点编号11/结点坐标(0, 0, 0)
n2 = ["12", 10, 0, 0] # 结点编号12/结点坐标(10, 0, 0)
n3 = ["13", 10, 10, 0] # 结点编号13/结点坐标(10, 10, 0)
n4 = ["14", 0, 10, 0] # 结点编号14/结点坐标(0, 10, 0)
n5 = ["15", 0, 0, 30] # 结点编号15/结点坐标(0, 0, 30)
n6 = ["16", 10, 0, 30] # 结点编号16/结点坐标(10, 0, 30)
n7 = ["17", 10, 10, 30] # 结点编号17/结点坐标(10, 10, 30)
n8 = ["18", 0, 10, 30] # 结点编号18/结点坐标(0, 10, 30)
nodes = [n1, n2, n3, n4, n5, n6, n7, n8] # 结点信息列表
elem1 = ["1", "SOLID", "11", "12", "13", "14", "15", "16", "17", "18"]
# 单元编号1/单元类型为实体单元/组成单元的结点编号依次为:"11"、"12"、"13"、"14"、"15"、"16"、"17"和"18"
elements = [elem1, ] # 单元信息列表
partname = "Part-1" # 部件名称
nodesets = [["Set-Nodes-1", [11, 12, 13, 14, 15, 16, 17, 18, 18, 17, 16, 15, 14, 13, 12, 11, ]],
["Set-Nodes-2", [11, 13, 15, 17, ]], ]
# 结点集合信息列表/标签可重复
elemsets = [["Set-Elems-1", [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ]],
["Set-Elems-2", [1, ]], ]
# 单元集合信息列表/标签可重复
datalines = create_part_data_lines(partname, nodes, elements, nodesets, elemsets)
for line in datalines:
print(line)
运行以上代码,将得到创建部件的数据行,如下图所示,将下图红框中的数据行复制到一全新 txt 文件中,并将文件另存为 Test.inp 文件。
现将 Test.inp 文件导入到 Abaqus/CAE中,具体操作如下:Abaqus/CAE Fiel >> Import >> Model ,而后
选择 Test.inp 文件,单击 Ok 后,Test.inp 被导入到 Abaqus/CAE 中,如下图所示。
五、装配/Assembly
在 inp 文件中的装配数据块内,主要定义部件的装配、部件间的接触关系等,而在 Abaqus/CAE 中,这些主要是由 Assembly 和 Interation 两个模块来实现。
def create_assembly_data_lines(istname, prtname):
# 作者:DalNur;邮箱:liyang@alu.hit.edu.cn
# 功能:创建装配数据行
# 参数:istname为装配的部件实例名称;prtname为与之相对应的部件名称。
# 返回:inp文件的数据行(data lines)
global datalines
datalines = []
line1 = "*Instance, name=%s, part=%s" % (istname, prtname)
line2 = "*End Instance"
line3 = "**"
datalines = datalines + [line1, line2, line3]
try:
global assembly # 声明全局变量
assembly = assembly + datalines
inpDatabase["Assembly"][5] = assembly
except:
pass
# if option:
# datalines = ["**", "** ASSEMBLY", "**", "*Assembly, name=Assembly", "**", ] + datalines + ["*End Assembly", ]
return datalines
函数 create_assembly_data_lines 用来创建模型装配数据行,它需要两个参数即 istname 和 prtname,其中,istname 为装配的部件实例名称,prtname 为相应的部件名称,示例如下。
ist1name, prt1name = "Inst-1", "Part-1" # 装配体名称、部件名称
ist2name, prt2name = "Inst-2", "Part-1" # 装配体名称、部件名称
datalines = create_assembly_data_lines(ist1name, prt1name)
for line in datalines:
print(line)
datalines = create_assembly_data_lines(ist2name, prt2name)
for line in datalines:
print(line)
六、材料/Material
在 inp 文件中的材料数据块内,主要定义材料的属性,如弹性、阻尼等等,这部分仅与 Abaqus/CAE 中 Property 模块内的 Material 相对应。
创建定义材料关键字的 Python 函数为 create_material_data_lines ,其中,参数 matname 为定义的材料名称,rho、e 和 nu 分别为材料的质量密度、弹性模量和泊松比。alpha 和 beta 分别为瑞丽阻尼系数的质量系数和刚度系数;structural 为结构阻尼系数。
def create_material_data_lines(matname, rho, e, nu, alpha=0, beta=0, composite=0, structural=0):
# 作者:DalNur;邮箱:liyang@alu.hit.edu.cn
# 功能:创建材料属性数据行
# 参数:matname、rho、e和nu分别为材料参数、质量密度、弹性模量和泊松比。
# 参数:alpha和beta为瑞丽阻尼系数,分别为质量系数和刚度系数。
# 参数:structural为结构阻尼系数。
# 返回:inp文件的数据行(data lines)
datalines = []
line1 = "*Material, name=%s" % matname
line2 = "*Damping, alpha=%s, beta=%s, composite=%s, structural=%s" % (alpha, beta, composite, structural)
line3 = "*Density"
line4 = "%s," % rho
line5 = "*Elastic"
line6 = "%s, %s" % (e, nu)
datalines = datalines + [line1, line2, line3, line4, line5, line6, ]
try:
global materials # 声明全局变量
materials = materials + datalines
inpDatabase["Materials"][3] = materials
except:
pass
return datalines
现创建两个材料,名称分别为 Q235 和 Q355 ,相应的材料属性如下所示。
matname, rho, e, nu = "Q235", 7850, 2.06E11, 0.3
alpha, beta, composite, structural = 0.0, 0.0, 0.0, 0.0
datalines = create_material_data_lines(matname, rho, e, nu, alpha, beta, composite, structural)
for line in datalines:
print(line)
matname, rho, e, nu = "Q355", 7850, 2.06E11, 0.3
datalines = create_material_data_lines(matname, rho, e, nu, alpha, beta, composite, structural)
for line in datalines:
print(line)
七、幅值曲线/Amplitude
在进行复杂动力学分析时,变化的动荷载等需要在 Abaqus 中定义为时间-幅值曲线,可以采用 Python 编程的方式在 Abaqus/CAE 中批量导入,也可以将幅值文件直接存储为子 inp 文件件,供主计算 inp 文件调用。创建幅值数据子 inp 文件的 Python 函数 create_amplitude_inp_file,如下所示。
def create_amplitude_inp_file(path, ampname="Amp-1", ampdata=[]):
# 作者:DalNur;邮箱:liyang@alu.hit.edu.cn
# 功能:创建幅值曲线子inp文件、方便主inp文件的引用
# 参数:path为子inp文件的路径;ampname幅值曲线的名称;ampdata为幅值曲线包含的数据列表。
# 返回:inp文件的路径
times, ampls = ampdata[0], ampdata[1]
num1, num2 = len(times), len(ampls)
num = min(num1, num2)
rst1, rst2 = num // 4, num % 4
line1 = "*Amplitude, name=%s" % ampname
with open(path, 'w') as f:
f.write(line1 + "\n")
for k in range(rst1):
line2 = ""
i, j = int(4 * k), int(4 * k + 4)
for time, amp in zip(times[i:j], ampls[i:j]):
s1 = "%s," % time
s2 = s1.rjust(17, " ")
s3 = "%s," % amp
s4 = s3.rjust(17, " ")
line2 = line2 + s2 + s4
line2 = line2[:-1]
f.write(line2 + "\n")
line3 = ""
k = int(4 * rst1)
for time, amp in zip(times[k:], ampls[k:]):
s1 = "%s," % time
s2 = s1.rjust(17, " ")
s3 = "%s," % amp
s4 = s3.rjust(17, " ")
line3 = line3 + s2 + s4
line3 = line3[:-1]
f.write(line3 + "\n")
return path
times = [i for i in range(1000)]
ampls = [i for i in range(1000)]
path = r"E:\AbqWorkDir\Amp-1.inp"
ampname = "Amp-1"
ampdata = [times, ampls]
create_amplitude_inp_file(path, ampname, ampdata)
由以上代码创建的幅值曲线 inp 文件 Amp-1.inp ,可将其导入到 Abaqus/CAE 中,如下所示。
def create_ampltitude_data_lines(paths=[]):
# 作者:DalNur;邮箱:liyang@alu.hit.edu.cn
# 功能:创建幅值曲线子数据行
# 参数:paths为幅值曲线子inp文件的路径列表
# 返回:数据行
datalines = []
for path in paths:
line1 = "*Include, input=%s" % path
line2 = "**"
datalines.append(line1)
datalines.append(line2)
try:
global amplitudes # 声明全局变量
amplitudes = amplitudes + datalines
inpDatabase["Amplitudes"][3] = amplitudes
except:
pass
return datalines
# 示例
paths = [path, ] # 幅值曲线inp文件路径列表
paths = [path, ]
create_ampltitude_data_lines(paths)
八、分析步/Step
在 inp 文件中的分析步数据块内,主要定义分析类型、场输出、时间历程输出、加载情况、边界条件等,这些功能主要与 Abaqus/CAE 中的 Step 和 Load 模块相对应。其中,初始边界条件定义在分析步数据块外。
在多工况分析中,分析步数据块一般单独定义在一个子 inp 文件夹内,供主计算 inp 文件调用。主计算 inp 文件中必须包含标题数据块 * Heading。
九、inp文件的总成
根据 inp 文件数据库 inpDatabase 内的数据,我们可以创建包含模型基本信息的 inp 文件,将其导入到 Abaqus/CAE 中,可对模型进行进一步处理。实现这一功能的 Python 函数为 create_job_inp_file ,代码如下。
显然,采用本文的方式可实现任意数值模型在 Abaqus 中的复建。基本思路是,我们用 csv 文件存储模型的基本信息,如部件结点和单元信息、集合信息等等,以 Python 语言为媒介,读取 csv 文件内的信息,而后创建 inp 文件。那么,我们再大胆一步,任何软件的有限元模型都可以通过读取 csv 文件内的数据,进行模型复建,这样,我们便以 Python 语言和 csv 文件构建起模型数据在各软件内传递的通道。
def create_job_inp_file(path, data):
# 作者:DalNur;邮箱:liyang@alu.hit.edu.cn
# 功能:创建作业的inp文件/主计算文件
# 参数:path为创建的inp文件绝对路径;data为inp文件数据库。
# 返回:创建的inp文件绝对路径。
with open(path, 'w') as f:
# 01-书写标题数据块
dtname = "Title"
for line in data[dtname]:
f.write(line + "\n")
# 02-书写打印数据块
dtname = "Printout"
for line in data[dtname]:
f.write(line + "\n")
# 03-书写部件数据块
dtname = "Parts"
datalines = data[dtname][:3] + data[dtname][3] + data[dtname][4:]
for line in datalines:
f.write(line + "\n")
# 04-书写装配数据块
dtname = "Assembly"
datalines = data[dtname][:5] + data[dtname][5] + data[dtname][5:]
for line in datalines:
f.write(line + "\n")
# 05-书写材料数据块
dtname = "Materials"
datalines = data[dtname][:3] + data[dtname][3] + data[dtname][3:]
for line in datalines:
f.write(line + "\n")
# 06-书写幅值数据块
dtname = "Amplitudes"
datalines = data[dtname][:3] + data[dtname][3] + data[dtname][3:]
for line in datalines:
f.write(line + "\n")
return path
将以上本文所列出的所有 Python 代码放置到一个 py 文件内,并运行下列代码,将得到最终的 inp 文件,即 SZ12-20210917-1330.inp,如下图所示。
path = r"E:\AbqWorkDir\SZ12-20210917-1330.inp" # 刚刚,神州十二号顺利着陆。
data = inpDatabase # inp文件数据库
create_job_inp_file(path, data) # 创建inp文件
十、尾声
行文至此,自动化创建 Abaqus inp 文件的各主要 Python 函数已介绍完毕。
根据本文列出的各 Python 函数,稍作加工便可实现任意数值模型在 Abaqus 中的复建。
本文是本人 Python 自动化建模技术的重要组成部分,因此偏向于自下而上创建有限元模型。
本文的顺利完成,标志着博主在 Python 自动化建模技术方面近四年的努力取得阶段性胜利,相关工作亦接近尾声。
自 2015 年 3 月 22 号开始接触 Abaqus 软件以来,经过多年的不懈努力,
实现了从最初的人肉点鼠标到现在的全自动化建模的跨越。
特此记录,借以总结,同时也能方便后学者。
如有疑问、合作需求及推荐工作,请联系邮件联系本人,Email: liyang@alu.hit.edu.cn 。
本文仅用于个人学习,除此之外,无其他任何用途。
因个人水平有限,文中难免有所疏漏,还请各位大神不吝批评指正。
胸藏文墨怀若谷,腹有诗书气自华,希望各位都能在知识的 pāo 子里快乐徜徉。
本文首次发表于 2021-09-17 16:21:31,Beijing 。
欢迎大家点赞、评论及转载,转载请注明出处!
为我打call,不如为我打款!
最后,祝各位攻城狮们,珍爱生命,保护发际线!