如果只是偶尔从 Granta MI 里导出一两张材料数据表,那图形界面完全够用。但当你需要把几十个牌号的拉伸性能、热物性参数、疲劳曲线整理成 CAE 仿真部门要的 CSV,或者每个月固定导出一次数据做归档时,纯手工操作就会变成一场灾难——复制、粘贴、对齐列名、处理单位、区分版本,任何一个环节出错,下游的仿真结果都会跟着错。
Granta MI Scripting Toolkit 解决的就是这个问题。它不是给 Granta MI 加了一个简单的“导出按钮”,而是把整个数据访问过程变成了一段可以反复执行的程序。你可以用脚本连接服务、按条件筛选记录、读取指定属性、批量导出数据,并且把整个流程固化下来,下次运行只是换一个输出路径或筛选条件。
这篇文章会用实际代码走通一条完整的导出链路:连接 Granta MI、打开数据库和表、按名称搜索记录、读取普通属性和表格(tabular)属性、最后导出成 CSV 文件。同时会讲清楚几个容易踩坑的地方,比如中文乱码、记录没有“主键”、tabular 数据展开方式,以及大批量导出时的性能问题。内容偏实战,建议收藏后照着跑一遍。
1. 导出 Granta MI 数据,为什么值得用脚本解决
先明确一个判断:用 Scripting Toolkit 导出数据,重点不是“自动化”三个字,而是让导出过程可重复、可追溯、可评审。
Granta MI 本身是一个企业级材料数据管理系统,数据结构比普通 Excel 表复杂得多。它里面不只是“表名 + 字段 + 行”的关系,还包括材料记录、属性值、单位、来源、版本、审批状态,以及记录之间的层级关系。手工导出时,这些信息很容易在复制粘贴的过程中丢失或变形。
脚本导出的优势体现在几个方面:
- 一致性。同样一套导出逻辑,每次执行结果都一样,不会因为操作者不同、状态不同产生差异。
- 可追溯。脚本文件本身记录了“导出的是什么、按什么条件筛选、取了哪些属性”,代码评审就是数据评审。
- 批量能力强。一次导出几百个记录、几十个属性,对脚本来说是循环问题,对人工来说是加班问题。
- 容易集成。脚本可以被命令行调用、被定时任务触发,也可以被包装成内部工具给非技术同事使用。
什么样的读者最适合用这套方式?如果你属于下面任一情况,这篇文章就是为你写的:
- 材料工程师:需要定期从 Granta MI 整理材料数据给 CAE 或设计部门。
- CAE/仿真工程师:希望把 Granta MI 中的材料牌号数据批量转成仿真模型需要的输入文件。
- 企业 IT / PLM 集成开发:需要把 Granta MI 数据同步到其他系统,或者做数据迁移。
- 数据管理员:需要审计数据完整性,定期导出快照做对比。
2. Granta MI 与 Scripting Toolkit 的核心概念
在写脚本之前,需要先理解 Granta MI 的数据模型。很多人第一次接触会不自觉地拿关系型数据库去套,结果在理解和编码时处处碰壁。
2.1 Granta MI 是什么
Granta MI 是 ANSYS 旗下 Granta 公司推出的材料数据管理系统,核心作用是统一管理材料性能数据,并把材料数据与 CAE 仿真流程连接起来。它保存的数据不像普通数据库那样只有“值”,还附带单位、来源、规范、测试条件、审批状态等背景信息。这也是材料数据的特殊性:同样一个弹性模量值,测试温度、测试标准、试样方向不同,意义完全不同。
2.2 Scripting Toolkit 是什么
Scripting Toolkit 是 Granta MI 提供的程序化访问接口,主要面向 Python。通过它,你可以绕过 GUI,直接用脚本完成以下操作:
- 连接 Granta MI 服务。
- 打开数据库和表。
- 搜索记录。
- 读取或写入属性值。
- 遍历记录的层级关系。
对于导出任务,我们最常用的是 Session、Database、Table、Record、Attribute 这五个核心对象。
2.3 核心对象对应关系
| Granta MI 对象 | 通俗理解 | 对应脚本中的用途 |
|---|---|---|
| Session | 与服务端的连接会话 | 负责登录和连接管理 |
| Database | 数据库 | 打开某个材料数据库 |
| Table | 数据表 | 定位到具体的数据表,如金属材料表 |
| Record | 记录 | 对应一条材料记录,例如一个牌号 |
| Attribute | 属性 | 记录下的具体属性,如屈服强度、热导率 |
2.4 与传统数据库表的区别
传统关系表里,一个表就是二维行列结构,每一行有固定字段。Granta MI 的数据则更灵活,记录下可以有不同类型的属性,属性可能是一个数值、一段文本、一张图片、一个文件,甚至是一个子表格(tabular)。
这就解释了为什么“导出成 CSV”并不像SELECT * FROM table那么简单。你需要明确三件事:
- 导出哪部分记录(搜索条件)。
- 导出哪些属性(属性清单)。
- 属性里的复杂结构(tabular、多值、带单位数据)如何展开成二维表。
2.5 关于“没有主键 id”的困惑
很多人在用 DBeaver、Navicat 等工具导出数据库数据时,会纠结“导出结果没有主键 id”。在 Granta MI 里也有类似的情况:普通 CSV 导出不会自动带一个类似自增主键的字段。如果你下游需要唯一标识一条记录,不要依赖行号,而应该显式导出记录的 GUID、路径或名称。稳健的做法是,在导出结果中用一列保存记录的唯一标识,例如record.guid或record.path,这样即使数据重复也不会造成下游数据关联混乱。
3. 环境准备与前置条件
3.1 软件环境
使用 Scripting Toolkit 需要满足以下条件:
- 一个可访问的 Granta MI 服务端,提供服务器地址和端口。
- 一个有连接权限的账号,且该账号对目标数据库、表有读取权限。
- Python 环境,推荐使用 Python 3.8 及以上版本。
- 安装对应的 Scripting Toolkit Python 包。
具体版本号以你所用 Granta MI 服务端对应的官方发布为准。不同版本包的接口细节会略有差异,本文演示的是通用思路,重点帮助你理解流程,不会把版本写死。
3.2 安装方式
典型安装命令如下:
pip install GRANTA-MIScriptingToolkit安装完成后,确认包可以正常导入:
from GRANTA_MIScriptingToolkit import granta as mpy print(mpy.__version__)如果控制台能正常输出版本号,说明环境基本就绪。
注意,有些公司的网络环境会限制 Python 包下载源,你需要提前配置好内部 PyPI 镜像。如果安装时出现网络超时,优先检查 pip 源,而不是反复重试。
3.3 连接前的信息清单
开始写代码之前,建议先准备好以下信息,避免在脚本里反复改:
- 服务地址,形如
http://your-mi-server/mi/servicelocator。 - 端口号。
- 目标数据库名称。
- 目标表名称。
- 需要筛选的记录条件(例如名称前缀、牌号范围)。
- 需要导出的属性名称列表。
- 输出目录和文件格式。
3.4 权限与安全提醒
连接 Granta MI 属于访问企业核心数据系统,必须注意:
- 在正式导出前,确认自己是否有对应权限。
- 优先使用只读账号。
- 不要把账号密码硬编码在脚本里提交到代码仓库。
- 涉及敏感材料数据时,遵守企业内部的数据合规要求。
4. 连接服务并读取目标表:最小示例
我们先走通最小链路:连接服务、打开数据库、打开表,并打印表中的记录数量。这个步骤看起来很基础,却最容易出问题。百分之八十的“脚本写好了但跑不通”都发生在连接和处理数据库名称这两步。
# 文件路径:demo_connect.py from GRANTA_MIScriptingToolkit import granta as mpy # 1. 创建连接会话 session = mpy.Session( "http://your-mi-server/mi/servicelocator", port=443, autologon=False ) # 2. 登录 session.connect("your_username", "your_password") # 3. 打开数据库 db = session.get_db("MI_Materials") # 4. 打开数据表 table = db.get_table("Metals") # 5. 获取表中所有记录(非递归) records = table.all_records() print(f"数据库: {db.name}") print(f"表: {table.name}") print(f"记录数: {len(records)}")这段代码做的事情是从服务端定位数据库MI_Materials,打开Metals表,然后拉取所有顶层记录。实际环境中,get_db和get_table的参数必须与 Granta MI 中的数据名称完全一致,大小写、空格都不能差。
如果你的公司环境启用了 SSL 证书校验,连接时可能还需要额外的证书配置。此时应参考对应版本 SDK 文档中关于 HTTPS 连接和证书处理的说明,不要随意关闭证书校验。
5. 完整示例:按条件筛选并导出为 CSV
最小示例跑通后,我们开始做真正的导出任务。假设场景是:从金属材料表中,把名称包含“AISI”的钢材料记录导出,属性包括密度、弹性模量、屈服强度、抗拉强度,输出为 CSV。
5.1 编写导出脚本
# 文件路径:export_csv.py import csv from GRANTA_MIScriptingToolkit import granta as mpy # ---------- 参数配置 ---------- SERVER = "http://your-mi-server/mi/servicelocator" PORT = 443 USERNAME = "your_username" PASSWORD = "your_password" DATABASE = "MI_Materials" TABLE = "Metals" ATTRIBUTES = ["Density", "Elastic modulus", "Yield strength", "Tensile strength"] OUTPUT_FILE = "steel_data.csv" # ---------- 连接 ---------- session = mpy.Session(SERVER, port=PORT, autologon=False) session.connect(USERNAME, PASSWORD) db = session.get_db(DATABASE) table = db.get_table(TABLE) # ---------- 筛选记录 ---------- records = table.search_for_records_where( {"Name": "AISI"} ) print(f"命中记录数: {len(records)}") # ---------- 导出 ---------- with open(OUTPUT_FILE, "w", newline="", encoding="utf-8-sig") as f: writer = csv.writer(f) writer.writerow(["Record Name"] + ATTRIBUTES) for record in records: row = [record.name] for attr_name in ATTRIBUTES: if attr_name not in record.attributes: row.append("") continue attr = record.attributes[attr_name] row.append(_format_attribute(attr)) writer.writerow(row) print(f"导出完成: {OUTPUT_FILE}") def _format_attribute(attr): """把属性值转换为可写入 CSV 的字符串。""" if attr.unit is not None: value = attr.value if value is None: return "" return f"{value} {attr.unit}" value = attr.value if value is None: return "" return str(value)注意,脚本中把_format_attribute函数定义在了调用之后。在实际编码时,函数应该定义在文件前部,或者放到类中。这里分开写是为了让主流程读起来更直观。你运行前需要把函数定义移动到调用之前。
5.2 代码关键点解释
search_for_records_where是按条件搜索记录的方法,参数是属性条件字典,这里用Name包含AISI的模糊匹配。具体匹配语法跟表的配置有关。record.attributes是一个属性字典,键是属性名。如果某个属性在记录上不存在,直接访问会抛异常,所以代码里先做了if attr_name not in record.attributes判断。attr.value是属性值。如果属性配置了单位,attr.unit会返回单位字符串。把单位和数值一起导出,能避免下游使用者误解数据。encoding="utf-8-sig"很关键。如果导出后要用 Excel 打开,UTF-8 编码没有 BOM 时中文表头会变成乱码,用utf-8-sig可避免这个问题。
5.3 避免中文乱码
数据库导出中文乱码,这是所有做数据导出的人都会遇到的问题。不只是 Granta MI,MySQL、DBeaver 等工具导出 CSV 也一样。中文乱码的根源通常是写入文件时的编码与打开文件时的编码不一致。
在 Python 里,三个常用编码选项的区别如下:
| 编码方式 | 适用场景 | 说明 |
|---|---|---|
utf-8 | 程序间数据交换 | 文件小,兼容性好,但 Excel 直接打开中文可能乱码 |
utf-8-sig | 需要 Excel 直接打开 | 带 BOM,Excel 能正确识别 UTF-8 |
gbk | 国内老系统兼容 | 文件体积较大,跨平台兼容性差 |
我的建议是:如果导出结果要交付给同事用 Excel 打开,统一用utf-8-sig。如果只是程序读入,用普通utf-8即可。
6. 导出层级数据与表格(tabular)属性
Granta MI 里最有特点的数据结构是 tabular 属性。它可以理解成“一条记录里嵌套了一张子表”。例如,一个材料牌号记录下可能有“不同温度下的热导率表”,这个表不是二维平铺在记录上,而是作为这个属性的值保存。
这种情况不能像普通属性一样直接写入 CSV 的一列。你需要决定导出策略:是把整张子表序列化为一段文本,还是把每条记录按子表的行数展开成多行数据。
6.1 展开成多行
这是最常见的策略,适合下游做数据分析。思路是:外层记录每一条,对应 tabular 子表里的每一行都生成一行输出。
# 文件路径:export_tabular.py import csv from GRANTA_MIScriptingToolkit import granta as mpy session = mpy.Session("http://your-mi-server/mi/servicelocator", port=443, autologon=False) session.connect("your_username", "your_password") db = session.get_db("MI_Materials") table = db.get_table("Metals") records = table.search_for_records_where({"Name": "AISI"}) tabular_attr_name = "Thermal conductivity vs temperature" output_file = "thermal_data.csv" with open(output_file, "w", newline="", encoding="utf-8-sig") as f: writer = csv.writer(f) writer.writerow(["Record Name", "Temperature", "Conductivity"]) for record in records: attr = record.attributes[tabular_attr_name] # 子表数据通常通过 attr.value 获取,value 是一组行对象的列表 rows = attr.value if not rows: continue for row in rows: # 行对象可以理解为“列名 -> 单元格值”的映射 temp = row["Temperature"] value = row["Conductivity"] writer.writerow([record.name, temp, value])6.2 注意事项
row["Temperature"]返回的是单元格对象。需要确认你使用的 SDK 版本中该对象是否可直接赋值给 CSV writer。如果不行,需要先转成字符串。- tabular 子表的列名必须和实际表结构一致,编写脚本前先在 Granta MI 界面查看一下属性的列结构,不要凭感觉写列名。
- 如果子表有多列,可以考虑把整行数据转成一个字典,再按固定列顺序写出。
导出层级记录(父记录下的子记录)时,思路类似。你需要确定是递归遍历所有层级,还是只导出某一层。递归遍历时要注意循环引用的风险,通常 Granta MI 的记录层级是树状结构,但仍建议在代码里加一个最大深度限制。
7. 运行结果与效果验证
脚本运行完成,并不代表导出结果正确。数据导出是一个非常容易被“看起来正常但实际错误”误导的任务。
7.1 判断成功的基本标准
导出完成后,建议按以下顺序检查:
- 文件是否生成。文件路径下是否存在目标文件。
- 记录数是否符合预期。如果筛选条件是
Name包含AISI,你预期的记录数是多少?如果结果 0 条或明显偏多,优先排查搜索条件。 - 列名是否正确。CSV 表头是否包含预期属性名。
- 数值是否合理。抽查几个数值,和 Granta MI 界面显示的值对比,特别是单位是否带上。
- 编码是否正常。用 Excel 或文本编辑器打开,确认中文没有乱码。
7.2 验证脚本可以这样做
可以写一个小的验证脚本,读取刚导出的 CSV,并检查关键字段。
# 文件路径:verify_export.py import csv with open("steel_data.csv", "r", encoding="utf-8-sig") as f: reader = csv.DictReader(f) rows = list(reader) print(f"总行数: {len(rows)}") if rows: print(f"第一条记录: {rows[0]}")如果输出能看到第一条完整数据,且不是空行,说明基本流程已经没有问题。
7.3 性能问题:记录数很多时怎么办
当你尝试导出上万条记录时,脚本可能会变慢,甚至超时。此时处理思路是:
- 按批次筛选记录,而不是一次拉全量。
- 只获取需要的属性,不要把所有属性都取到内存。
- 分批写入 CSV,避免一次性拼接大字符串。
- 必要时使用任务调度方式,在低峰期执行。
还有一种情况是脚本本身执行很快,但网络传输慢。Granta MI 的数据往往包含大量元数据,如果只取值不取单位、历史信息,可以通过属性读取接口限制返回内容,具体取决于 SDK 支持的参数。实在不行,就分表导出,避免一个脚本处理全部数据。
8. 常见问题与排查思路
根据实际使用中容易遇到的问题,整理了一张排查表,建议先收藏再慢慢验证。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 连接失败或连接超时 | 服务地址错误、端口不通、网络隔离 | 浏览器访问服务地址;用 telnet 测试端口 | 确认地址和端口;联系管理员开通网络策略 |
| 登录失败 | 账号密码错误、账号锁定 | 在 Granta MI GUI 上验证账号 | 重置密码或申请正确权限 |
| 找不到数据库或表 | 数据库名/表名不匹配 | 在 Granta MI 界面复制准确的名称 | 用代码打印所有库名和表名,逐个核对 |
| 访问被拒绝 | 当前账号没有读取权限 | 查看服务端日志 | 申请只读权限 |
| 导出结果为空 | 搜索条件与数据不匹配 | 单独打印搜索命中的记录数 | 简化搜索条件,使用更少的关键词测试 |
| 中文乱码 | CSV 编码格式问题 | 用不同编辑器打开文件 | 使用utf-8-sig编码 |
| tabular 属性导出为空 | 列名写错或属性类型判断错误 | 打印 tabular 属性列名和行数 | 在 GUI 中确认属性列结构 |
| 数值与 GUI 显示不一致 | 单位未转换或取到了底层原始值 | 同时导出属性值和单位进行比较 | 核对单位字段;确认显示设置 |
| 脚本内存占用高 | 一次性拉取大量记录 | 检查进程内存 | 分批处理、限制属性范围 |
8.1 搜索条件不生效的典型原因
search_for_records_where的过滤条件通常依赖表上的搜索配置。如果某个属性没有配置“可搜索”能力,你用这个属性搜索就会得到 0 条结果。遇到这种情况,不要盲目改代码,先确认表上哪些属性支持搜索。最稳妥的方式是先测试用Name搜索,再逐步增加其他条件。
8.2 属性值带单位但导出后变纯数字
这通常是因为取的是attr.value而不是带单位的字符串。如果你希望导出时保留单位,建议使用属性值对象中带单位信息的格式化方法,或者直接把attr.unit一起写到 CSV 里。不要在导出后再靠人工判断单位,那样迟早会出错。
9. 工程实践与进一步优化建议
导出脚本一旦跑通,就要考虑它能不能长期用、能不能交给别人用、能不能应对后续需求变化。
9.1 把导出逻辑封装成函数
不要把所有的连接、搜索、导出、格式化代码堆在一个文件里。建议拆成几个部分:
- 配置模块:统一管理服务地址、账号、数据库名。
- 连接模块:负责建立会话、获取数据库、获取表。
- 导出模块:负责具体导出逻辑。
- 主入口:解析命令行参数并调用各模块。
这样后续换数据表、换导出属性时,只需修改配置或参数,不需要大改代码。
9.2 参数化脚本
把可变的字段,比如数据库名、表名、搜索关键词、输出路径,通过命令行参数传递。这样可以方便地接入定时任务。
python export_csv.py \ --server "http://your-mi-server/mi/servicelocator" \ --database "MI_Materials" \ --table "Metals" \ --keyword "AISI" \ --output "steel_data.csv"使用argparse或click都可以实现,重点是不改代码就能调整导出范围。
9.3 日志记录
导出任务不是每次都能成功,尤其是定时任务。建议在脚本中记录关键日志,包括命中记录数、导出文件路径、耗时、失败原因。日志写到文件里,方便任务失败后排查。
9.4 敏感信息管理
脚本中不要明文写密码。推荐方式:
- 使用环境变量读取账号密码。
- 使用企业内部密钥管理服务。
- 使用配置文件但限制文件权限。
无论哪种方式,都要确保导出脚本所在的机器和目录有严格的访问控制。
9.5 定时导出与归档
如果需要周期性导出,把脚本做成命令行工具后,可以用系统自带的任务计划程序或调度平台定时执行。建议每次导出都生成带日期后缀的文件,例如steel_data_20250601.csv,不要覆盖历史文件,这样出现数据差异时还能回看上次导出的内容。
9.6 关于“导出数据没有主键 id”的建议
这条建议值得再强调一次。导出后的 CSV 如果不包含唯一标识,下游一旦要做增量更新或数据关联,就会非常被动。因此在导出脚本里,建议始终包含一列唯一标识。Granta MI 中最可靠的是记录的 GUID 或路径,而不是名称,因为名称可能重复或被修改。
writer.writerow([record.name, record.guid, ...])9.7 版本兼容与回归验证
Granta MI 服务端升级、SDK 升级都可能导致 API 行为变化。建议在升级后重新跑一遍导出的回归用例,对比旧导出文件和新导出文件,确认字段、单位、行数没有变化。
10. 总结与下一步实践
这篇文章的核心思路可以概括为一句话:Granta MI Scripting Toolkit 导出数据的本质,是把“人在界面上的点击操作”转换成“可复用、可审计的程序逻辑”。连接、搜索记录、读取属性、处理单位、展开 tabular、写入 CSV,每步都有明确的代码载体。
如果你是第一次接触这套工具,建议先不要急着追求复杂功能。第一步,跑通最小连接示例,确认环境没问题;第二步,导出一张表的一个属性;第三步,逐步增加属性和搜索条件;第四步,再考虑 tabular、批量、定时等复杂场景。
下一步值得继续深入的方向有三个:一是把导出脚本封装成团队内部工具,让同事不用写代码也能完成导出;二是把导出能力接入仿真流程,实现从材料数据库到 CAE 模型的数据自动流转;三是结合测试环境和生产环境分离策略,在验证环境确认脚本行为后再接入生产数据。导出只是第一步,让材料数据真正被下游系统安全、准确地使用,才是这项工作的价值所在。