Abaqus 二次开发教程(7):材料、截面与单位制——没有单位对象,只有你必须自己守住的量纲链
版本声明块
- 工具/软件:Abaqus 2026(2026 FD01),内嵌 Python 3.10.5(2024 GA 起;此前为 Python 2.7.15,旧版本)
- 语言/环境:Python + Abaqus Scripting Interface(ASI),
Material与Section挂在 Model 仓库上;脚本可运行于 Abaqus/CAE 或abaqus cae noGUI=(第 1 篇)- 本文目标:把“单位”从口头约定变成脚本里可执行的校验,并给出三条可数判据
一句话结论:ASI 的模型层没有单位系统(unit system)成员,单位是脚本作者与求解器之间的隐式契约——mm–N–s 下钢的弹性模量写作 210000、密度写作 7.85e-9(即 N·s²/mm⁴),m–kg–s 下同一物理量是 2.1e11 与 7850;用杆件波速sqrt(E/ρ)校验时两套单位必须给出同一个 5172 m/s,两套结果不一致即为【铁律】第 1 条被违反。
〇、本篇要解决的认知问题
- ASI 里为什么没有“单位”这个对象?单位一致性到底由谁保证、出错时求解器为什么不报错?
- mm–N–s 与 m–kg–s 两套单位制里,弹性模量、密度、能量、压强四类量的数值如何换算?为什么密度最易错?
- 密度(
Density)漏写在静力学里为什么不报错,在动力学、显式求解或重力载荷下为什么一定给错误结果? Section为什么必须指派到有名字的 Region?material=与sectionName=存的是字符串引用,这带来了什么风险?- 材料(
Material)与截面(Section)分成两个仓库的设计,为什么会产出“材料存在但区域缺截面”这类提交错误?
一、机制解析
1.1 为什么 ASI 没有单位对象
脚本层 内核层 求解器层 E = 210000 (纯数值) ──→ 存储的是数值 ──→ 把数值当作“某套单位下的量” ↑ 没有任何字段记录“这是 N/mm² 还是 Pa”这是本系列最反直觉的一条机制:模型数据库里没有单位系统成员(第 5 篇的对象树里没有unitSystem)。求解器读输入文件时,它不知道你的 210000 是 N/mm² 还是 Pa——它只是把它当数值参与计算。单位一致性因此不是软件保证的属性,而是作者与求解器之间的隐式契约。
为什么这对你重要:隐式契约的失效是“静默的”。单位混用不会报语法错,它只会给出差 1000 倍或差 10⁶ 倍的位移。脚本可复现的前提,是把这份契约写成可执行的校验(第 2 节)。
过渡句:既然没有单位对象,就要找一种“量纲自洽”的自检方式。最可靠的方式不是查表,而是用物理量反算——质量与波速是两把尺子。
1.2 两套单位制对照表(本系列统一用第一套)
| 物理量 | 量纲关系(mm–N–s) | mm–N–s 取值(钢) | m–kg–s 取值(钢) | 常见错写 |
|---|---|---|---|---|
| 长度 | mm | 100 | 0.1 | 把 m 当 mm 输入,差 1000 倍 |
| 力 | N | 100 | 100 | kN 与 N 混用,差 1000 倍 |
| 压强 / 模量 | N/mm²(= MPa) | 210000 | 2.1e11 | MPa 配 m,差 10⁶ 倍 |
| 密度 | N·s²/mm⁴(= Mg/mm³) | 7.85e-9 | 7850 | 直接写 7850 进 mm–N–s |
| 质量 | N·s²/mm²(= Mg) | 7.85e-5(10000 mm³ × 7.85e-9) | 0.0785 | 由密度×体积反算,最容易验证 |
| 时间 | s | 1 | 1 | ms 与 s 混用,差 1000 倍 |
【铁律】第 1 条:单位必须整套显式一致(mm–N–s 或 m–kg–s 全链一致),脚本、输入文件、后处理不得混用。这里的关键词是“整套”:只要有一项落在另一套里,所有派生量(能量、密度、波速、阻尼)都会连带错。
过渡句:表里最容易被忽略的是密度,因为它是唯一“需要换算而不是直接照抄”的量。
1.3 密度为什么是单位制的哨兵
密度出现在质量矩阵里,不出现在静刚度里。这带来一个危险的不对称:
- 静力学(Abaqus/Standard 静力分析步):求解
K·u = F,质量矩阵不参与,密度漏写不会报错、不影响结果; - 动力学、显式求解(Abaqus/Explicit)、重力或惯性载荷:质量来自密度 × 体积,密度缺失即质量缺失,结果立刻错;
- 显式求解的稳定增量步长(stable time increment)与单元声波速度相关,而波速
c = sqrt(E/ρ)同时依赖 E 与 ρ——密度错 10⁶ 倍,步长与用时都会跟着错。【经验法则】用sqrt(E/ρ)与真实材料的波速对照,是最便宜的跨单位自检:钢约 5172 m/s(一维杆件),两套单位必须给出同一个数。
【最佳实践】即使做静力学,也要把Density显式写进脚本:它是脚本在未来被复用于动力学时的唯一保护。
过渡句:单位讲完,回到结构层——材料与截面为什么是两个仓库,这决定了提交时的自检方式。
1.4 材料、截面、区域:三层仓库与一次指派
model.materials['Steel'] ← 定义“什么材料”(E、ν、密度),可被多个截面引用 │ 引用(字符串 material='Steel') ▼ model.sections['Section'] ← 定义“多少材料”(厚度/面积/几何),不含本构 │ 引用(字符串 sectionName='Section') ▼ part.sectionAssignments['Section-1'] ← 把截面指派到 Region(必须是有名字的仓库项) │ 引用(region 指向 Set) ▼ part.sets['AllCells'] ← 引用层:有名字才可复现(第 5 篇)这条链的三个设计后果:
- 引用是字符串不是对象:
HomogeneousSolidSection(material='Steel')存的是名字。改名(changeKey)后引用不会跟着变(第 5 篇的命名陷阱),提交时才会在输入文件里对不上。 - 指派必须落在有名字的 Set 上:几何数组元素无名,无法成为可复现的指派对象。【最佳实践】先
Set、再SectionAssignment。 - 提交前的自检接口是
queryRegionsMissingSections():它列出“有网格但没有截面的区域”。这是脚本层最容易写成判定标准的接口——“缺失区域数必须为 0”。
材料定义的形式也值得单独说明:ASI 的材料数据以表格(table)形式传入,如Elastic(table=((E, nu), ))、Density(name='Density', table=((rho, ), ))。表里只写数值,不写单位标签;这也再次印证 1.1 节——单位只存在于脚本作者的注释里。各向异性、塑性等更复杂的表结构与参数名以官方文档为准。
过渡句:下面三段代码把这份隐式契约写成可执行的判定——第一段是单位一致性校验脚本(本篇主交付物),第二段是质量反算与波速对照,第三段是把单位表写进脚本头部的可复用写法。
二、完整代码与逐行剖析
2.1 单位一致性校验脚本(本篇主交付物)
# unit_check.py —— 三条硬判据:量纲自洽、质量反算、波速对照(mm-N-s)fromabaqusimportmdb,session# session 在 noGUI 下不可用(【铁律】第 8 条)fromabaqusConstantsimport*importjsonimportmathimportsys MODEL='Cantilever'UNIT_SYSTEM='mm-N-s'# 单位表:单位只存在于注释与这张表里,内核不记录(1.1 节)UNITS={'length':1.0,# mm'force':1.0,# N'stress':1.0,# N/mm^2(MPa)'density':1.0,# N*s^2/mm^4'mass_derived':1.0,# N*s^2/mm^2(= force / acceleration)}# 钢的真实参数(mm-N-s):E=210000 N/mm^2, rho=7.85e-9 N*s^2/mm^4E_MM=210000.0RHO_MM=7.85e-9# 同一物理量在 m-kg-s:E=2.1e11 Pa, rho=7850 kg/m^3E_SI=2.1e11RHO_SI=7850.0defbar_wave_speed(E,rho):"""一维杆件波速 c=sqrt(E/rho):两套单位必须给出同一个物理值(m/s)。"""returnmath.sqrt(E/rho)defcheck_model(model):problems=[]format_nameinsorted(model.materials.keys()):mat=model.materials[mat_name]# 判据 A:材料必须有密度——静力学不报错,动力学必错(1.3 节)ifnotgetattr(mat,'density',None):problems.append('材料 %s 缺少 Density(【铁律】第 1 条的常见违反)'%mat_name)# 判据 B:每个截面引用的材料名必须在材料仓库里(引用是字符串)forsec_nameinsorted(model.sections.keys()):sec=model.sections[sec_name]ifgetattr(sec,'material',None)notinmodel.materials:problems.append('截面 %s 引用了不存在的材料 %s'%(sec_name,getattr(sec,'material',None)))# 判据 C:有网格的区域必须有截面指派forpart_nameinsorted(model.parts.keys()):part=model.parts[part_name]iflen(part.elements)>0:missing=part.queryRegionsMissingSections()ifmissingisnotNoneandlen(missing)>0:problems.append('Part %s 有 %d 个区域缺截面'%(part_name,len(missing)))# 判据 D:质量反算——体积 × 密度必须等于内核算出的质量(可观察输出)forpart_nameinsorted(model.parts.keys()):part=model.parts[part_name]vol=part.getVolume()rho=0.0forsec_nameinsorted(part.sectionAssignments.keys()):sec=model.sections[sec_name]rho=getattr(model.materials[sec.material],'density',0.0)expected_mass=vol*rho# 不同版本该接口的参数与返回形态可能不同:用 try 包住,形态以官方文档为准try:actual=part.getMassProperties(region=part.sets['AllCells'])print('质量反算:期望 %.6e,内核返回 %s'%(expected_mass,actual))exceptExceptionase:print('质量反算(改用体积×密度):期望 %.6e(接口形态 %s)'%(expected_mass,type(e).__name__))returnproblems model=mdb.models[MODEL]problems=check_model(model)# 物理自检:两套单位必须给出同一个波速;不一致即为混用c1=bar_wave_speed(E_MM,RHO_MM)*0.001# mm/s → m/s(5.172e6 mm/s = 5172 m/s)c2=bar_wave_speed(E_SI,RHO_SI)print('波速校验:mm-N-s = %.1f m/s,m-kg-s = %.1f m/s,差值 = %.2f%%'%(c1,c2,abs(c1-c2)/c2*100.0))print('钢的一维杆件波速经验值约 5172 m/s')report={'unit_system':UNIT_SYSTEM,'problems':problems,'wave_speed_mm_N_s':c1,'wave_speed_m_kg_s':c2}withopen('unit_check_report.json','w',encoding='utf-8')asf:json.dump(report,f,indent=2,ensure_ascii=False,sort_keys=True)total=len(problems)print('问题总数:%d'%total)forlineinproblems:print(' - %s'%line)print('判定:%s'%('通过'iftotal==0andabs(c1-c2)<1.0else'单位不自洽或缺密度'))sys.exit(0iftotal==0andabs(c1-c2)<1.0else1)逐行看关键决策点:
getattr(mat, 'density', None):材料成员在不同版本里形态可能不同,校验脚本必须容错;判据本身(“必须有密度”)不依赖具体接口。- 判据 C 与第 5 篇的规则 1 同源,但这里加了一个前提:“有网格才检查”。无网格的 part 不会有缺失区域,判据会误报。
c1与c2是同一物理量的两种单位表达;判定用“差值 < 1%”而不是“数值相等”,因为浮点与单位换算会引入微小偏差。这条是【经验法则】级的可数判据。- 文件写入显式
encoding='utf-8':Python 3 的open()默认编码依赖系统 locale(第 4 篇),报告脚本若不做这件事,两次运行结果不可比。
2.2 完整可运行脚本:材料 → 截面 → 指派 → 自检
# material_section_pipeline.py —— 三层仓库的最小可复现写法(mm-N-s)fromabaqusimportmdb,sessionfromabaqusConstantsimport*# 【铁律】第 3 条:符号常量不可省略importregionToolsetimportmathimportsys MODEL='SteelBlock'L,H,W=100.0,10.0,10.0E=210000.0# N/mm^2NU=0.3RHO=7.85e-9# N*s^2/mm^4 ——【铁律】第 1 条:这套里最易错的一项model=mdb.Model(name=MODEL)sketch=model.ConstrainedSketch(name='profile',sheetSize=200.0)sketch.rectangle(point1=(0.0,0.0),point2=(W,H))part=model.Part(name='Block',dimensionality=THREE_D,type=DEFORMABLE_BODY)part.BaseExtrusion(sketch=sketch,depth=L)part.regenerate()model.Material(name='Steel')model.materials['Steel'].Elastic(table=((E,NU),))# 显式写密度:静力学不报错,动力学/显式必错(1.3 节)model.materials['Steel'].Density(name='Density',table=((RHO,),))# 截面只描述“多少材料”,不含本构:引用材料名(字符串)model.HomogeneousSolidSection(name='Section-Steel',material='Steel',thickness=1.0)# 指派必须落在有名字的仓库项上(第 5 篇的引用层)part.Set(name='AllCells',cells=part.cells)part.SectionAssignment(region=regionToolset.Region(cells=part.cells),sectionName='Section-Steel')part.seedPart(size=2.5,deviationFactor=0.1)part.setMeshControls(regions=part.cells,elemShape=HEX,technique=STRUCTURED)part.generateMesh()# 可数判定 1:几何与质量vol=part.getVolume()mass=vol*RHOprint('体积 = %.2f mm^3,反算质量 = %.6e(N*s^2/mm^2,即 Mg)'%(vol,mass))print('判定 1:体积必须为 10000.00,反算质量必须为 7.85e-5')# 可数判定 2:缺截面区域数必须为 0missing=part.queryRegionsMissingSections()print('判定 2:缺截面区域数 = %s'%(len(missing)ifmissingisnotNoneelse'n/a'))# 可数判定 3:波速对照(两套单位必须给出同一个物理值)c_mm=math.sqrt(E/RHO)*0.001# mm/s → m/sc_si=math.sqrt(2.1e11/7850.0)print('判定 3:波速 %.1f m/s(mm-N-s) vs %.1f m/s(m-kg-s)'%(c_mm,c_si))ok=abs(vol-10000.0)<1e-6andabs(mass-7.85e-5)<1e-9andabs(c_mm-c_si)<1.0print('结论:%s'%('通过'ifokelse'单位不自洽'))sys.exit(0ifokelse1)要点:
sectionName='Section-Steel'与material='Steel'都是字符串引用;脚本改名后引用不会自动更新,这类错误在提交阶段才暴露——【最佳实践】把材料名与截面名作为常量集中定义,不在调用处硬编码。- 判定 1 与判定 3 是两条独立尺子:体积×密度是代数校验,波速是物理校验。两条都通过,才说明“整套”单位自洽(【铁律】第 1 条)。
- 本脚本的 E 用 210000 N/mm²(钢的真实模量)。第 3 篇演示用的 E=21000 N/mm² 是低模量取值,用于让理论挠度数字好看;两者不要混用,否则波速校验会给出 517 m/s 而非 5172 m/s。
2.3 把单位表写进脚本头部:可复用的最小约定
# units.py —— 单位契约集中定义(被第 12 篇参数化脚本 import)importmath# 【最佳实践】单位契约写在一处,且用“派生量”验证,而不是靠注释UNIT_SYSTEM='mm-N-s'# 主量:显式声明(这四项定了整套单位制)LENGTH='mm'FORCE='N'TIME='s'MASS='N*s^2/mm^2'# 由 F = m*a 反推:m = F / (L/T^2)# 派生量:不允许直接照抄 SI 数值DENSITY_FACTOR=1.0# N*s^2/mm^4;若用 kg/m^3 需乘 1e-9STRESS_FACTOR=1.0# N/mm^2(= MPa)defdensity_from_si(rho_si,target=UNIT_SYSTEM):"""kg/m^3 → N*s^2/mm^4:7850 → 7.85e-9。这是最容易出错的一次换算。"""iftarget!=UNIT_SYSTEM:raiseValueError('本函数只用于 mm-N-s')returnrho_si*1.0e-9defstress_from_si(pa,target=UNIT_SYSTEM):"""Pa → N/mm^2:2.1e11 → 210000。"""iftarget!=UNIT_SYSTEM:raiseValueError('本函数只用于 mm-N-s')returnpa*1.0e-6if__name__=='__main__':# 判定标准:两个换算结果必须分别等于 7.85e-9 与 210000,误差 < 1e-9 相对值d=density_from_si(7850.0)s=stress_from_si(2.1e11)print('密度换算:7850 kg/m^3 → %.3e'%d)print('模量换算:2.1e11 Pa → %.1f N/mm^2'%s)print('波速一致性:%.1f m/s'%(math.sqrt(s/d)*0.001))ok=abs(d-7.85e-9)/7.85e-9<1e-9andabs(s-210000.0)/210000.0<1e-9print('判定:%s'%('通过'ifokelse'换算系数错误'))raiseSystemExit(0ifokelse1)这段在普通 Python 3.10 下可独立运行(不需要 Abaqus 环境),因为它只做纯数学换算——这正是它适合做 CI 门禁的原因:单位契约错误能在建模之前被拦住,而不是在结果对不上之后被倒推。
三、常见报错与排查
现象:结果与理论差 1000 倍或 10⁶ 倍,但没有任何报错。
根因:单位混用——E 用 MPa 数值而力用 kN,或密度用 g/cm³、kg/m³ 直接照抄进 mm–N–s(【铁律】第 1 条)。
解法:跑 2.1 的三条判据;用体积 × 密度反算质量并与内核值对照;用sqrt(E/ρ)波速与物理经验值(钢约 5172 m/s)对照。两套单位给出不同物理值即为混用。现象:提交时报“区域缺少截面”或输入文件里出现未指派网格。
根因:先generateMesh()后SectionAssignment,或指派作用在没有名字的几何数组元素上。
解法:顺序改为 Material → Section → Set → SectionAssignment → generateMesh;用queryRegionsMissingSections()打印缺失区域数,判定标准是 0。现象:显式分析(Abaqus/Explicit)跑得异常快或异常慢,增量步长不合理。
根因:密度单位错导致质量错,进而波速与稳定增量步长错(1.3 节)。
解法:打印波速并与 5172 m/s 对照;若差异接近 1000 倍或 10⁶ 倍,即为密度或模量落在了另一套单位里。现象:
KeyError: 'Steel'或截面材料引用失败。
根因:HomogeneousSolidSection(material='Steel')存的是字符串引用,材料被changeKey改名后不会同步(第 5 篇)。
解法:材料名、截面名集中定义成常量;校验脚本跑判据 B(截面引用的材料名必须存在于材料仓库)。现象:静力学脚本迁到动力学后结果突变,但脚本逻辑没变。
根因:原脚本漏写Density;静力学不使用质量矩阵,动力学使用,缺失即质量缺失。
解法:把Density写进模板脚本并在 CI 里判“每个材料必须有密度”;把判定写进退出码。
四、动手练习
单位一致性校验:在一个故意把密度写成 7850(而非 7.85e-9)的模型上运行 2.1。
判定标准:unit_check_report.json必须含 1 条“缺密度/引用错误”问题、退出码为 1,且波速差值 > 1%。把密度改回 7.85e-9 后重跑,问题数必须降为 0、退出码为 0。质量反算对照:运行 2.2,把种子从 2.5 mm 改成 1.0 mm 再跑一次。
判定标准:两次的体积都必须为 10000.00 mm³、反算质量都为 7.85e-5;网格变化不得改变质量(质量只依赖体积与密度)。若两次质量不同,说明密度被写进了错误的量纲或截面指派未生效。换算函数验证(思考题,附验证要点):解释为什么
density_from_si(7850)必须给出 7.85e-9 而不是 7850。
验证要点清单:① 运行 2.3 打印两个换算值,必须分别为 7.85e-9 与 210000;② 用math.sqrt(s/d)*0.001打印波速,必须落在 5172 m/s 的 ±1% 内;③ 故意把DENSITY_FACTOR改成 1e-6 重跑,波速必须偏离 5172 m/s 超过 100 倍,证明校验有效。三条都要有输出记录。
五、小结与下一篇预告
本篇的机制核心是“没有单位对象”:ASI 只存数值,单位是作者与求解器的隐式契约,因此失效是静默的——它不报错,只给出差 1000 倍或 10⁶ 倍的位移。三条尺子让契约可执行:量纲自洽(密度必须有值)、代数反算(体积 × 密度 = 质量)、物理对照(sqrt(E/ρ)在两套单位下必须给出同一个 5172 m/s)。三层仓库的设计(材料 → 截面 → 区域)解释了为什么“材料存在但区域缺截面”是提交期错误,而queryRegionsMissingSections()是脚本层最便宜的自检。第 8 篇讨论分析步与输出请求:它决定这些量在哪个步里生效、以及后处理能读到什么。第 9 篇把 ODB 里的变量导出成 CSV,届时单位错误会以“数值差 1000 倍”的形式在导出表里现形。
本篇认知问题回显(FAQ)
Q1:Abaqus Scripting Interface 里为什么没有单位系统(unit system)成员?
A:ASI 的模型层只存数值,不记录单位标签;单位是脚本作者与求解器之间的隐式契约,因此错用不会报错,只给出错误数值。
Q2:mm–N–s 与 m–kg–s 两套单位制下钢的弹性模量和密度分别是多少?
A:mm–N–s 是 E=210000(N/mm²)、ρ=7.85e-9(N·s²/mm⁴);m–kg–s 是 E=2.1e11(Pa)、ρ=7850(kg/m³)。
Q3:为什么 Abaqus 脚本漏写Density在静力学里不报错,在显式求解里一定错?
A:静力分析步不使用质量矩阵;动力学与显式求解的质量来自密度 × 体积,且稳定增量步长依赖sqrt(E/ρ),密度缺失即质量缺失。
Q4:SectionAssignment为什么必须指派到有名字的Set,而不是几何数组元素?
A:几何数组元素没有名字、不可复现;截面指派必须引用登记进仓库的命名区域,否则换一次会话就指向别的实体。
Q5:用什么可执行判据检查 Abaqus 脚本的单位是否整套一致?
A:三条:每个材料必须有密度、体积 × 密度反算质量必须与内核值一致、sqrt(E/ρ)在两套单位下必须给出同一个物理值(钢约 5172 m/s)。