工程师平台
Abaqus 实战2026-09-08

材料这一栏最适合交给脚本,也最容易静默写错

材料这一栏是 Abaqus 里最适合交给脚本的地方: 它没有几何、不用选面、不涉及交互,全部输入就是一堆数字和几个枚举值。 这篇给一个真正能跑的最小例子,然后把四个「Abaqus 不会替你查、写错了也不报错」的地方逐个点出来 —— 元组的那个逗号、常量不能写成字符串、温度依赖标志、以及覆盖已有材料的正确顺序。
建材料只要两个调用
model.Material(name=...) 拿到对象, 剩下的每种属性各是它身上的一个方法。没有别的结构。
单行表格的那个逗号
table=((210000.0, 0.3),) 末尾的逗号漏了, 就变成两个数而不是一行两列 —— 这是纯 Python 陷阱。
温度依赖必须自己声明
Abaqus 只数列数,数不出你多的那一列是温度还是第七个弹性常数。 这一位错了,不报错。

01最小可用例

先看一个完整能跑的:在当前 mdb 的某个 model 里建一个带密度、 弹性、塑性的材料。

# -*- coding: utf-8 -*-
from abaqus import mdb
from abaqusConstants import ISOTROPIC

model = mdb.models['Model-1']

mat = model.Material(name='Steel_Q345')
mat.setValues(description='structural steel')

mat.Density(table=((7.85e-09,),), temperatureDependency=False)

mat.Elastic(type=ISOTROPIC,
            table=((206000.0, 0.3),),
            temperatureDependency=False)

mat.Plastic(hardening=ISOTROPIC,
            table=((345.0, 0.0),
                   (420.0, 0.02),
                   (480.0, 0.06),
                   (520.0, 0.15)),
            temperatureDependency=False)
这些数字是哪来的
上面的数值取自插件自带示例库里的演示材料,是用来跑通流程的演示值,不是材料数据。 自己的项目请用有出处的数据替换。单位制是 tonne-mm-s —— 密度 7.85e-09 那个量级为什么长这样,在Abaqus 单位制怎么选里推过。

结构就这么简单:Material(name=...) 返回一个材料对象, 之后每种物理属性都是这个对象上的一个方法,各自吃一张表。 没有中间层,没有需要先创建的容器。

模型名不能想当然

mdb.models['Model-1'] 里的名字写错会直接抛异常。 要跟着「当前正在看的那个 model」走,用的是这一串:

from abaqus import mdb, session

def current_model_name():
    try:
        viewport = session.viewports[session.currentViewportName]
        name = viewport.displayedObject.modelName
        if name in mdb.models:
            return name
    except (AttributeError, KeyError):
        pass
    names = sorted(mdb.models.keys())
    if not names:
        raise ValueError('the current mdb has no models')
    return names[0]

注意最后那个兜底:取不到当前视口时退回按名字排序的第一个。 这条规则在多 model 的文件里制造过不少误会 ——Analysis-Final 会排在 Model-1 前面, 因为 A 在 M 前。相关的排查在材料建了却不生效那篇的第一个断点。

02坑一:单行表格末尾那个逗号

这是写材料脚本时最常见的一个错,而且它是纯 Python 的问题, 跟 Abaqus 一点关系都没有。

table=((206000.0, 0.3),)     # 一行两列,对
table=((206000.0, 0.3))      # 括号只是分组 —— 这是两个数

第二行里的外层括号没有逗号,Python 把它当成分组括号直接去掉了, 于是传进去的是 (206000.0, 0.3), 一个长度 2 的元组,而不是「一行、这一行有两个数」。

多行的时候不会出这个问题,因为行之间自然有逗号。 所以它专挑只有一行的场合下手 —— 也就是绝大多数等向弹性、密度、单点热膨胀。

一个不用记逗号的写法
把行先攒进一个 list,最后转成元组,就绕过了这个语法:
rows = []
rows.append((206000.0, 0.3))
mat.Elastic(type=ISOTROPIC, table=tuple(rows))
批量生成材料时本来就要循环攒行,顺手就避开了。

顺带一个相关的:属性块的参数吃的是,不是数。 密度只有一列,很容易顺手写成 table=7.85e-09, 那会得到一句和材料毫无关系的报错 ——argument of type 'float' is not iterable, 或者 'float' object has no attribute 'get'。 看到这两句里的 float,去找哪个地方本该是表却给了个数。

03坑二:常量不是字符串

ISOTROPICJOHNSON_COOKDISPLACEMENTENERGYLINEARMAXIMUMONOFF —— 这些全是 abaqusConstants 里的符号常量, 不是字符串。写成 'ISOTROPIC' 不等价。

