Skip to content

DMO 炮孔数据库文件

dimine_python_sdk.lib.io 提供 DmoFile 类用于读写 Dimine 炮孔数据库文件(.dmo)。DMO 本质上是带固定表头(炮孔数据库标准字段,新版 26 列)的 DMT 数据表文件;DmoFile类型化模型容器——读取后把炮孔与连线信息解析为 dmo.holes(每孔一个 DmoHole)与 dmo.network(连线拓扑 DmoNetwork)供程序直接使用,加载时对表头做宽松校验(缺列/多列/类型不符只记录告警、不抛异常,见 dmo.header_warnings),保存时按固定表头还原字段类型,避免 pandas dtype 推断丢失 long/binary 等类型。

若要从爆破设计算法(布孔/装药/网络)直接生成或落盘 .dmo,推荐经 dimine_python_sdk.lib.blastingbuild_dmo / resolve_schedule 装配 (见 爆破设计指南)。本文只讲 DmoFile 本身。


一、加载与保存

从文件加载

读取即解析并缓存 holes / network 类型化模型:

from dimine_python_sdk.lib.io import DmoFile

dmo = DmoFile("炮孔数据库-data.dmo")
print(dmo.holes)                  # list[DmoHole]——炮孔结构(坐标/孔参数/装药信息)
print(dmo.network)                # DmoNetwork——连线拓扑 + 地表起爆时间
print(dmo.network.numbers)        # 全部炮孔编号
print(dmo.header_warnings)        # 表头非标准告警(如有)

也可以先建空对象再显式加载(链式):DmoFile().load("炮孔数据库.dmo")

保存到文件

就地修改模型后 save() 写回。只要模型持有了连线信息(network.edges 非空), 保存前会沿连线网络重算「地表起爆时间」

dmo.holes[0].diameter = 250.0                       # 就地改某孔孔径
dmo.network.line_of("A-01", "A-02").delay = 42.0    # 改某条连线的延时雷管
dmo.save("output.dmo", burn_velocity=2000.0)        # 另存(无扩展名时自动追加 .dmo)
dmo.save()                                          # 覆盖原文件(用默认/全局燃速)

burn_velocity(导爆管燃速 m/s,缺省取全局 HoleParam.tube_burn_velocity,无 DIMINE_HOME 回退 2000)只影响导爆管雷管的连线燃烧时长;数码电子雷管网络(count_line_burn=False)不需要。

从类型化模型新建并保存

DmoFile() 创建空容器,赋值 holes / network 后保存即生成 .dmo

from dimine_python_sdk.lib.io import DmoFile
from dimine_python_sdk.lib.io.dmo_file import BlastLine, ChargeSegment, DmoHole, DmoNetwork

dmo = DmoFile()
dmo.holes = [
    DmoHole(number="BH-01", region="M01", mouth_x=0.0, mouth_y=0.0, mouth_z=765.0,
            bottom_x=0.0, bottom_y=0.0, bottom_z=750.0,
            diameter=220.0, depth=15.0, azimuth=0.0, inclination=-90.0,
            segments=[ChargeSegment(fill_type="填塞物", length=4.5, delay=-1),
                      ChargeSegment(fill_type="乳化炸药", length=10.5, delay=350)]),
    DmoHole(number="BH-02", region="M01", mouth_x=5.0, mouth_y=0.0, mouth_z=765.0,
            bottom_x=5.0, bottom_y=0.0, bottom_z=750.0,
            diameter=220.0, depth=15.0, azimuth=0.0, inclination=-90.0),
]
dmo.network = DmoNetwork(
    nodes=dmo.holes,
    edges={("BH-01", "BH-02"): BlastLine(end_x=5.0, end_y=0.0, end_z=765.0, delay=25.0)},
    delays={"BH-01": 0},
    initiating_hole="BH-01",
)
dmo.save("new.dmo")

「装药结构」「连线信息」「孔内分段起爆」三个 binary 列在 save() 时由 segments / edges / delays + 孔口/孔底坐标自动编码,无需手工构造二进制。

上下文管理器

from dimine_python_sdk.lib.io import dmo_conn

with dmo_conn("炮孔数据库.dmo") as dmo:
    print(dmo.info)                   # {file, hole_count, edge_count, initiating_hole, header_warnings}
    dmo.holes[0].depth = 16.0
    dmo.save()

