4. 基于文本模板的K文件参数化生成
在上一节中我们已经看到,LS-DYNA 的 K 文件本质上是一组按固定字段格式排列的文本卡片。只要卡片的关键字和字段位置不变,改变其中的数值并不会破坏文件的可读性。这一特性为“文本模板 + 占位符替换”的参数化生成方式提供了基础:我们不需要调用 LS-PrePost 或任何 CAE 前处理器,仅用一段脚本就能批量产出成百上千个 K 文件。
本节以一个最简单的悬臂梁结构为对象,完成一次完整的参数化建模任务。目标不是把梁建得多复杂,而是把“模板—参数—批量生成—可运行验证”这条链路走通,让后续更复杂的参数化优化有可复用的骨架。
一、为什么选择文本模板而不是脚本建模
LS-DYNA 提供了多种参数化途径:*PARAMETER 关键字、LS-OPT 参数化、Python 调用 LS-PrePost 的 API,以及直接生成文本 K 文件。它们各有适用场景。*PARAMETER 适合在单个 K 文件内部做少量参数联动,但批量生成不同文件时仍需外部驱动;LS-PrePost API 功能强,但依赖图形环境,脚本较重;而文本模板方式最轻量——模板就是一份能正常求解的 K 文件,把需要变化的数字替换成占位符即可。
文本模板方式的另一个优势是“所见即所得”。模板文件本身可以直接提交给 LS-DYNA 求解,确认无误后再做占位符化,出错时也容易定位到具体卡片。对于梁、板、简单装配体这类结构规则、卡片数量可控的模型,这种方式效率极高。
二、悬臂梁模型的模板结构
我们采用的示例对象是一根矩形截面悬臂梁:左端固支,右端自由,自由端承受一个横向集中力。梁长 L、截面宽 b、截面高 h、弹性模量 E、密度 ρ、集中力 F 都是可参数化的量。为了让模型能真正跑起来,还需要定义壳单元或实体单元、节点、部件、材料、截面、边界条件和载荷曲线。
为控制篇幅,这里采用壳单元(*SECTION_SHELL)建模,梁沿长度方向划分 nseg 段,宽度方向划分 nw 段。节点坐标由脚本按规则计算生成,而不是手工填写。模板中保留卡片关键字和字段顺序,把节点坐标、单元连接、厚度、材料参数、载荷幅值等替换为占位符。
下面给出模板文件的核心片段(节选,实际模板需包含完整的 *NODE、*ELEMENT_SHELL、*PART、*SECTION_SHELL、*MAT_ELASTIC、*BOUNDARY_SPC_SET、*LOAD_NODE_SET、*DEFINE_CURVE 和 *CONTROL 卡片):
*KEYWORD
*NODE
{{NODE_BLOCK}}
*ELEMENT_SHELL
{{ELEMENT_BLOCK}}
*PART
cantilever beam
1 1 1
*SECTION_SHELL
1 1 {{THICK}}
*MAT_ELASTIC
1 {{E}} {{NU}} {{RHO}}
*BOUNDARY_SPC_SET
1 0 1 1 1 1 1 1
*LOAD_NODE_SET
2 2 {{F}}
*DEFINE_CURVE
1 0 1.0 1.0
0.0 0.0
1.0 1.0
*CONTROL_TERMINATION
{{TEND}}
*END
可以看到,模板中真正需要替换的只有少数几个占位符:{{NODE_BLOCK}}、{{ELEMENT_BLOCK}}、{{THICK}}、{{E}}、{{NU}}、{{RHO}}、{{F}}、{{TEND}}。其中节点块和单元块是“结构化文本”,由脚本按网格规则生成,其余是标量参数。
三、占位符替换脚本的实现
脚本的核心逻辑分三步:读取模板、计算参数、替换并写出。下面用 Python 给出一个最小可用实现,重点展示节点块和单元块的生成方式。
import os
def gen_nodes(L, b, nseg, nw):
lines = []
nid = 1
for i in range(nseg + 1):
x = L * i / nseg
for j in range(nw + 1):
y = b * j / nw
lines.append(f"{nid:10d}{x:10.4f}{y:10.4f}{0.0:10.4f}")
nid += 1
return "\n".join(lines)
def gen_elements(nseg, nw):
lines = []
eid = 1
for i in range(nseg):
for j in range(nw):
n1 = i * (nw + 1) + j + 1
n2 = n1 + 1
n3 = n2 + (nw + 1)
n4 = n1 + (nw + 1)
lines.append(f"{eid:10d}{n1:10d}{n2:10d}{n3:10d}{n4:10d}")
eid += 1
return "\n".join(lines)
def render(template, params):
text = template
for key, val in params.items():
text = text.replace("{{" + key + "}}", str(val))
return text
if __name__ == "__main__":
with open("beam_template.k", "r", encoding="utf-8") as f:
tpl = f.read()
cases = [
{"L": 200.0, "b": 20.0, "nseg": 20, "nw": 2,
"THICK": 2.0, "E": 210000.0, "NU": 0.3,
"RHO": 7.85e-9, "F": 50.0, "TEND": 0.01},
{"L": 300.0, "b": 20.0, "nseg": 30, "nw": 2,
"THICK": 2.0, "E": 210000.0, "NU": 0.3,
"RHO": 7.85e-9, "F": 50.0, "TEND": 0.01},
]
os.makedirs("cases", exist_ok=True)
for idx, p in enumerate(cases, start=1):
p["NODE_BLOCK"] = gen_nodes(p["L"], p["b"], p["nseg"], p["nw"])
p["ELEMENT_BLOCK"] = gen_elements(p["nseg"], p["nw"])
out = render(tpl, p)
with open(f"cases/beam_{idx:03d}.k", "w", encoding="utf-8") as f:
f.write(out)
print(f"generated cases/beam_{idx:03d}.k")
这段脚本没有使用任何第三方库,替换逻辑就是字符串替换。节点编号从 1 开始,按“先沿长度、后沿宽度”的顺序排列;单元连接按四边形壳单元的逆时针顺序给出。只要模板中的 *ELEMENT_SHELL 卡片字段顺序与生成顺序一致,模型就能被 LS-DYNA 正确读取。
四、参数化生成的数据流与验证闭环
整个流程可以概括为:参数表 → 模板渲染 → 批量 K 文件 → 提交求解 → 结果提取。下面这张图展示了这一数据流,以及每个环节中需要检查的关键点。
图中左侧的“检查点”是实际工作中最容易出问题的地方。节点编号必须连续且与单元连接一致,否则 LS-DYNA 会报“node not defined”;壳单元的法向必须统一,否则在 *BOUNDARY_SPC_SET 和载荷施加时会出现方向混乱。建议在生成后先用 LS-PrePost 打开一个样本文件,检查网格和边界条件是否正确,再批量提交。
五、一个可复用的参数表
为了让批量生成可管理,建议把参数集中写在一张表里,而不是散落在脚本中。下面给出本节示例的参数表,字段和取值都属于教学示例,实际使用时按需修改。
| 参数名 | 含义 | 示例值 | 单位 | 备注 |
|---|---|---|---|---|
| L | 梁长度 | 200 / 300 | mm | 沿 x 方向 |
| b | 截面宽度 | 20 | mm | 沿 y 方向 |
| nseg | 长度方向分段数 | 20 / 30 | — | 决定单元数量 |
| nw | 宽度方向分段数 | 2 | — | 至少为 1 |
| THICK | 壳厚度 | 2.0 | mm | 对应 *SECTION_SHELL |
| E | 弹性模量 | 210000 | MPa | 钢的典型值 |
| NU | 泊松比 | 0.3 | — | — |
| RHO | 密度 | 7.85e-9 | t/mm³ | 与 mm-MPa 单位制匹配 |
| F | 自由端集中力 | 50 | N | 沿 z 方向 |
| TEND | 终止时间 | 0.01 | s | — |
单位制必须统一。上表采用 mm-MPa-t 体系:长度 mm、应力 MPa、密度 t/mm³、力 N、时间 s。如果混用单位,求解结果会出现数量级错误,而且往往不会报错,只是位移或应力明显不合理。这是参数化生成中最隐蔽的坑之一。
六、生成后的检查与常见问题
批量生成完成后,不要直接全部提交。先做三项检查:第一,用文本编辑器打开一个生成文件,确认占位符已全部替换,没有残留的 {{ }};第二,检查 *NODE 和 *ELEMENT_SHELL 的条目数是否与 nseg、nw 计算一致,节点数应为 (nseg+1)×(nw+1),单元数应为 nseg×nw;第三,用 LS-PrePost 打开样本文件,查看网格形状和边界条件位置。
常见问题包括:节点编号重复导致单元连接错误;壳单元法向不一致导致载荷方向异常;*DEFINE_CURVE 的曲线编号与 *LOAD_NODE_SET 引用的编号不匹配;*CONTROL_TERMINATION 的 TEND 过小导致求解未完成。这些问题在模板阶段就可以通过固定卡片顺序和编号规则来规避。