所以脚本开头这一行不能省:

from abaqusConstants import *

如果你的数据来源是配置文件、CSV 或者 Excel, 那里存的一定是字符串,需要在中间转一次。 插件内部就是这么做的,做法可以直接抄:

import sys

def to_constant(name, default=None):
    if name is None:
        return default
    key = name.strip().upper()
    try:
        return getattr(sys.modules['abaqusConstants'], key)
    except AttributeError:
        raise ValueError('unknown Abaqus constant "%s"' % key)

getattr 而不是自己维护一张映射表, 有一个实际好处:这个 Abaqus 版本里没有的常量会在用到它的时候报一句能读懂的话,而不是在脚本 import 阶段就整个崩掉。 版本之间常量增删的时候,这个差别很值钱。

04十类属性块的调用与列顺序

这是全篇最该收藏的一张表。 列顺序写错不会报错(列数对得上就照收), 所以它是这一栏里代价最高的一类错。

属性调用每行的列顺序
密度mat.Density(table=, temperatureDependency=)value
等向弹性mat.Elastic(type=ISOTROPIC, table=, temperatureDependency=)youngsModulus, poissonsRatio
塑性(等向硬化)mat.Plastic(hardening=ISOTROPIC, table=, temperatureDependency=)yieldStress, plasticStrain
塑性(Johnson-Cook)mat.Plastic(hardening=JOHNSON_COOK, table=)A, B, n, m, meltingTemperature, transitionTemperature
应变率相关mat.plastic.RateDependent(type=, table=)JOHNSON_COOK 型:C, epsilonDot0;其它:multiplier, exponent
导热系数mat.Conductivity(type=, table=, temperatureDependency=)value
比热mat.SpecificHeat(table=, temperatureDependency=)value
热膨胀mat.Expansion(type=, table=, temperatureDependency=, zero=)alpha(zero 是标量,不是列)
蠕变mat.Creep(law=, table=, temperatureDependency=)STRAIN 与 TIME:A, n, m;HYPERBOLIC_SINE:A, B, n, deltaH, R
阻尼mat.Damping(alpha=, beta=, composite=, structural=)四个都是标量,不用表;至少给一个
韧性损伤起始mat.DuctileDamageInitiation(table=, temperatureDependency=)fractureStrain, stressTriaxiality, strainRate
剪切损伤起始mat.ShearDamageInitiation(table=, temperatureDependency=, ks=)fractureStrain, shearStressRatio, strainRate(ks 是标量)
JC 失效起始mat.JohnsonCookDamageInitiation(table=)d1, d2, d3, d4, d5, meltingTemperature, transitionTemperature, referenceStrainRate
FLD 起始mat.FldDamageInitiation(table=, temperatureDependency=)minorStrain, majorStrain(次应变在前)
损伤演化initiation.DamageEvolution(type=, table=, softening=, degradation=)DISPLACEMENT 型:位移;ENERGY 型:断裂能
三行高亮:Johnson-Cook 的 Plastic 不接 temperatureDependency;剪切损伤的 ks 是标量参数而不是表的一列;FLD 两列是次应变在前。这三处是逐行核过插件源码得出的,写反了都不报错。

这几个块各自的含义和填法,批 4 那三篇讲得更细:韧性损伤Johnson-Cook 两个块剪切损伤与 FLD

损伤演化是独立的第二个调用

这是结构上最容易漏的一处:损伤起始和损伤演化是两个对象, 演化挂在起始对象上,要单独再调一次。

mat.DuctileDamageInitiation(
    table=((0.35, -0.33, 0.001),
           (0.25,  0.0,  0.001),
           (0.18,  0.33, 0.001)),
    temperatureDependency=False)

mat.ductileDamageInitiation.DamageEvolution(
    type=DISPLACEMENT,
    table=((0.5,),),
    softening=LINEAR)

注意方法名是大写开头的 DuctileDamageInitiation, 创建出来的成员名是小写开头的 ductileDamageInitiation —— 这条命名规律在 Abaqus 里是通用的, 要接着往下挂东西就得用小写那个。 只写起始不写演化,导入不报错、材料编辑器里也看得见, 但单元一个都不会删。

超弹和 CDP 各有一个必踩的坑