二、固定表头 schema

DMO 文件拥有固定的 26 列标准字段,定义于模块常量 DMO_FIELD_SCHEMA

from dimine_python_sdk.lib.io import DMO_FIELD_NAMES, DMO_FIELD_TYPES

print(DMO_FIELD_NAMES)   # 字段名列表(26 项)
print(DMO_FIELD_TYPES)   # {'爆破区域': 'string', '炮孔Handle': 'long', ...}

dmo.schema               # [{"name": "爆破区域", "type": "string"}, ...]

字段类型支持 string / long / int / double / binary。标准字段覆盖:

  • 标识:爆破区域炮孔编号虚拟炮孔起爆点炮孔炮孔Handle
  • 起爆参数:起爆延时所属爆堆地表起爆时间孔内起爆时间孔内分段起爆
  • 孔口/孔底坐标:孔口坐标X/Y/Z孔底坐标X/Y/Z
  • 孔参数:孔径孔深方位角倾角
  • 装药参数:装药半径相对孔径的收缩比例装药重量装药长度单耗面积装药结构连线信息

三个时间列易混淆,语义(已用真实 Dimine 文件核对):

  • 起爆延时:仅起爆点孔有意义 —— 从按下开关到燃烧到达该起爆点的延时,非起爆点孔恒为 0。一个网络可有多个起爆点(导爆管/数码电子均可,模型 DmoNetwork.initiating_holes),各起爆点等于其自身的「地表起爆时间」,保存时自动写该孔 delays 值,其余孔写 0。
  • 地表起爆时间:从按下总开关到燃烧到达该炮孔的延时,沿连线网络逐孔累积(父孔 + 孔间延时 + 连线燃烧时间 = 距离/导爆管燃速)。数码电子雷管逐孔独立编程、无导爆管传爆,不计连线燃烧时间(模型 DmoNetwork.count_line_burn=False),起爆时间 = 父孔 + 孔间延时。count_line_burn 不落盘:读取 .dmo 时由 load 按「地表起爆时间」列与连线拓扑反推(含 travel→导爆管、纯 relay→数码电子,证据不足时默认导爆管),使数码电子文件的 load→save 重算不误加连线燃烧时长,并按「首爆点锚 0」推导。
  • 孔内起爆时间 = 地表起爆时间 + 孔口到第一段炸药段的雷管延时之和(ChargeSegment.delay)。
  • 孔内分段起爆(binary):只写不读,不落入模型。保存时按装药分段 + 孔口/孔底坐标 + delays 推导——每个雷管延时 >= 0 的分段在其段中点产生一个起爆点(坐标沿孔口→孔底线性插值,时间 = 地表起爆时间 + 该段雷管延时),编码为 Dimine 原生二进制(int32 条数 + 每条 float64 x/y/z + int32 t)。

对应 DmoNetwork.delays(地表起爆时间)。只要模型持有了连线信息edges 非空, 即设计过/修改过连线网络),save() 保存前即沿连线网络重算 delays:根孔(各起爆点等 无父孔者,导爆管可有多个)锚定各自 delays 里已有值(=「起爆延时」列值,代表从总开关起的 绝对起点),其后代 = 父孔地表起爆时间 + 孔间延时雷管 + 连线燃烧时长(孔口距离 / 导爆管 燃速),燃速缺省取全局 HoleParam.tube_burn_velocity(无 DIMINE_HOME 时回退 2000 m/s, 也可经 save(burn_velocity=…) 显式指定);数码电子雷管时 count_line_burn=False,不累加 连线燃烧时长,且无起爆延时锚——首爆点(入度 0 节点,可有多个)地表起爆时间锚 0,只累加 孔间延时雷管;无连线时 delays 原样透传。起爆点另受「起爆延时」 列回填约束。孔内起爆时间不落入模型,保存时按上式自动推导(= delays + 首段炸药 雷管延时之和)。连线拓扑 DmoNetwork.edges{ (源孔编号, 目标孔编号): BlastLine } 单一数据源:每条边的 BlastLine.delay 是该连线安装的延时雷管(真实文件如 17ms, 不是两孔时间差),箭头/字高/线宽为显示属性,读入后保存原样写回——重算只影响 delays(地表起爆时间),不会把连线延时改写成时间差。


