我要提问
ARTICLE DETAIL

资讯详情

前沿编程新知与开发实战干货的深度解读。

HyperView Python二次开发:自动导入模型与结果文件的批处理实战

HyperView Python二次开发:自动导入模型与结果文件的批处理实战 干仿真后处理的工程师很多都有这种经历模型算完只是起点真正耗时间的是打开后处理软件手动把模型文件、结果文件一个个拖进去再调视图、选工况、截屏导出。偶尔看一两个结果还好如果碰上几十个优化迭代方案或批量工况这种重复操作能占掉一整天的节点。HyperView的Python二次开发正好能把“自动导入模型和结果”这个动作固化成脚本让计算机替你做掉所有机械操作。这篇内容面向正在做CAE结果批处理、想提升后处理效率的仿真工程师我会从环境准备讲到完整脚本再给你一些我实际踩过的坑。1. 整体设计把“自动导入模型和结果”拆成可重复的流水线1.1 手工后处理的瓶颈到底在哪很多人觉得后处理不就是在软件里点几下吗实际上一套完整的结果查看流程远不止打开文件那么简单。你要先确定加载哪个模型再挂上对应的结果文件然后选择要看的子工况或时间步切换成云图显示设置图例范围调整视角最后截图或导出数据。单个步骤看起来不复杂但组合起来每一步都在消耗注意力。更重要的是手工操作很难保证一致性。同一个结果今天你截的视角和明天截的视角可能不一样同一个工况不同人选择的子工况索引也可能差一位。结果一旦用来出报告这种不一致轻则返工重则让结论出现偏差。批处理脚本的价值不只是“省时间”更是把所有操作约束到同一套流程里每次输出的图片和数据都可复现。1.2 为什么用 Python 而不是传统 Tcl提到 HyperView 二次开发很多人第一反应是 Tcl。早期脚本确实以 Tcl 为主但现在 Python 接口已经是更稳妥的选择。我的判断依据有几个第一Python 的文件扫描、路径处理、数据导出能力远强于 Tcl处理几十个文件时不用自己造字符串解析的轮子第二团队里会 Python 的人通常比会 Tcl 的人多脚本后续维护成本低第三可以用 pandas、numpy 这类库直接做数据后处理把结果从后处理软件里导出来之后马上进入分析流程。当然Tcl 也不是不能用老项目、老模板里如果已经积累了大量 Tcl 脚本没必要一次性重写。我的建议是新项目优先 Python旧脚本只在需要扩展时逐步迁移。标题里的“自动导入模型和结果”这件事用 Python 写起来逻辑更直观也更容易和其他自动化系统对接。1.3 流水线里最核心的三步整个自动导入流程核心可以压缩成三步。第一步创建后处理会话并初始化页面相当于把空白工作台准备好。第二步把模型文件和结果文件导入会话这一步要处理好模型与结果的关联关系包括子工况的选择规则。第三步针对导入后的视图做统一配置包括云图类型、图例范围、视角和输出要求。把这三步串起来之后剩下的就是套一层循环遍历目录下所有结果文件。这样设计的最大好处是单文件处理逻辑可以独立调试批量循环只是重复调用同一个函数。如果某一个文件出错不会影响前面已经处理好的结果这也是我在工程里最看重的一点。2. 环境准备与基础对象模型2.1 环境准备装好接口跑通第一个脚本开始写代码之前你首先得确认本机安装的 HyperView 版本里带了 Python 二次开发接口。大部分新版 HyperWorks 安装完成后会在安装目录下提供 Python 解释器和对应模块。你可以从开始菜单直接打开也可以在命令行进入安装目录后运行 python 来验证。验证方式很简单在 Python 交互式环境里执行导入语句import hvw print(hvw.__version__)如果模块找不到先检查环境变量 PYTHONPATH 是否指向了 HyperView 的 Python 库目录。有些版本不自动配置环境变量你需要手动把安装目录下的脚本路径加进去。我第一次用的时候就卡在这一步折腾了半天才发现是环境变量没配对。建议你把import验证作为所有脚本的第一步不要直接开始写长逻辑否则报错时很难分清到底是环境问题还是逻辑问题。另外不同版本的接口类名可能有差异我下面的代码以本机环境为参考你实际开发时对照帮助文档里的关键字做同名替换即可。2.2 理解 Session / Page / Window / ClientHyperView 的 Python API 基本都是围绕一套固定的对象模型展开搞明白这套模型脚本就成功了一半。你可以把 Session 想象成一张办公桌它管理着整个后处理会话Page 是桌面上的工作簿Window 是工作簿里的窗口负责显示具体内容Client 是窗口里的一个客户区模型和结果最终加载在 Client 上。实际代码里这四层对象是依次创建的session hvw.HyperView() page session.CreatePage() window page.CreateWindow() client window.AddClient()刚开始接触的时候很容易搞混Window和Client。简单记一句话Window 是画框Client 是画框里真正贴画的位置。一个 Window 可以只挂一个 Client也可以拆分成多个视图区域每个 Client 独立加载模型和结果。你要是默认整个窗口只放一个图那就只会用到其中一个 Client。2.3 模型和结果文件的选择自动导入前文件格式要先确认清楚。HyperView 能处理多种结构分析结果文件比如常见的二进制结果文件以及某些求解器导出的通用结果格式。模型信息有的包含在结果文件里有的则是单独的网格模型文件。这里有一个关键点如果你的结果文件本身已经包含了网格模型那就不需要额外再导入模型文件如果结果文件只是纯结果就必须用模型文件把网格骨架先搭起来。我的经验是在脚本里写一个文件组合规则表比如按扩展名判断结果文件自带模型只调用一次导入接口。模型与结果分离先导入模型文件再导入结果文件。多个结果文件对应同一个模型模型只导入一次循环时不断切换结果文件。这个规则表看起来简单但能避免很多误操作。文件选错后面云图数据全是空的排查起来非常痛苦。3. 自动导入模型和结果的代码实现3.1 创建会话并载入模型下面这段代码演示的是最基本的单文件导入流程包含创建会话、打开页面、添加客户端并导入模型和结果。我在代码里加了异常判断因为批处理脚本里最怕一个文件出错就中断整个流程。import os import hvw def load_model_and_result(model_file, result_file, loadcase_id1): session hvw.HyperView() page session.CreatePage() window page.CreateWindow() client window.AddClient() try: if model_file and os.path.exists(model_file): client.SetModelFile(model_file) client.SetResultFile(result_file) client.SetCurrentLoadcase(loadcase_id) return client except Exception as e: print(fLoad failed: {model_file}, {result_file}, error: {e}) session.Close() return None注意SetModelFile和SetResultFile这两个接口不同版本可能写成OpenModel、PostModelFile、AttachResultFile之类。不用死记关键是理解操作顺序先有模型再挂结果最后设置工况。如果你只关心变形云图不关心应力值模型文件甚至可以省略但大多数标准报告场景还是建议模型和结果都导入齐全。3.2 附加结果文件与选择子工况导入结果文件之后并不是所有结果都会立刻显示。你需要在后处理对象里指定要查看的结果类型和子工况。这一步在手动操作时对应的是图形区下方的下拉框在脚本里则是通过类似SetContourType和SetLoadcase的接口完成。我习惯先打印出当前结果文件里可用的子工况列表再做选择。这样处理的好处是你不容易因为记错工况编号而选错数据。示意代码如下loadcases client.GetLoadcaseList() print(Available loadcases:, loadcases) if loadcase_id not in loadcases: print(Loadcase %s not found, fallback to first one % loadcase_id) loadcase_id loadcases[0] client.SetCurrentLoadcase(loadcase_id) client.SetContourType(VonMises)选择子工况时最常见的坑是编号从 0 开始还是从 1 开始。不同版本可能不一样拿到的列表打印出来看一遍比猜更靠谱。特别是包含多工况、多时间步的瞬态分析文件这一步错一位后面所有数据都没意义。3.3 批量导入目录扫描与容错设计单文件跑通之后批量导入就是简单地把文件循环起来。我会先用os.scandir扫描目录得到所有结果文件列表再按文件名规则排序保证处理顺序稳定。下面是一个直接可用的批量循环逻辑import glob import os result_files sorted(glob.glob(os.path.join(result_dir, *.h3d))) model_file os.path.join(model_dir, base_model.hm) save_dir os.path.join(output_dir, png) os.makedirs(save_dir, exist_okTrue) for idx, result_file in enumerate(result_files): print(f[{idx 1}/{len(result_files)}] Processing {result_file}) client load_model_and_result(model_file, result_file, loadcase_id1) if client is None: continue # 这里可以插入后续的云图设置、截图、数据提取代码 # client.ExportImage(...) client.Close() print(fFinished: {os.path.basename(result_file)})这个循环里我加入了两个关键设计。第一单个文件失败不会导致程序退出错误被load_model_and_result内部拦截后返回 None外层继续处理下一个文件。第二每处理完一个文件就关闭当前 client避免内存越积越高。别小看第二点我以前没加Close处理到第 20 个文件时软件直接卡死。4. 导入后的结果处理与自动出图4.1 读结果数据提取最大位移和应力自动导入模型和结果只是手段读数据才是目的。HyperView 的 Python 接口通常支持查询结果数据比如获取某个 result 类型下的全局最大值、最小值、对应节点或单元编号。你可以利用这一点自动提取每个工况下的最大应力、最大位移然后写进 CSV 汇总表。示意代码思路如下result_data client.GetResultData(VonMises, loadcase_idloadcase_id) max_stress result_data.GetMaximumValue() max_node result_data.GetMaximumEntityId() print(fMax VonMises: {max_stress:.2f} at node {max_node})这里要提醒一句不同版本的数据结构差异很明显有的封装成字典有的是独立对象。你开发时先打印dir(result_data)看看有哪些方法再决定怎么取值。我遇到过不少次接口名变了但官方文档没同步更新的情况打印对象属性比翻文档更快。4.2 云图与视图配置数据提出来后云图才是给人看的。脚本里要做的事情包括设置云图类型、选择显示分量、调整图例范围、关闭或开启网格线、设置视角。否则每次导出的图片可能颜色范围不统一放在报告里很难对比。我习惯把图例范围固定在一个合理值而不是让软件自动调整。自动范围虽然好看但不同文件之间没有可比性。比如批量对比应力我会先扫描前几个文件的最大应力定一个统一上限然后所有图都按这个上限出图。代码上大致是client.SetContourType(VonMises) client.SetLegendVisible(True) client.SetLegendRange(min_value0.0, max_value800.0) client.SetViewOrientation(iso1)“iso1”这种视角名来自软件的预置视角不同版本中命名不一定相同。第一次写的时候我把预置视角列表打印出来看一眼就能知道当前版本支持哪些名字。4.3 导出图片与数据记录云图配好以后出图就很简单了。我通常直接调用导出接口指定文件名和分辨率。为了报告排版方便我会把文件名命名为“工况编号_结果类型_时间步”这种模式后处理之后不需要再手动重命名。image_name fcase_{loadcase_id:03d}_{os.path.basename(result_file)}.png image_path os.path.join(save_dir, image_name) client.ExportImage(png, fileimage_path, width1600, height1200)导出图片的同时我也会准备一份 CSV 汇总把每个文件的路径、最大应力、最大位移、节点编号都记下来。这样图片用来展示表格用来分析两套产物一次生成。CSV 的写入我直接用 Python 的标准库csv不额外引入 pandas 也完全够用。5. 实战一次批量处理的完整案例5.1 案例需求与目录结构假设现在有一个真实的模拟项目 X包含 50 个优化迭代方案。每个方案都输出一个结果文件统一放在E:/simdata/results/目录下。另外有一个公共模型文件base_model.hm放在E:/simdata/model/下。需要做的是对每个方案自动导入模型和结果查看第一子工况下的 Von Mises 应力云图导出 PNG 图片并输出每个方案的最大应力汇总表。目录组织如下E:/simdata/ ├── model/ │ └── base_model.hm ├── results/ │ ├── case_001.h3d │ ├── case_002.h3d │ └── ... └── output/ ├── png/ └── summary.csv这种目录结构我在多个项目里都用整理起来非常顺手。脚本本身不依赖绝对盘符以外的任何资源放在任何一台机器上都能跑只要安装好软件和 Python。5.2 完整脚本骨架把前面几节的代码合并起来就得到一个完整的批量处理脚本。骨架如下import os import glob import csv import hvw MODEL_FILE rE:/simdata/model/base_model.hm RESULT_DIR rE:/simdata/results OUTPUT_DIR rE:/simdata/output LOADCASE_ID 1 LEGEND_MAX 800.0 def process_file(session, result_file, case_name): page session.CreatePage() window page.CreateWindow() client window.AddClient() try: client.SetModelFile(MODEL_FILE) client.SetResultFile(result_file) client.SetCurrentLoadcase(LOADCASE_ID) client.SetContourType(VonMises) client.SetLegendRange(0.0, LEGEND_MAX) max_stress client.GetResultData(VonMises, LOADCASE_ID).GetMaximumValue() image_path os.path.join(OUTPUT_DIR, png, f{case_name}.png) client.ExportImage(png, fileimage_path, width1600, height1200) client.Close() return {case: case_name, max_stress: max_stress, image: image_path} except Exception as e: print(f{case_name} failed: {e}) client.Close() return None def main(): session hvw.HyperView() summary [] result_files sorted(glob.glob(os.path.join(RESULT_DIR, *.h3d))) for result_file in result_files: case_name os.path.splitext(os.path.basename(result_file))[0] row process_file(session, result_file, case_name) if row: summary.append(row) session.Close() with open(os.path.join(OUTPUT_DIR, summary.csv), w, newline) as f: writer csv.DictWriter(f, fieldnames[case, max_stress, image]) writer.writeheader() writer.writerows(summary) if __name__ __main__: main()注意这里我把创建 session 提到了主函数里每个文件共用同一个 session而不是每个文件都新建。这样省去了反复启动和关闭软件的耗时处理 50 个文件时的差异非常明显。如果你遇到 session 不稳定就退回每个文件独立 session以稳定性优先.5.3 运行效果与注意事项按这个脚本跑完以后output/png 下会多出 50 张云图summary.csv 里会有一张 50 行的表格。整个过程耗时取决于文件大小我在实际机器上处理中小型模型时平均每个文件大约 5 到 8 秒50 个文件也就是五到八分钟。有几个注意事项是实际跑的时候容易踩的。第一路径里别带中文HyperView 有些版本对中文路径支持不好提示找不到文件时先怀疑路径编码。第二模型文件加载之前要先确认软件能识别该模型格式某些中间格式需要额外导入选项。第三如果结果文件非常大建议先做一两个文件试跑确认内存占用正常再放开批量循环。6. 常见问题与排查技巧实录6.1 文件导入不进或在加载时闪退遇到文件导入失败我一般按四步排查。第一步看文件扩展名是否在软件支持列表里第二步看文件路径是否包含空格或特殊字符必要时加引号或复制到纯英文路径第三步看软件的版本是否低于生成该文件的求解器版本新版本结果导出的文件旧版后处理经常打不开第四步检查模型文件是否完整有没有遗失材料卡片或单元属性。闪退问题多半是内存不足或文件本身损坏。我建议先单独导入一个文件试一下如果在手动操作下也闪退那大概率不是脚本问题直接换文件或换机器。6.2 结果与模型错位、子工况不对结果文件与模型文件如果来自不同的网格划分版本会出现节点编号对不上、云图显示不出来等问题。最直接的征兆是模型能显示但结果列表为空。这时候要确认两者是不是同一套求解模型生成的不要拿着 Mesh1 的结果挂到 Mesh2 的模型上。子工况不对的现象更难发现因为云图照样显示只是数值不对。解决方法是每次加载完都打印可用工况列表并做一次数据二次校验。例如把脚本算出的最大应力与之前手工后处理记录的值对比差异超过 1% 就停下来检查。6.3 内存与性能优化批量处理脚本最常见的死法是处理到一半内存爆炸。我总结了三个优化习惯第一每个文件处理完及时 Close 客户端并手动触发垃圾回收第二不要在循环中保留不必要的对象引用上次处理得到的云图句柄、数据对象该释放就释放第三关闭实时刷新的动画效果有些接口会在加载过程中频繁重绘批量处理时非常拖速度。如果仍然感觉慢可以看看软件是否支持无界面模式或轻量化模式。有些版本的 Python API 支持在后台不弹出完整界面执行脚本速度能提升不少。具体开关每个软件不一样你可以安装目录下翻翻文档也可以直接搜索当前版本支持的启动参数。6.4 调试与日志最后聊聊调试技巧。我写这类脚本一定会在每个阶段打印进度并且把日志写进文件。刚开始你以为执行很快真跑起来屏幕上滚过去几十行都看不完所以最好用日志文件留存。我的做法是加一个简单的日志函数def log(msg): line f[{time.strftime(%Y-%m-%d %H:%M:%S)}] {msg} print(line) with open(log_path, a, encodingutf-8) as f: f.write(line \n)然后在每个关键节点调用log(Start loading model...)、log(Load result done)。文件一旦出错翻开日志就知道卡在哪一步。这个方法看起来笨但在几十上百个文件的批处理里比单步调试高效得多。我个人在实际操作中的体会是HyperView 的 Python 二次开发难点从来不是语法而是把手工操作的顺序真正理解透。只要你把“先建会话、再导模型、后挂结果、最后出图”这四层顺序理顺批处理自动导入模型和结果就是一层窗户纸。还有一个小技巧脚本写好后不要马上删除把关键参数整理到文件顶部下次换项目时只改路径和工况编号整个工具又能复用起来。自动化最大的价值不是一次性写完而是让后面的每一个同类项目都能受益。
返回列表