这两个块结构比别的复杂,各有一处不看源码想不到的:

  • 超弹用试验数据时,table 仍然是必填的。虽然系数由 Abaqus 自己拟合、根本没有系数可给, 但少了这个参数会得到 expected 1, got 0。 办法是传一个空元组:table=(),同时testData=ON
  • CDP 的子表构造器带 Concrete 前缀。cdp.ConcreteCompressionHardening(...), 不是 cdp.CompressionHardening(...)。 少了前缀会得到object has no attribute 'CompressionHardening', 而这句话里没有任何字提示你缺的是前缀。

05坑三:温度依赖标志,Abaqus 数不出来

给材料加温度相关性的做法是每行末尾多一列温度。 于是问题来了:Abaqus 只能数出这一行有几个数, 数不出多出来的那一个是温度还是别的东西。

等向弹性没事,因为它固定两列,第三列只能是温度。 出事的是各向异性 —— 各类型的常数个数是固定的, 记不住就会数错:

弹性类型常数个数带温度列时每行
ISOTROPIC23
TRACTION34
LAMINA67
ORTHOTROPIC910
ENGINEERING_CONSTANTS910
ANISOTROPIC2122
个数出自 Abaqus 关键字手册,插件内核里把这张表原样存着,就是为了把「六个常数」和「五个常数加一个温度」区分开。
一个真实发生过的错法
用「行比列宽就是带温度」这条规则去推,会在各向异性上翻车: 各向异性块本来就没有具名的列,于是这条规则对每一个复合材料都推出「带温度」。 一个正确写了六个常数的 LAMINA,就会被当成五个常数加一个温度导进去 ——G23 被读成温度 3980 度。没有任何一条报错会提到这件事, 模型算得完,刚度是错的。

所以写脚本时的规矩很简单:各向异性弹性一律显式写 temperatureDependency, 不要指望能推。写了之后行宽和它对不上,Abaqus 会当场拦下来 —— 这正是你想要的:把一个静默的错变成一个响亮的错。

温度相关表的排法、同一温度怎么分组、区间外怎么取值, 在温度相关材料表那篇里。

06坑四:覆盖已有材料的正确顺序

脚本跑第二遍的时候材料已经在了。 直觉写法是删掉再建:

del model.materials['Steel_Q345']          # 别这么写
mat = model.Material(name='Steel_Q345')
mat.Elastic(...)                           # 这里一抛异常,材料就真没了

这个写法有一个很贵的失败模式:新数据要是有问题, 旧的已经删掉了,你手工调过的那份材料换来一行报错。 正确的顺序是先建后换:

SCRATCH = 'zzMaterialImporterScratch'

def build_or_replace(model, name, build):
    """build(mat) 负责往材料上写属性;出错时不动已有的那份。"""
    exists = name in model.materials
    scratch = SCRATCH
    counter = 0
    while scratch in model.materials:
        counter += 1
        scratch = '%s%d' % (SCRATCH, counter)

    build_name = scratch if exists else name
    try:
        mat = model.Material(name=build_name)
        build(mat)
    except Exception:
        if build_name in model.materials:
            del model.materials[build_name]
        raise                                  # 旧材料一个字没动

    if exists:
        del model.materials[name]
        model.materials.changeKey(fromName=build_name, toName=name)

多写这十几行,换来的是「失败不丢数据」。 插件内部就是这个模式,暂存名也是这个 —— 它刻意跟被替换的材料没有任何关系, 而不是在原名后面加后缀:Abaqus 的材料名有长度上限, 从原名派生就要截断, 而 Steel_A36_PlateSteel_A36_Pipe截出来的尾巴会撞在一起 —— 那正好是撞了会没人发现的场合。

两个失败点,处理不一样
del model.materials[name] 也可能失败, 最常见的原因是还有 Section 在引用它。 这时候什么都没换、旧的还在,是安全的。 但如果失败发生在 changeKey 那一步, 新材料就是唯一剩下的那份了 ——这时千万不要为了「保持模型整洁」把它删掉, 应该留在暂存名下,让人手工改名。

07怎么跑,以及怎么查 API 名字

脚本有两种跑法:

  • 在 CAE 里跑。File 菜单里的 Run Script,选中脚本文件。 适合边写边试,异常会打到消息区。
  • 无界面批处理。abaqus cae noGUI=build_materials.py, 适合放进流水线。注意这种跑法里不能弹任何对话框。

abaqus.rpy 是查 API 名字最靠得住的办法

Abaqus 会把 GUI 里的操作对应的内核命令记进工作目录下的abaqus.rpy,参数原样照抄。 所以「某个属性在脚本里叫什么」这个问题有一个不用翻手册的答案:

  1. 在 CAE 里手工把这个属性填一遍。
  2. 打开工作目录下的 abaqus.rpy,翻到最后。
  3. 那几行就是等价的脚本,方法名和参数名都在里面。
