UMAT里写Puck准则这件事,我前前后后折腾了小半年才敢说真正跑通了。网上关于Puck的论文一堆,但百分之八十都停在公式推导阶段,真能把FORTRAN代码跑进Abaqus、还经得起实验对比的教程少得可怜。这篇东西不打算复读教科书,就讲清楚三件事:为什么Hashin不够用、Puck公式里那些看似吓人的项到底在干嘛、以及UMAT里最容易卡死人的那几个坑到底怎么绕过去。
1. 从Hashin到Puck:基体失效预测的精度差距在哪
1.1 Hashin准则为什么不够用
如果你只是做定性分析,Abaqus内置的Hashin准则确实方便,选一下材料参数、勾几个失效模式就完事。但只要你拿它跟实验数据对过一次,尤其是压缩载荷下的开孔层合板,很容易发现一个问题:Hashin算出来的失效载荷往往偏高,而且给不出任何关于裂纹面方向的信息。
问题出在Hashin把失效判断做得太“粗糙”了。它对基体拉伸失效只检查横向应力σ2和面内剪切τ12的组合,对压缩时基体在压应力下表现出的“抗剪增强”效应几乎没有刻画。实际上一块单向板在横向压缩加剪切载荷下,基体的失效载荷明显高于纯剪切,这个现象在物理上非常明确,但Hashin用一个简单的二次式表达不了。
更麻烦的是Hashin不区分骨架平面上的剪切分量。复合材料的基体裂纹通常在一个特定的断裂面上萌生,这个面跟厚度方向有夹角,断裂面上的剪应力分布跟材料主方向上的剪应力是完全不同的两回事。如果你用σ2和τ12去判断,本质上是假设裂纹永远沿着0°方向走,这跟实验照片里动不动就见到的斜裂纹完全对不上号。
1.2 Puck准则的革命性思路:作用面应力与断裂面角度
Puck准则之所以能成为航空复材失效分析的主流选择之一,核心在于它把思路从“应力分量组合”转到了“找最危险的物理断裂面”。它首先把失效分为两大类:纤维失效(FF, Fiber Failure)和基体失效(IFF, Inter-Fiber Failure)。纤维失效由纵向应力σ1主导,这个判断跟Hashin差不多。但基体失效就没有那么简单了。
Puck认为,基体裂纹是在某个空间截面(作用面)上由该面上的正应力和剪应力共同驱动的。你的工作不是直接拿σ2、τ12这些主方向应力去套公式,而是要把应力张量旋转到无数个候选断裂面上,算出每一个候选面上的法向应力σn和两个剪应力分量τnl、τnt,再判断哪个面最先达到失效条件。
这个概念是整个Puck准则的地基。它解释了为什么纯横向压缩下基体的破坏面不是垂直于载荷方向的0°面,而是跟载荷方向成54°左右的斜截面——因为在那个角度上,法向压应力对裂纹的“夹紧”效应和剪应力对裂纹的“驱动”效应之间达到了最危险的平衡。
Puck准则的另一个高明之处在于引入了“摩擦效应”。断裂面上的法向压应力不是中性的,它通过摩擦增大了剪切滑移的阻力,也就是说压应力会“保护”基体,拉应力则会“放大”剪切的破坏作用。这个机制用一对斜率参数p21和p23定量描述,后面UMAT里你会反复跟它们打交道。
2. Puck准则失效公式的严谨拆解
2.1 纤维失效(FF):一条简单的应力比线
纤维失效在Puck准则里反而是简单得让人怀疑是不是漏了什么。拉伸状态下:
f_E(FF) = σ1 / X_T
压缩状态下:
f_E(FF) = -σ1 / X_C
没了,就这个。当然也有考虑纤维压缩时的微观屈曲版本,加了纤维间的剪切效应修正,比如用τ12参与的二次式。但对绝大多数工程实践,上面这个简单比值已经足够准确。
原因在于单向复合材料里纤维承担了绝大部分纵向载荷,纤维方向的失效本质上就是应力达到强度极限的事件,基本不存在其他应力分量的耦合。所以即便Umat实现时你顺手把τ12加进纤维压缩判断里,结果也几乎不会变,反而徒增参数标定的负担。
2.2 基体失效(IFF):作用面上的“有效应力”
基体失效的公式才是Puck准则的主战场。在任意一个候选断裂面上,你需要三个应力分量:法向应力σn、面内剪应力τnl、面外剪应力τnt。它们由材料主方向的应力分量通过坐标旋转得到,这里要清楚一点:不是随便选角度的旋转,而是绕纤维方向(1方向)旋转,因为基体裂纹只能沿着纤维方向扩展,这是单向板的物理约束。
旋转角θ从0°扫到180°,每个角度对应一个候选断裂面。假设旋转后作用面法线与2轴夹角为θ,坐标转换公式是这样的:
σn = σ2 · cos²θ + σ3 · sin²θ + 2τ23 · sinθ · cosθ
τnl = τ12 · cosθ + τ13 · sinθ
τnt = (σ3 - σ2) · sinθ · cosθ + τ23 · (cos²θ - sin²θ)
得到这三个量之后,分两种工况判断。
当σn ≥ 0(断裂面上受拉),用拉伸模式:
f_E(IFF) = sqrt[ (τnt / S21)² + (τnl / S21)² + (σn / Y_T · (1 - p21 · Y_T / S21))² ] + (p21 / S21) · σn
当σn < 0(断裂面上受压),用压缩模式:
f_E(IFF) = (p23 / S23) · σn + sqrt[ (τnt / S23)² + (τnl / S21)² + (p23 / S23 · σn)² ]
注意压缩模式里的p23跟S23组合,它表示的是压应力对剪切强度的提升系数。并且σn是负数,所以(p23/S23)·σn是一个负的摩擦贡献,会让整个失效指数下降,反映的就是“压力夹紧了裂纹面”。
2.3 失效指数fE的工程解读
失效指数fE不是一个简单的0/1开关,它是一个连续量。fE小于1表示安全,等于1表示临界失效,大于1表示已经进入过度失效状态。这个性质在工程上有很大价值,你可以直接用fE当安全裕度指标,也可以用它驱动渐进损伤的演化方程。
我在实际项目里习惯把fE的最大值以及对应的断裂角θfp存进STATEV,这样后处理时能看到每个积分点的“裕度云图”和“断裂面方向云图”,对判断失效模式非常直观。而且fE跟载荷是近似线性关系的,这给外推安全系数提供了便利,比一些输出应力比值的准则好用得多。
3. UMAT实现架构:从理论公式到Abaqus可调用的FORTRAN代码
3.1 UMAT接口与变量约定
写UMAT首先得跟Abaqus的接口对上号。核心子程序声明里,你要操作的几个关键数组是:
- STRESS:当前积分点的应力张量,进入子程序时是增量步开始时的值,返回时必须是更新后的值
- DDSDDE:雅可比矩阵,即∂Δσ/∂Δε,Abaqus用它组建整体刚度矩阵
- STATEV:状态变量数组,自己定义用途
- PROPS:材料参数数组,在inp里通过*USER MATERIAL传入
- STRAN / DSTRAN:总应变和应变增量
- NDI / NSHR:正应力分量个数和剪应力分量个数,这俩决定了你是在处理壳单元还是体单元
有一个新手很容易绕晕的点:UMAT里收到的应力应变是在材料坐标系下的。对于壳单元和连续壳单元,Abaqus会自动把结果旋转到铺层坐标系,你不需要额外做旋转。但如果你用体单元模拟结构件,就必须自己通过ORIENT定义材料方向,否则算出来的东西完全没有物理意义。
3.2 Puck准则判断流程的代码框架
UMAT的主流程可以分成五步,我写了一个固定模板,每次换材料只改参数部分:
第一步,根据应变增量计算试探应力。用未退化的弹性刚度矩阵,Δσ = C · Δε。
第二步,从试探应力里提取纵向应力σ1和横向分量σ2、σ3、τ12、τ23、τ13。先算纤维失效指数。如果f_E(FF) ≥ 1,记录纤维损伤标志。
第三步,扫描断裂角θ。从0°到180°,步长我通常取1°。每个角度做坐标旋转,得到σn、τnl、τnt,判断σn符号后套对应的IFF公式,记录最大失效指数f_E_max和对应角度θ_fp。
第四步,判断损伤。如果最大失效指数超过1,根据是纤维失效还是基体失效,给对应的损伤变量赋值。
第五步,用损伤后的刚度重新计算应力,更新雅可比矩阵,把fE_max、断裂角、损伤变量写进STATEV。
这里有个关键决定:扫描步长。取1°会带来180次三角函数运算,对单点积分来说压力不大,对隐式分析的大模型就有点吃力。但步长取5°又可能漏掉真正的峰值角度,尤其在纯压缩工况下,失效指数对角度的敏感性很高。我测试下来的折中方案是:粗扫5°找到大致峰值区间,再在峰值附近1°精扫。这个优化能让单次UMAT调用省掉大约四成计算量。
3.3 损伤演化与应力更新的关键决定
Puck准则只告诉你“什么时候失效”,没告诉你“失效后怎么退化”。这个你必须自己定。工程上最常见的是两种做法。
第一种是瞬时退化。一旦某个积分点失效指数超过1,直接把对应方向的刚度乘一个很小的系数,比如0.01到0.05。这个做法的好处是简单、稳定,收敛性好,坏处是对单元尺寸敏感,计算结果有一定网格依赖性。但如果你做的是破坏起始分析,只想找到第一个失效点,瞬时退化完全够用。
第二种是渐进退化。基于断裂能的线性软化或指数软化,让损伤变量从0平滑过渡到1。这个做法物理上更真实,能量耗散是固定的,网格敏感性小得多。代价是要引入特征长度Lc,对体单元通常取积分点体积的立方根,对壳单元取面内特征尺寸。公式上可以用:
d = 1 - exp( -A · (f_E - 1) )
其中A是需要标定的损伤演化参数,或者说跟断裂能相关的衰减率。
我个人建议新手上来先写瞬时退化版本,跑通全部验证后再升级到渐进退化。因为渐进退化版本一旦跟接触、大变形、多方向层合板耦合在一起,收敛调试的难度会呈几何级数上涨。
3.4 雅可比矩阵DDSDDE的处理策略
UMAT里最容易“翻车”的不是失效判断,而是DDSDDE给错了。Abaqus的隐式求解器对雅可比矩阵的要求非常高,你给一个近似值顶多拖慢收敛,给一个符号错误的值直接导致整体刚度矩阵不正定,报错一堆Negative eigenvalue。
对于未损伤的弹性状态,DDSDDE直接取面内刚度矩阵就行。损伤发生之后,严格的做法是对降阶后的刚度矩阵求一致性切线,这在渐进退化下推导比较繁琐。一个工程上广泛接受的做法是:损伤后继续使用弹性雅可比矩阵,只是应力更新用了退化刚度。这样做物理上不完全严格,但很多公开发表的论文都这么处理,实测收敛性也不会太差。
如果你想把雅可比也退化,最简单的方式是DDSDDE = (1 - d) · C_original,注意这里的d要取所有损伤模式里最大的那个,并且要保证雅可比正定。我踩过一次坑:对基体损伤只折减了面外分量,结果局部刚度矩阵出现零主元,导致单元畸变,后来统一折减全部分量反而没问题。所以往往“数学上不那么精确”的处理在数值上反而更鲁棒。
4. 参数校准与断裂角:最容易翻车的地方
4.1 斜率参数p的工程取值
斜率参数p21和p23是Puck准则里最容易被忽视、也最容易拍脑袋的参数。p21描述断裂面上法向拉应力对剪切强度的“放大”作用,p23描述法向压应力对剪切强度的“抑制”作用。它们不是一个自由拟合参数,而是有明确物理意义的:如果你有不同压应力水平下的剪切强度实验数据,把这些点画在σn-τ平面上,包络线在σn=0处的斜率就是对应的p值。
但工程现实是,多数项目根本没有经费去测不同压应力下的剪切强度。这时候可以引用Puck在论文里给出的推荐值:对于碳纤维环氧体系,p21大约在0.3到0.35之间,p23在0.25到0.30之间;玻璃纤维体系会高一些,p21可以到0.4左右。这些推荐值覆盖了绝大多数工业复材体系,精度足够用于设计分析。
我做过一组敏感性测试,把p21从0.3改到0.4,失效载荷变化大概在5%以内;但如果你把S23的取值搞错,那误差就是20%起步了。所以给新手的建议是:先保证S23准确,p值用推荐值完全OK。
4.2 断裂角θfp怎么确定
断裂角是Puck准则区别于其他准则最显著的输出,也是验证参数合理性最直观的标尺。单向板在纯横向拉伸下,断裂面几乎垂直于载荷方向,θfp接近0°。而在纯横向压缩下,理论预测的断裂角是54°左右,这个数字在实验观察里非常稳定,几乎是碳纤维环氧体系的指纹特征。
如果你的UMAT在纯横向压缩下单点测试扫出来的最大失效指数角度不是50°~55°之间,那说明参数组有问题。最常见的原因是S23取错了——S23偏大会让预测的断裂角偏小,偏小则会超出60°。这里有个快速校验公式(简化形式):
sin(2θfp) ≈ - (τ23 / |σ2|) · (S23 / ... )
虽然工程上不用背这个公式,但你要理解它背后的关系:压缩断裂角与横向压缩强度跟剪切强度的比值直接关联。横向压缩强度Y_C相对剪切强度S23越大,断裂角越接近55°;如果Y_C/S23偏小,角度会明显减小。所以当你拿到一组材料参数后,先不算任何载荷工况,直接手算一下这个比值,心里就有数了。
4.3 单层板vs层合板验证案例设计
UMAT写完后,别急着上大模型,先跑三个规模的验证:
第一层验证是单单元单层板。分别施加横向拉伸、横向压缩、面内剪切三种载荷,看失效指数和断裂角是否跟理论一致。这一步能抓出坐标旋转、公式符号这类低级错误。
第二层验证是多单元单层板。做一块带中心圆孔的0°单向板拉伸,观察损伤萌生位置和扩展路径。孔边应力集中区应该最先出现基体损伤,损伤连接成一条沿纤维方向的裂纹带。
第三层验证是层合板。典型算例是[45/-45]s层合板拉伸,由于层间应力耦合,试验值跟单向板有显著差异。这里可以对比你的UMAT预测失效载荷跟公开实验数据,误差在10%以内算是很健康的。
层合板验证最容易暴露的问题是把层间应力忽略了。在Puck准则里,层间正应力σ3对基体失效的贡献是通过σn和τnt通道进入的,如果你用的体单元没把σ3算准,或者壳单元忽略了横向正应力,那层合板边缘的失效起始位置一定会偏。
5. 调试与验证:从单元测试到标准算例的完整流程
5.1 单单元失效测试的具体操作
在Abaqus/CAE里创建一个8节点减缩积分体单元(C3D8R),只需要一个单元,材料方向通过*ORIENTATION定义成沿1轴。载荷分三种:横向拉伸、横向压缩、面内剪切。每次只施加一种,查看STATEV输出。
我强烈建议在STATEV里除了存失效指数和断裂角,再存一个“当前最大历史失效指数”。因为UMAT在增量迭代里会被反复调用,同一积分点的应力可能在弹性加载过程中来回试探,如果只用当前时刻的fE去判断,会出现“这步判断失效、下一步又恢复了”的振荡。历史最大值能保证损伤不可逆,这是物理上必须满足的。
单单元测试通过后,可以做一个小规模的网格敏感性测试:同样一块板,分别用1mm、2mm、5mm网格跑一遍,看失效载荷的差异。瞬时退化版本在这个测试里会显示明显的网格依赖,这不代表你的代码错了,而是瞬时退化的固有特性。如果你要求网格无关解,就得升级到断裂能驱动的渐进退化版本。
5.2 常见错误与报错排查
在这里把我在调试过程中实际遇到过的报错和坑列出来,按出现频率排序。
第一个是“Time increment required is less than the minimum specified”。这个本质上是收敛失败,大多数情况是瞬时退化刚度突变太猛导致的。解决思路有两个:一是把损伤后的剩余刚度系数从0.01提高到0.05,让软化不那么剧烈;二是检查雅可比矩阵是否跟应力更新路径一致。
第二个是“Negative eigenvalue”。这个通常在层合板多损伤模式耦合时出现。我排查过一次,最后发现是DDSDDE里损伤变量对历史最大值做了过深的折减,导致刚度矩阵顺序主子式不满足正定性。修复方式是限制d不能超过0.99,并且统一折减所有刚度分量。
第三个是“Material orientation is not defined”。这个属于建模问题,不是UMAT问题。体单元模型必须在SOLID SECTION里引用ORIENTATION定义的材料方向。我见过有人直接在CAE里建了局部坐标系却忘了关联到截面属性上,跑出来结果全错。
还有个不算报错但很容易误判的问题:壳单元的横向剪切刚度更新。UMAT中壳单元的横向剪应力分量只有两个,NDI=3、NSHR=1还是2取决于单元类型,如果你在UMAT里假设了三维应力状态,可能会越界访问数组。我一般在子程序开头加一个分区判断,按NDI和NSHR的大小给矩阵分配维度,这样平面应力单元和体单元能共用一套代码。
5.3 验证中的“物理合理性”检查清单
数值收敛解决了,不代表结果是对的。我每次跑完一套复材失效分析,都会过脑一遍物理合理性清单:
第一,纤维拉伸失效的点是否在孔边或应力集中处最先出现?如果损伤先出现在远离应力集中的地方,基本可以断定材料方向定义错了。
第二,基体失效后的裂纹带是否沿着纤维方向扩展?单向板的IFF裂纹沿1轴方向,不可能横穿纤维。如果你看到损伤云图沿垂直纤维方向铺开,那坐标旋转公式的某个分量大概率写错了。
第三,纯横向压缩算例的断裂面角度是否落在50°到55°区间?不在就检查S23和p23。
第四,层合板的开孔压缩失效载荷跟实验对比是否在10%以内?如果偏差很大,先检查是否漏了层间应力σ3的影响,再检查纤维压缩失效的模式系数。
这套检查表不复杂,但它能帮你把“代码能跑”和“结果可信”之间的鸿沟填上。我见过不止一个团队,UMAT跑得飞快,但结果错得离谱,最后发现是坐标系旋转换错了轴,这种低级错误靠检查表几分钟就能揪出来。
落实到Puck参数本身,我最后再强调一次:纯横向压缩断裂角是检验你参数组的金标准。不管你是自己做实验标定还是从文献里抄参数,这个角度一旦对不上,后面的层合板分析结果就不用看了。反过来,断裂角对上了,哪怕p值取自推荐范围,你的预测精度也比内置的Hashin准则高出一大截。这是我在多个实际项目里反复验证过的结论。