ARTICLE · 1139893
开源版BPA电力系统分析软件解读-番外篇(一)BSE转CSV的python程序开发
从一堆乱码到一个 CSV 工具:BSE 转换程序诞生记
一、先搞清楚:BSE 到底是个什么东西
.BSE 不是普通文本文件。
它是 BPA 潮流程序(IPF)算完之后吐出来的二进制结果文件,里面存的是节点电压、支路功率、直流系统状态这些潮流计算的结果。
用记事本打开,只能看到一堆方块和问号。
所以第一件事,不是写代码,而是搞清楚它的格式。
二、第一关:Fortran 的"套娃"记录
打开二进制一看,发现这文件不是平铺直叙的,而是一段一段的。
每一段前面有个 4 字节的长度,后面跟着同样一个长度。这叫 Fortran 无格式记录(unformatted record),是 Fortran 写二进制时的老传统。
于是第一个模块诞生了:FortranRecordReader。
它干的活很朴素:
- 读 4 字节,得到这一段的长度
- 按这个长度把内容读出来
- 再读末尾 4 字节,核对首尾长度是否一致
顺带还解决了一个大问题:大小端。
同一个文件,可能是小端机写的,也可能是大端机写的。程序不去猜,而是拿开头几个字节试一遍,哪种解释能自洽就用哪种。
这一步的意义:把"读文件"和"理解文件"拆开。读文件只负责搬运字节,理解交给后面。
三、第二关:把文件头读明白
字节能读了,接下来得知道每一段是什么。
文件开头是一段文件头,里面藏着:
- 文件类型标记:必须是
PF DATA,不是就报错 - 算例名、日期、标签、程序版本、用户
- 一个 100 个整数的计数数组
这个计数数组很关键,它告诉程序:后面有多少母线、多少支路、多少直流、多少区域。
其中有一个字段叫 case_version,它决定了后面数据是老格式还是新格式。
这一下就分出了两条路:
_parse_old_body:老格式,数据分段打包,还要做一步"展开"和转换- 新格式路径:字段布局更规整,直接按结构读
这就是程序里第一个真正的分支。
四、第三关:老格式的"翻译工作"
老格式有个麻烦:它的数据不是按"一行一条记录"排的,而是按数组排的。
比如支路,先给你一串整数,再给你一串实数,得自己拼回去。
更麻烦的是,老格式里还有一些紧凑编码:几个浮点数其实是字符,几个整数其实是指针。
于是程序里出现了几个专门的"翻译官":
_convert_old_y_matrix:把打包的导纳矩阵展开成数组_convert_old_branches:把紧凑的支路行还原成可读字段int_from_float:把"存成浮点数的整数"还原成整数decode_chars:把"存成浮点数的字符"还原成文字
这一步是整个程序最费脑子的地方,因为格式文档不全,很多细节只能靠对照结果反推。
五、第四关:把原始数据变成"人话"
字节读完了,格式也解开了,但拿到手的还是一堆数组。
数组是给机器看的,不是给人看的。
于是有了 build_xxx_rows 这一组函数:
build_bus_rows:一条母线一行,算出电压幅值和角度build_branch_rows:一条支路一行,带首末端母线名build_dc2t_rows:两端直流,带整流侧、逆变侧信息build_dcmt_bus_rows:多端直流换流站build_special_bus_rows:特殊母线build_transformer_rows:变压器build_owner_rows:设备所有者
它们的共同点是:把数组变成带列名的字典。
比如一条母线,不再是 [3, 525.0, 1.02, ...],而是:
bus_index, name, base_kv, voltage_pu, angle_deg, pload_mw ...这一步的意义:让数据从"能读"变成"能看懂"。
六、第五关:一次导出十个文件
数据整理好了,最后一步是落盘。
write_outputs 负责把数据写成 CSV,一次生成十个文件:
summary.csv:算例概况,一眼看全局buses.csv:母线结果branches.csv:支路结果dc2t.csv:两端直流special_buses.csv:特殊母线auto_transformers.csv:变压器owners.csv:所有者continuation_buses.csv:继续母线switched_shunts.csv:可投切电抗器parsed_records.csv:记录清单,方便排查格式
写文件时特意用了 UTF-8 BOM,这样中文用 Excel 双击打开不会乱码。
这是给"最终用户"的体贴。
七、第六关:让命令行"会说话"
工具能跑了,但用户不知道它跑到哪一步。
于是 main 里加了四步进度提示:
[1/4] Input BSE: ...[2/4] Reading Fortran records ...[2/4] Parsed 26 Fortran records[3/4] Writing CSV files ...[4/4] Conversion completed出错时不是甩一堆堆栈,而是给一句人话:
error: not an IPF BSE file: header type is '...'同时用退出码告诉调用方成功还是失败:
0:成功1:文件格式或读写错误2:参数或路径错误
这一步的意义:工具不光要能用,还要好排查。
八、第七关:双击就能用
命令行虽好,但用户不一定习惯。
于是配了两样东西:
一个是 bse2csv.ini,把输入文件、输出目录、编码、精度都写进配置,改配置不用改代码:
ini [DEFAULT]input_file = LCC-EXAM.BSEoutput_dir = outputencoding = utf-8-sigprecision = 8
一个是 bse2csv.bat,双击就能跑:
- 自动找
python或py - 切到 UTF-8 代码页,避免中文乱码
- 用
python -u关闭缓冲,进度实时刷新 - 跑完
pause,窗口不闪退 - 失败时打印退出码,提示往上翻看错误
这一步的意义:把工具从"程序员能用"变成"谁都能用"。
九、回头看,这个程序的骨架其实很清晰
梳理下来,就六层:
- 读字节:
FortranRecordReader - 解格式:
BseParser.parse,分老格式和新格式 - 转数据:
_convert_old_*、int_from_float、decode_chars - 建表格:
build_xxx_rows - 写文件:
write_outputs - 管交互:
main+ INI + BAT
一句话概括:
把"读二进制"和"看懂数据"分开,把"解析"和"展示"分开。
结尾
很多时候,工程工具就是这样:起点是一个具体的小问题,终点是一套能反复使用的流程,总体上还是:先读字节,再解格式,最后翻译成人话。