3. 模型库架构设计原则
一个 Modelica 模型库能否被长期复用,往往不取决于单个模型写得多精巧,而取决于它的架构是否清晰。架构设计要回答的核心问题是:模型放在哪里、叫什么名字、对外暴露什么、内部如何实现、以后怎么加新东西、怎么改而不破坏别人。本节围绕包结构、命名空间、接口与实现分离、可扩展性、可维护性和版本管理初步展开,目标是让读者能规划出一个可交付、可演进的库骨架。
包结构:用目录层级表达领域分解
Modelica 中 package 既是命名空间,也对应文件系统中的目录。一个库通常以顶层包为根,向下按“领域—子系统—部件—基础类”逐层分解。以某热管理库 ThermalLib 为例,可采用如下结构:
ThermalLib/
package.mo // 顶层包,含 uses 与版本注解
UsersGuide/ // 文档与示例说明
Examples/ // 可直接仿真的系统级示例
Interfaces/ // 接口、连接器、部分模型
Media/ // 介质属性
Components/
Pumps/
Valves/
HeatExchangers/
Sources/ // 边界条件与激励
Sensors/ // 测量与信号输出
Utilities/ // 函数、常量、枚举
分解粒度要服务于复用:Interfaces 放连接器和 partial 基类,Components 放可替换的具体实现,Examples 放验证用例。判断结构是否合理,可以看“新增一个换热器型号”是否只需在 HeatExchangers 下加一个文件,而不必改动其它目录。
命名空间:让名字可预测、可定位
命名空间的价值在于唯一性和可读性。建议遵循“从一般到具体”的命名顺序,类名用大驼峰,实例名用小驼峰,连接器类型以 Port 结尾。例如 ThermalLib.Components.HeatExchangers.PlateHE 一眼可知其归属。库内引用优先使用相对路径(如 Interfaces.FluidPort),跨库引用使用完整路径或 import:
import Modelica.SIunits.Temperature;
import ThermalLib.Interfaces.FluidPort;
要避免把实现细节暴露在顶层命名空间,也不要在不同层级重复同名类。命名一旦发布就应视为契约,改名会直接破坏下游模型。
接口与实现分离:替换实现而不动系统
这是库可复用的关键。做法是先用 partial model 或 connector 定义“对外长什么样”,再让具体模型 extends 它。例如定义泵的接口基类:
partial model PartialPump
replaceable package Medium = ThermalLib.Media.Water;
Interfaces.FluidPort_a port_a;
Interfaces.FluidPort_b port_b;
Modelica.Blocks.Interfaces.RealInput N_set;
parameter Real etaNom = 0.7;
end PartialPump;
具体泵 CentrifugalPump 继承该基类并实现方程。系统模型只依赖 PartialPump,通过 replaceable 与 redeclare 在装配时选择型号。这样接口稳定、实现可换,验证用例也能复用同一套连接关系。
可扩展性与可维护性:为变化留出位置
可扩展性体现在“加新实现不改老代码”。常用手段包括:用 replaceable 声明可替换部件、用枚举或 choices 限定可选实现、把公共参数上提到基类。可维护性则要求:每个类职责单一、方程与参数有注释、单位统一使用 Modelica.SIunits、避免在多个文件中复制同一段方程。一个实用检查是——若某参数在三个以上模型中重复出现,就应上提到 partial 基类或 Utilities 常量中。
下面给出一个可填写的库骨架检查表,用于在提交前快速自检:
| 检查项 | 合格判据 | 常见问题 |
|---|---|---|
| 包结构 | 领域分解清晰,Interfaces/Components/Examples 分离 | 所有模型堆在顶层 |
| 命名空间 | 类名唯一、路径可预测、无重名 | 跨层同名、随意缩写 |
| 接口分离 | 系统只依赖 partial/connector | 直接依赖具体型号 |
| 可扩展 | 新增型号仅加文件,不改系统 | 改基类导致连锁修改 |
| 可维护 | 单位统一、注释完整、无重复方程 | 硬编码数值、缺注释 |
| 版本 | version 注解与 uses 一致,变更记录可查 | 无版本号、静默改接口 |
版本管理初步:把接口当契约
Modelica 库通过 annotation(version="1.2.0") 声明自身版本,通过 uses(ThermalLib(version="1.2.0")) 声明依赖版本。建议采用语义化版本:主版本号变更表示接口不兼容(如删除连接器、改参数名),次版本号表示向后兼容的新增(如新增泵型号),修订号表示不改变接口的修正(如修方程、改注释)。发布时同步更新 UsersGuide 中的变更记录,并在 Examples 中保留可回归的验证用例。这样,下游用户升级前就能判断风险,库也具备了持续演进的基础。
Interfaces 层,再并行开发 Components;每次发布前用 Examples 全量仿真一遍,确认接口未破坏、结果仍在预期范围内。