三、表头宽松校验

加载时 DmoFile 会对表头做宽松校验,非标准列或缺列不会抛异常,而是记录到 header_warnings

dmo = DmoFile("旧版炮孔数据.dmo")

for w in dmo.header_warnings:
    print(w)
# 缺少标准列: ['孔内分段起爆', '连线信息']
# 含非标准列: ['孔内延时']
# 字段 '炮孔Handle' 类型不符: 期望 long, 实际 double

校验规则: - 缺标准列:记录告警,缺失标准列不属于模型(对应字段取默认 / 空) - 含非标准列:记录告警,非标准列不进入模型,保存时不会写回 - 类型不符:记录告警,保存时仍按 schema 中的标准类型还原

dmo.info 返回摘要 {file, hole_count, edge_count, initiating_hole, header_warnings}, 其中 header_warnings 即上述加载告警。


四、类型化数据访问

DmoFile 不直接暴露内部数据表,程序通过 holesnetwork 两个类型化模型访问:

  • holes:每孔一个 DmoHole——炮孔编号、坐标、孔参数、装药信息(charge_weight / charge_length / segments …);
  • networkDmoNetwork 连线图——节点即 holes 里的 DmoHole,边为 {(源孔编号, 目标孔编号): BlastLine}
# —— 炮孔(DmoHole)——
len(dmo.holes)                 # 炮孔数
dmo.holes[0].number            # 炮孔编号
dmo.holes[0].depth             # 孔深 m
dmo.holes[0].diameter          # 孔径 mm
dmo.holes[0].stemming_length   # 填塞长度 m(由 segments 计算)
dmo.holes[0].spacer_length     # 间隔总长度 m(由 segments 计算)
for seg in dmo.holes[0].segments:        # 装药分段(ChargeSegment)
    print(seg.fill_type, seg.length, seg.delay)

# —— 连线网络(DmoNetwork)——
dmo.network.numbers            # 炮孔编号列表
dmo.network.edge_pairs         # 连线 [(from, to), ...]
"A-01" in dmo.network          # 孔是否存在
dmo.network.get("A-01")        # 按编号取 DmoHole(None 若无)
dmo.network.line_of("A-01", "A-02")    # 该连线的 BlastLine(None 若无)
dmo.network.outgoing("A-01")   # 出边相邻孔
dmo.network.incoming("A-01")   # 入边相邻孔
dmo.network.neighbors("A-01")  # 全部相邻孔
dmo.network.degree("A-01")     # 度
dmo.network.delays             # {孔号: 地表起爆时间 ms}
dmo.network.initiating_holes   # 起爆点孔编号(可多个)

# —— 模型就地修改后 save() 生效 ——
dmo.network.connect("A-01", "A-03")        # 加一条连线(默认 BlastLine)
dmo.network.disconnect("A-01", "A-03")     # 删连线
dmo.network.add_hole(DmoHole(number="A-09", ...))   # 加孔
dmo.network.remove_hole("A-09")            # 删孔(连带移除其连线)
dmo.network.line_of("A-01", "A-02").delay = 42.0    # 改某条连线延时雷管
dmo.save()

# —— 汇总 / schema ——
dmo.info         # {file, hole_count, edge_count, initiating_hole, header_warnings}
dmo.schema       # 26 列 [{"name": ..., "type": ...}, ...]

dmo.recompute_delays(burn_velocity=...) 可显式沿连线网络重算「地表起爆时间」(有连线时 save() 前自动调用,一般无需手动)。底层仍按标准 26 列表读写;DmoFile 不继承 DmtFile,DMT 通用数据表的 DataFrame 式访问见 DMT 数据表文件


五、异常处理

from dimine_python_sdk.lib.io import DmoError, DmoLoadError, DmoSaveError

try:
    dmo = DmoFile("not_exist.dmo")
except DmoLoadError as e:
    print(f"加载失败: {e}")

try:
    dmo = DmoFile()
    dmo.save()   # 未指定路径
except DmoSaveError as e:
    print(f"保存失败: {e}")

异常层次: - DmoError(RuntimeError) — 基类 - DmoLoadError(DmoError) — 加载失败 - DmoSaveError(DmoError) — 保存失败


参考链接