顺带一个安全提醒
正因为 abaqus.rpy 记录参数原样照抄,不要把口令、令牌这类东西作为参数传给内核函数 —— 它会明文躺在工作目录里。 插件把下载来的材料数据先落成临时文件再让内核去读, 读完立刻删,就是为了绕开这一点。

什么时候别写脚本

脚本的代价是列顺序、常量、温度标志全归你负责, 而这三类错里有两类不报错。 所以如果你要做的事只是「把一份已经定好的材料表建进模型」, 写脚本是在给自己找活干 —— 那种情况用 JSON 文件更省事,格式清单在帮助文档的格式一节,Excel 里的表怎么变成这个文件在Excel 材料表怎么进 Abaqus

脚本真正划得来的是材料本身要算出来的场合: 按温度插值生成一批、按系数扫描生成一批、 或者建材料只是一段更长的建模脚本里的一步。 三条复用路线怎么选、什么时候该用文件什么时候该用脚本,在材料复用到别的模型里对过一遍。插件替你挡掉的那些检查列在产品页上。

常见问题

Abaqus 用 Python 建材料的最少代码是什么?

拿到 model 之后调 Material 拿材料对象,再在这个对象上按需要调各个属性方法。例如取 mdb.models 里的某个 model,调 Material 传 name 参数得到 mat,然后 mat.Density 传 table,mat.Elastic 传 type 和 table,mat.Plastic 传 hardening 和 table。没有中间层,也没有需要预先创建的容器。脚本开头要 from abaqus import mdb 和 from abaqusConstants import 星号,因为 ISOTROPIC 这类值是符号常量不是字符串。

Abaqus 材料的 table 参数为什么报 not iterable?

因为传了一个数而不是一张表。所有属性块的 table 都吃「行的序列」,即使只有一行一列也要包两层。密度写成 table 等于一个浮点数会得到 argument of type float is not iterable 或者 float object has no attribute get 这类和材料毫无关系的报错,看到里面的 float 就去找哪个地方本该是表却给了个数。另一个高频写法错误是单行时漏了外层元组末尾的逗号,那样括号只起分组作用,传进去的是两个数而不是一行两列。

temperatureDependency 要不要显式写?

各向异性弹性必须显式写,其它块可以靠列数推。原因是 Abaqus 只能数出每行有几个数,数不出多的那一个是温度还是第七个弹性常数。各向异性块没有具名的列,靠推会把每个复合材料都判成带温度:一个正确写了六个常数的 LAMINA 会被当成五个常数加一个温度导进去,第六个常数被读成温度,而且没有任何报错会提到这件事。各类型的常数个数是 ISOTROPIC 两个、TRACTION 三个、LAMINA 六个、ORTHOTROPIC 和 ENGINEERING_CONSTANTS 各九个、ANISOTROPIC 二十一个。

脚本重复运行时怎么覆盖已有材料?

不要直接删旧的再建同名的,因为新数据一出错旧的就彻底没了。正确顺序是先在一个和原名无关的暂存名下把新材料整个建出来,建成功了才删旧的,最后用 changeKey 把暂存名改成正式名。暂存名不要从原名派生:Abaqus 的材料名有长度上限,派生要截断,而名字相近的两种材料截出来的尾巴会撞在一起。另外 changeKey 那一步失败时不要为了整洁把新材料删掉,那时它是唯一剩下的那份数据。

损伤演化在脚本里怎么写?

损伤起始和损伤演化是两个独立对象,要分两次调用。先调 DuctileDamageInitiation 之类的方法建起始,再在它创建出来的成员上调 DamageEvolution。方法名是大写开头,创建出来的成员名是小写开头,要接着往下挂东西必须用小写那个。只写起始不写演化,导入不会报错、材料编辑器里也看得见,但单元一个都不会删。

怎么知道某个材料属性在脚本里叫什么名字?

在 CAE 里手工把这个属性填一遍,然后打开工作目录下的 abaqus.rpy 翻到最后,那几行就是等价的脚本,方法名和参数名都在里面。Abaqus 会把 GUI 操作对应的内核命令记进这个文件,参数原样照抄。也正因为它原样照抄,不要把口令令牌这类东西作为参数传给内核函数,那会明文留在工作目录里。

这一步可以不自己做

列顺序、符号常量、温度依赖这三类错里有两类不报错。 脚本值得写的是「材料本身要算出来」的场合;只是把定好的表建进模型,用文件更省事。

在线材料库需要 Abaqus 2023 或更高版本;2022 及更早只能用本地 JSON 材料库。