COMSOL Multiphysics 二次开发系列教程(1):执行边界与三条运行时——同一个 API 底座的四个宿主
版本声明块
- 工具/软件:COMSOL Multiphysics 6.4(2025-11 发布);Java Shell window 与 Chatbot window 自 6.3 引入
- 语言/环境:Java(COMSOL API for use with Java,即 com.comsol.model 包)+ Python(MPh 1.4.0,经 JPype 桥接)
- 本文目标:读完之后,你能判断一段 API 代码"能在哪个宿主里跑、需不需要编译、受不受平台限制",并能写出四条路径下等价的最小可运行代码
一句话结论:COMSOL API(com.comsol.model 包)是唯一底座,Method Editor、Java Shell window、comsolcompile + comsolbatch、MPh 只是同一底座的四个宿主——判定边界看三件事:要不要 JDK 编译环节、是否仅限 Windows、能否在同一进程里开第二个 client;ModelUtil.create("Model") 与 mph.start() 是同一底座在 Java 与 Python 侧的两个入口。
〇、本篇要解决的认知问题
读完这八问,你就有了全系列反复回扣的心智模型:一个底座、四个宿主、边界由"编译环节 + 平台 + 进程"三件事决定。
一、机制解析
1.1 底座只有一个:com.comsol.model 包
先摆出全系列最基础的事实,也是铁律 1(API 底座唯一):COMSOL API 是所有程序化接口的共同底座。官方 Learning Center 文章《Overview of the COMSOL API》的原话是——无论你写在 Application Builder 的方法里、用 LiveLink for MATLAB 驱动仿真、还是通过 LiveLink for Excel 跑模型,你始终在交互的是同一批支撑用户界面的类和数据结构。换句话说,GUI 不是"另一套系统",GUI 是这套类的图形外壳。
这带来一个极其有用的推论:GUI 的每一个操作都对应一次 API 调用。你在 Model Builder 里给一个几何特征改尺寸,背后就是 set("size", …);你在树上右键某个节点选 Copy as Code to Clipboard,就是把这次映射显性化打印出来。Record Method(录制方法)则是把一段会话里所有这类映射按顺序落成代码——录制时 COMSOL Desktop 窗口会被红框包围,这个红框就是"你正在生成代码而不是手动建模"的视觉确认。
这条推论的工程价值在于:代码与界面之间不再有"翻译层"。你不需要猜测某个 API 参数对应界面上哪个字段,也不需要怀疑"录制出来的代码是不是简化版"——它就是同一条调用链。反过来,任何一段 API 代码都能在 GUI 里逐行验证:跑完代码后打开模型树,看到的节点状态与代码里创建的 feature 一一对应。入门阶段最该建立的习惯就是这个双向验证:写一段代码、看一眼树、确认两者一致。
还有一条常被忽略的口径:API 覆盖范围是"全量"的。官方文档明确——几何创建、网格划分、求解、可视化与结果评估都在 API 控制范围内,且这套接口在所有物理接口(电磁、结构力学、声学、流体、传热、化学工程及任意多物理组合)之间保持一致。这意味着你在一个模块里学会的 feature().create(…) / set(…) 写法,换到另一个模块不用重学。这是"底座唯一"带来的第二重收益:学习成本只付一次。
为什么这对你重要:一旦接受"GUI 只是外壳",你就知道任何 GUI 能做的事都能脚本化,反过来,脚本里出现的每一个 set 都能在 GUI 里找到对应面板。调试时"代码行为与界面行为不一致"这类困惑会迅速消失——它们本来就是同一件事。
┌─────────────────────────────────────────────┐
│ COMSOL API 底座(com.comsol.model) │
│ Model / ModelUtil / ModelEntity / feature │
└─────────────────────────────────────────────┘
▲ ▲ ▲ ▲ ▲
│ │ │ │ │
┌───────────────┘ │ │ │ └───────────────┐
│ │ │ │ │
Application Builder Java Shell 命令行 MPh LiveLink
Method Editor window comsol (JPype 桥) for MATLAB
(自动编译,仅 Windows) (即执行,仅 Windows) batch (Python 门面)
│
数据管线/批处理宿主
四个宿主(把 LiveLink for MATLAB 算作第五个,本篇只在生态集成篇展开)不是四套 API,而是同一底座的四种宿主进程形态。差别只在三件事:谁负责编译、跑在哪个平台、进程模型允许几个 client。
过渡句:下面按"编译环节是否存在"这条线把四条路径分开讲,因为这是入门阶段最容易踩坑、也最容易产生误解的分界。
1.2 路径一:Method Editor——自动编译的轻量开发环境
Method Editor(方法编辑器)是 Application Builder 内置的轻量开发环境,特征是自动编译:你写完代码,右键方法节点选 Run,代码就跑起来了,你不需要装 JDK、不需要 javac、不需要命令行。它只需要一个标准的 Windows 版 COMSOL 安装,没有任何额外安装动作。
Method Editor 的产物叫"方法(Method)“,它属于某个 app 或模型,可以绑定到表单上的按钮事件,可以在 app 运行时被调用。这就是为什么它必须"自动编译”——app 的运行时机由用户点击决定,编译环节必须藏在宿主里,不能要求最终用户装 JDK。反过来说:如果你要把代码交付给没有 JDK 的同事,Method Editor 是唯一不需要对方具备编译环境的宿主。
创建方法的路径是 Application Builder 工作区 → 树上右键 Methods 节点 → New Method。录制路径是 Home 选项卡的 Record Method 按钮(生成新方法)或 Method 选项卡的 Record Code 按钮(追加到已有方法)。
关键边界:Method Editor 与 Java Shell window 都只在 Windows 版软件中可用。这是硬边界,不是性能差异。Linux/macOS 上你能用命令行路线和 MPh,但打不开这两个窗口。
还有一个容易被忽略的细节:Method Editor 里默认看不到 Java 类头(import com.comsol.model.*; 与 public class …),需要在 Preferences 窗口里打开。你要引用 Java 第三方库时,这个开关就是入口——这也是"Method Editor 里的代码"与"Save As 出来的 .java 文件"在结构上唯一的差别:后者一定带类头,前者默认不带。
为什么这对你重要:入门阶段最容易形成的坏习惯是"把录制出来的代码当成最终代码"。录制会把会话里的每一步都写出来,包括你改错又改回来的那些。真正可复用的方法必须手工清理——清理的动作本身就是理解 API 的过程。Method Editor 的价值不在于"能生成代码",而在于它让代码与模型处于同一个宿主里,改一行、跑一次、看结果,这个循环比命令行路线短得多。
1.3 路径二:Java Shell window——即执行的命令提示符
Java Shell window(Java Shell 窗口)是 6.3 引入的交互式环境,提供运行 Java 代码的命令提示符,支持 Method Editor 的多数特性(代码补全、语法高亮等)。入口在 Model Builder 的 Home 选项卡 → Layout 组 → Windows 菜单 → Java Shell。
它和 Method Editor 的差别不是"能不能编译",而是语义不同:
| 产物 | 方法(Method),可绑定 app 事件 | 无产物,代码即执行 |
| 是否需要先建方法节点 | 需要(New Method) | 不需要,粘贴即跑 |
| 执行时机 | app 运行时或手动 Run | 立即 |
| 输出对象可点击回树节点 | 否 | 是:双击输出可跳到对应节点,或工具栏 Go to Node |
| 典型用途 | 可复用逻辑、app 功能 | 原型验证、批量创建 feature、查看属性、断点处求值 |
"对象可点击回树节点"这一条是 Java Shell 最有价值的特性:你在窗口里用代码创建了一个几何特征,输出里出现它的对象标签,双击就跳到 Model Builder 树上对应节点。这让"代码 → GUI 状态"的往返变得极短,是入门阶段最该练的调试习惯。
为什么这对你重要:API 学习最大的障碍是"代码写完看不到发生了什么"。Java Shell 把代码与模型树接在同一个窗口里——你写一行、跑一次、双击回节点、看属性对不对。这个回路比"改代码 → 跑方法 → 切到 Model Builder 找节点"短一半。6.3 起它还能在 Application Builder 与 Model Manager 工作区使用,配合 Data Viewer(原 Variables 窗口改名)可以看到参数、声明和 Java Shell 会话变量——这也是"同一底座"的直接证据:同一个窗口能同时看到三处状态。
Java Shell 的另一个常被低估用途是单行验证。你不确定某个 feature 的属性名是什么,直接在 shell 里跑一行读出来,比翻文档快。这种"边跑边查"的方式在入门阶段比系统读完 API 参考更有效——它把文档变成可执行的对象。
6.4 在 Java Shell 上增加了 Record Code(录制代码)能力;Java Shell 还支持代码补全与语法高亮,覆盖 Method Editor 的多数特性。
1.4 路径三:comsolcompile + comsolbatch——需要 JDK 编译环节的命令行路线
这条路径是"经典 Java 二次开发"路线,也是四条里唯一多出一个编译环节的:
comsolcompile(Windows 命令名)的价值在于它自动把 COMSOL API 的路径设置给 Java 编译器。你不需要手工把 COMSOL 安装目录 plugins 下的 JAR 文件拼进 classpath——这正是很多新手在 IDE 里编译失败的第一原因。典型用法(官方示例形式):
comsolcompile -jdkroot PATH_TO_JDK HelloWorld.java
PATH_TO_JDK 是 JDK 的安装目录。这里有一条容易踩的版本陷阱:comsolcompile 本身不替代 JDK——它只是把 COMSOL API 路径配好,真正的编译器仍来自你安装的 JDK。JDK 版本与 COMSOL 的 Java 兼容级别必须对得上(Method Editor 自 6.3 起支持 Java 11,IDE 路线需要相应兼容设置;具体兼容级别以你所用版本的官方文档为准)。
IDE 路线(Eclipse 或 VS Code)则需要在 build path 里加入 COMSOL 安装目录 plugins 下的全部 JAR,这才能找到 COMSOL API 的类定义并以 client/server 模式运行。VS Code 路线的官方做法是把 java.project.referencedLibraries 指向 C:/Program Files/COMSOL/COMSOL63/Multiphysics/apiplugins/*.jar(Windows 默认位置)。
命令名的平台差异是必须记住的坑:Windows 上是 comsolcompile(连写),Linux/macOS 上是 comsol compile(空格分开的 command + target)。同理 batch:Windows 是 comsolbatch,Linux/macOS 是 comsol batch。
为什么这条路径值得单独存在?因为它是唯一能"脱离 COMSOL Desktop 图形界面、以独立程序形态集成进现有代码库"的路径。编译产物 .class 可以被 COMSOL Desktop 通过 File > Open 打开(你会看到与 .mph 相同的模型),也可以连接 COMSOL Server 进程,或作为 standalone 程序集成进更大的系统。Method Editor 做不到"集成到外部代码库",MPh 做不到"交付一个纯 Java 编译产物"。
为什么这对你重要:如果目标是把仿真嵌进公司已有的 Java 系统、或需要在没有图形界面的服务器上跑批处理,这条路径是唯一选择。它的代价是多一个编译环节和一次 classpath 配置——这个代价在 3.1 节的报错里会具体显现。经验法则:交互式探索用前两条路径,交付与集成用这条,数据管线用下一条。
1.5 路径四:MPh——同一 Java API 的 Python 门面
MPh 是 PyPI 上的 Python 包(pip install MPh),依赖 JPype 作为 Python 到 Java 的桥,依赖 NumPy 返回数值数组。它最常被误解为"另一套 API",其实它做的是两件事:
- 提供一套 Python 化的封装(mph.start()、client.load()、model.solve()、model.evaluate());
- 通过 .java 属性把完整的 COMSOL API 原样暴露出来——Client 的 .java 映射到 ModelUtil,Model 的 .java 映射到 model 对象。
所以 MPh 是门面 + 直通,不是替代。这也解释了它的两个"不需要":不需要安装 Java(COMSOL 自带 JRE),不需要为 Java 源码走 comsolcompile 编译环节。
安装与版本口径(官方文档明确):需要 64 位 Python 以匹配 COMSOL 的平台位数;COMSOL 版本 6.0 及更新预期可用,官方成功测试到 6.3;COMSOL 5.5/5.6 可能可用但需手动降级 JPype(pip install "jpype1<1.6",因为新版 JPype 不再支持这些旧版本自带的 Java 8)。Windows 上 Microsoft Store 装的 Python 有已知问题,建议用 python.org 的安装包。COMSOL 安装在安装程序建议的默认位置时 MPh 开箱可用——Windows 上自定义位置也可,因为安装程序把位置写进中央注册表,MPh 会去查。
许可口径:MPh 不碰许可配置,它假定"你能启动 GUI 或任意命令行工具,许可管理就已经处理好了"——这对最常见的 CPU-Locked 与 Floating Network 两种许可成立。特殊许可(如 Class Kit 需要 -ckl)要在 mph.start() 前用 mph.option('classkit', True) 显式告知 MPh。
MPh 的设计取舍值得单独说:它刻意隐藏 tag(标签),用 name/label(名称)引用节点。官方文档的解释很直白——tag 是实现细节,是脚本化 COMSOL 模型时"不必要的麻烦",因为 name/label 同样被强制唯一,tag 并非用于消歧;而 tag 不能自由修改:删掉一个 feature 再重建,它可能拿到不同的 tag,所有引用它的代码都要跟着改。用 name 引用可以规避这一点。这是 MPh 与 Java 路线在"心智模型"层面最大的差别,也是全系列**铁律 2(tag 是主键)**的由来。
为什么这对你重要:批量脚本最容易失效的原因就是 tag 漂移。今天脚本引用 blk1,明天有人在 GUI 里删了它又重建,它可能变成 blk5,脚本立刻报错。用 name 引用(MPh 的默认方式,或 Java 侧的 label)能让脚本跨模型、跨会话稳定工作。这一条在入门阶段就要定下来,因为它决定你后面所有脚本的写法风格。
过渡句:四条路径讲完,下面把它们的边界一次性摊平,再落到代码。
1.6 四条路径对照表
| Method Editor(Application Builder 内) | Java | 自动编译(宿主内) | 仅 Windows | app 功能、可复用方法、绑定表单事件 |
| Java Shell window | Java | 自动编译(即执行) | 仅 Windows | 原型验证、批量创建 feature、断点求值、代码↔树节点往返 |
| comsolcompile + comsolbatch | Java | 需要 JDK 编译环节(comsolcompile 自动设置 COMSOL API 路径) | 全平台(Windows/Linux/macOS) | 集成进现有代码库、无图形界面的批处理、交付 .class 产物 |
| MPh(Python 包) | Python | 无需编译,无需安装 Java(COMSOL 自带 JRE) | 全平台 | 数据管线、CSV 驱动批量建模、Python 生态集成 |
三列判定口径(本篇的核心工具):
- 要不要 JDK 编译环节 → 只有第三条需要;
- 是否仅限 Windows → 前两条是硬限制;
- 同一进程能否开第二个 client → 不能,这是铁律 9(同一进程只能一个 client),MPh 的多核并行必须靠多进程实现。
这张表在入门阶段的作用不是"选一个最好的",而是"知道自己在哪条路上、能不能换"。四条路径之间没有代码继承关系:Method Editor 里的方法不能直接搬到 Python,MPh 的封装层不能直接搬到 Java。能搬的只有"逻辑",而且搬的时候必须处理类型差异(见 2.3 节)。这也是为什么本篇坚持先讲底座、再讲宿主——先确认"是同一套东西",再谈"哪些部分不通用"。
1.7 命令行总表与默认端口 2036
与本篇相关的命令(按官方命令参考整理;Windows 为连写命令名,Linux/macOS 为 comsol <target> 形式):
| comsol | comsol | 启动独立 COMSOL Desktop |
| comsol batch | comsolbatch | 运行 .mph 或 .class 文件(批处理模式) |
| comsol compile | comsolcompile | 编译 Model File for Java(把 app 编译为可执行文件需 COMSOL Compiler) |
| comsol mphclient | comsolmphclient | 运行 COMSOL Multiphysics client |
| comsol mphserver | comsolmphserver | 启动 COMSOL Multiphysics server |
| comsol doc | comsoldoc | 打开 COMSOL Documentation 窗口 |
| comsol trust | comsoltrust | 信任 add-in 中的方法 |
端口口径:COMSOL Multiphysics server 的默认端口是 2036,用 -port <port> 可改;COMSOL Server 的服务端口同样默认 2036,用 -serviceport XXXX 可改,若 2036 被占用会自动顺延到 2037、2038 等。client-server 分离(跨机器)需要 Floating Network License(FNL,浮动网络许可),单机任意许可可以跑 client + server。
命令名的平台差异是必须记住的坑,这里一次性列清(同一份脚本在两个平台上的可执行形态不同):
| 批处理运行 | comsolbatch | comsol batch |
| 编译 Java 源文件 | comsolcompile | comsol compile |
| 启动 client | comsolmphclient | comsol mphclient |
| 启动 server | comsolmphserver | comsol mphserver |
Windows 版命令位于安装目录的 bin\\win64 子目录,需要在命令行里访问时把该路径加入 Windows 的 PATH;Linux 版 comsol 命令位于安装目录的 bin 目录。每个启动器都有对应的 .ini 文件(comsol.ini、comsolbatch.ini 等),可以在其中预置选项——批量管线里常用的做法就是把 -np(核数)一类的固定参数写进 ini,而不是每次命令行传参。
# Linux 示例(COMSOL 6.3 安装在 /usr/local/comsol63/multiphysics)
ComsolDir=/usr/local/comsol63/multiphysics
export LD_LIBRARY_PATH=\\
$ComsolDir/lib/glnxa64:\\
$ComsolDir/lib/glnxa64/gcc:\\
$ComsolDir/ext/graphicsmagick/glnxa64:\\
$ComsolDir/ext/cadimport/glnxa64:\\
$LD_LIBRARY_PATH
macOS 上变量名为 DYLD_LIBRARY_PATH,ComsolDir 形如 /Applications/COMSOL63/Multiphysics。MPh 的默认行为与这条口径直接相关:mph.start() 不带端口参数时创建 stand-alone client(单进程内,更轻量);带端口参数则变成 thin client 连服务器。但 MPh 文档明确:Linux/macOS 上 stand-alone 模式开箱不可用(会抛 java.lang.UnsatisfiedLinkError,因为找不到所需的外部共享库),因此 mph.start() 默认走 client-server 模式(配置项 session 默认值 'client-server',可设为 'stand-alone' 或 'platform-dependent')。这就是"同一份 MPh 代码在 Windows 上跑得比 Linux 上快"的真实原因之一——不是算法差异,是宿主形态差异。
为什么这对你重要:端口和许可决定了脚本能不能在共享机器上跑。单机任意许可可以本地起 server 再连;跨机器的 client-server 分离需要 Floating Network License(FNL,浮动网络许可)。批处理许可(batch license)还需显式加 -usebatchlic,前提是持有 batch licenses。这些边界在第 4 篇展开,本篇只要求你记住:2036 是默认值,跨机器要先确认许可类型。
二、完整代码与逐行剖析
三段代码分别对应三条路径:Java 编译路线(需要 JDK 与 comsolcompile)、MPh 的 Java 直通层(无编译环节)、MPh 的 Python 封装层(隐藏 tag)。三段做的是同一件事——在 3D 几何里放一个 0.1 m × 0.2 m × 0.5 m 的 Block——这正是"底座唯一"最直观的验证方式:同一逻辑、三种宿主写法、结果完全一致。第四段是命令行环节本身(编译与运行),它没有"代码"形态,但属于同一路线的必经步骤。
2.1 Java 最小可运行代码(编译路线)
下面这段是 COMSOL API 的 “Hello World”:在 3D 几何里放一个 0.1 m × 0.2 m × 0.5 m 的 Block。它必须带类头,因为它是 .java 源文件、要走编译环节。
import com.comsol.model.*; // COMSOL API 核心包:Model、ModelEntity、feature 等接口
import com.comsol.model.util.*; // 工具包:ModelUtil(静态方法,管理模型)
public class HelloWorld { // 类名必须与文件名 HelloWorld.java 一致——Java 规则
public static void main(String[] args) {
run(); // 入口只调用 run():把建模逻辑与入口分离,便于被外部代码复用
}
public static Model run() {
Model model = ModelUtil.create("Model"); // 创建模型;"Model" 是模型的 tag
model.modelNode().create("comp1"); // 创建 component(组件)节点,tag = comp1
model.geom().create("geom1", 3); // 在 comp1 下创建几何,tag = geom1,第二个参数 3 = 3D
model.geom("geom1").feature().create("blk1", "Block"); // 在 geom1 里创建一个 feature:tag = blk1,类型 = Block
// 关键决策点:size 传的是字符串数组,不是 double 数组。
// 因为 COMSOL 里任何数值位置都可以写表达式,API 用字符串承载它,
// 这样 "0.1" 与 "0.1*2[cm]" 走的是同一条解析路径。
model.geom("geom1").feature("blk1").set("size", new String[]{"0.1", "0.2", "0.5"});
model.geom("geom1").run("fin"); // 执行几何 finalize,等价于 GUI 的 Build All
return model;
}
}
逐段理解要点(这一段是全套 API 的最小语法骨架,后续 19 篇都在它上面扩展):
- ModelUtil.create(tag) 是静态方法(ModelUtil 是私有构造、不可实例化的静态类),返回 Model 对象。官方 API 文档对它的说明里有一条值得注意:如果已存在同 tag 的模型,那个模型会被移除——除非它正被另一个 client 使用,此时抛异常。这就是"同一进程只能一个 client"这条铁律在 API 层的体现。
- model.modelNode().create("comp1") 与 model.geom().create("geom1", 3) 的分工值得注意:前者创建的是模型树上的 component 节点,后者创建的是这个 component 关联的几何。一个模型可以有多个 component,每个 component 可以有各自的几何——这就是 tag 存在的必要性。
- model.geom("geom1") 对应 GUI 里"右键 geom1",feature().create("blk1", "Block") 对应"从弹出菜单选 Block"。这种一一对应就是 1.1 节说的"GUI 每个操作都是一次 API 调用"。
- set("size", new String[]{…}) 用字符串数组承载数值,这一点在入门阶段最反直觉。原因不是历史包袱,而是 COMSOL 允许在任何数值输入位置写表达式;API 必须用同一种类型承载"纯数值"和"表达式",所以统一用字符串。这也意味着 "0.1" 与 "0.1*2[cm]" 走同一条解析路径——后者带显式单位,前者按模型默认单位解释(这是第 7 篇单位系统篇的核心陷阱)。
- run("fin") 不是可选装饰。不执行 finalize,几何只是"待建"状态,后续网格与求解会拿到不完整的数据。它等价于 GUI 的 Build All 按钮。
编译与运行(Windows):
comsolcompile -jdkroot PATH_TO_JDK HelloWorld.java
comsolbatch -inputfile HelloWorld.class -outputfile HelloWorld.mph
Linux/macOS 对应写法是 comsol compile HelloWorld.java 与 comsol batch -inputfile HelloWorld.class。-jdkroot 指向 JDK 安装目录;comsolcompile 会自动把 COMSOL API 路径设置给编译器,这是它相对裸 javac 的全部价值。
这里还有一个值得早知道的口径:命令行路线的输入是 .mph 或 .class,两者可互换但产物形态不同——.class 是编译产物、可被 COMSOL Desktop 通过 File > Open 打开,.mph 是模型文件。若管线里涉及 Model Manager 的模型版本位置(dbmodel:/// URI),编译产物不能与它混用(这条口径在第 16 篇完整展开)。
最后补一条本篇口径:ModelUtil 是静态类(私有构造、不可实例化),它内部有三个实现委托——clientImpl、serverImpl、methodExecImpl。这三个字段名本身就是"四个宿主"的机器证据:同一个静态类,按宿主不同走不同委托。3.1 节那条解密报错之所以发生在运行期而不是编译期,正是因为编译只需要类定义、运行期需要具体委托的实现——委托找不到实现,底座就不完整。
2.2 MPh 等价代码(无编译环节)
同一件事,Python 侧写法。注意 pymodel 是 MPh 的 Python 包装对象,model 是它下面的 Java 对象——这样写是为了把功能代码原样照搬:
import mph
client = mph.start() # 启动 COMSOL client(Java 桥);约需十秒
pymodel = client.create('Model') # Python 层对象,对应 ModelUtil.create('Model')
model = pymodel.java # 直通 Java API:这一层就是 com.comsol.model.Model
model.modelNode().create("comp1")
model.geom().create("geom1", 3)
model.geom("geom1").feature().create("blk1", "Block")
# 关键决策点:Java 的 new String[]{…} 在这里必须是 Python list。
# JPype 需要的是"三个字符串的列表";Python 没有 new 关键字,
# 直接传 list 才能被正确转换成 String[]。
model.geom("geom1").feature("blk1").set("size", ["0.1", "0.2", "0.5"])
model.geom("geom1").run("fin")
pymodel.save('model') # 落盘为 model.mph,可被 GUI、Java、MATLAB 项目打开
两段代码的函数部分是逐行相同的(连分号都被 Python 忽略),差异只有三处:Java 的类头与 main()、new String[]{…} → list、以及保存方式。这就是"同一底座"最直观的证据。
更 Python 化的写法(隐藏 tag,用 name 引用):
import mph
client = mph.start()
model = client.create('block of ice') # 模型名 = name,不是 tag
model.create('geometries', 3) # 创建几何组,3 = 3D
model.create('geometries/Geometry 1', 'Block') # 在几何下创建 Block
model.property('geometries/Geometry 1/Block 1', 'size', ('0.1', '0.2', '0.5'))
model.build('Geometry 1') # 触发几何构建
两种写法选哪种?如果脚本要跨模型复用、且节点可能被删除重建,用 name 写法(tag 会变,name 不会);如果要照搬官方 Java 代码或调用 MPh 未封装的 API,用 .java 直通写法。
mph.start() 的启动成本也要有预期:官方文档给出约十秒量级。这决定了 MPh 脚本的正确形态——client 启动一次、模型加载一次、在循环里改参数与求解,而不是每个参数值都新开一个 client。需要多核并行时,用 multiprocessing 开多个进程,每个 worker 内部各自 mph.start(cores=1),用队列分发任务与收集结果。
经验法则:MPh 的 Python 封装层适合"读参数、改参数、求解、取值、落盘"这条主链路;模型树的复杂导航与 MPh 未覆盖的节点操作,走 .java 直通层。两层混用不是妥协,而是 MPh 的官方推荐用法。
2.3 跨宿主复制代码的三处必改点
| new String[]{"0.1","0.2","0.5"} → ["0.1","0.2","0.5"] | Python 无 new;JPype 期望 list 才能转成 String[] |
| int[] 参数(如 set("plistarr", new int[]{}))→ list | 同上,JPype 类型转换 |
| Model model = ModelUtil.create("Model") → pymodel = client.create('Model') | MPh 的 client 是入口,不能在同一进程里再 mph.start() 第二个 |
第三处是铁律 9 的直接后果:COMSOL API 不支持同时运行多个 client,而 JPype 不能在同一 Python 进程里管理多个 JVM。要并行只能开多个 Python 进程,每个进程一个 client(每个 worker 用 mph.start(cores=1))。
反直觉点集中列一遍,供自查:
三、常见报错与排查
六条问题按"现象 → 根因 → 解法"三段式列出,全部来自官方文档与官方知识库记录的真实问题。本篇引用三条铁律(铁律 1、2、9,全系列铁律不超过 10 条),完整调试方法(dir()、mph.inspect()、32/64 位与 JPype 类型问题)在第 15 篇展开,本篇只覆盖与执行边界直接相关的部分。
3.1 现象:IDE 里编译通过、运行时抛 javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
- 现象:在 Eclipse/NetBeans 里能编译,运行时报解密长度错误,加不加 ModelUtil.initStandalone(false) 都一样。
- 根因:编译时 classpath 里缺 COMSOL 的 JAR(或混用了不同版本的 JAR),运行期拿不到正确的资源/类实现,ModelUtil 的 clientImpl/serverImpl 委托找不到对应实现,于是解析模型数据时解密失败。这条报错的本质是"编译能过、运行期底座不完整"——编译器只需要类定义,运行期需要完整实现,两者来源不同。
- 解法:把 COMSOL 安装目录 plugins 下的全部 JAR 加入 build path;或干脆用 comsolcompile(它自动设置 COMSOL API 路径),不在 IDE 里手工拼 classpath。IDE 路线还需要 Java 版本兼容设置(Method Editor 自 6.3 起支持 Java 11)。
3.2 现象:Linux/macOS 上 mph.start() 抛 java.lang.UnsatisfiedLinkError
- 现象:Windows 上正常,Linux/macOS 上启动 client 即失败。
- 根因:stand-alone 模式在 Unix 类系统上需要外部共享库,MPh 找不到它们。
- 解法:把 COMSOL 的共享库目录加入 LD_LIBRARY_PATH(Linux)或 DYLD_LIBRARY_PATH(macOS);或让 MPh 走 client-server 模式(默认即为 'client-server')。注意 client-server 模式在频繁访问 API(如递归遍历模型树)时会明显慢于 stand-alone,但求解时间不受影响。
3.3 现象:同一 Python 进程里第二次 mph.start() 失败
- 现象:第一次 client 正常,第二次创建 client 报错;或在脚本里反复 start 导致会话崩溃。
- 根因:COMSOL API 不支持同一 Java 程序里多个 client,JPype 不支持同一 Python 进程里多个 JVM——两者叠加,MPh 的 Client 类不能被实例化第二次。
- 解法:一个进程一个 client;需要并行就开多个进程(multiprocessing),每个 worker 内部各自 mph.start(cores=1)。另外别指望捕获 KeyboardInterrupt:JPype 关闭 JVM 的已知问题会让 Ctrl+C 通常直接崩掉 Python 会话。
3.4 现象:命令行跑批处理时参数扫描节点被忽略
- 现象:GUI 里跑正常,命令行跑完输出文件里没有扫描结果。
- 根因:comsol batch 默认忽略 Batch Sweep 与 Cluster Sweep 节点——这两类节点需要启动外部进程,批处理进程默认不允许自己派生子进程。这条口径在第 10 篇完整展开。
- 解法:加 -mode desktop 允许批处理进程启动外部进程(Windows 写作 comsolbatch.exe -mode desktop -inputfile tuning_fork.mph -outputfile tuning_fork_out.mph,Linux/macOS 写作 comsol batch -mode desktop …)。若要用命令行指定参数集合,改用 -pname/-plist 或 -paramfile,但这种方式每个参数值生成一个独立输出文件、解不同步到单文件;要拿到"所有外部任务的解同步到单文件"的输出,必须走 -mode desktop 并在模型里勾选 Synchronize solutions。三种扫描形态的完整对照在第 10 篇展开。
3.5 现象:录制或另存为 Java 文件后代码里全是冗余操作
- 现象:导出的 .java 里出现大量已删除、已移动节点的残留代码。
- 根因:模型历史(model history)记录了会话中每一步操作,包括误操作;Save As → Model File for Java 会把历史整体导出。
- 解法:导出前执行 File > Compact History(清理历史、去重、按 Model Builder 顺序重排)。但注意:开发过程中"不压缩历史"有时是有用的——压缩前保存,改动会集中在代码末尾,更容易定位。批量脚本里则必须关掉历史(model.hist().disable() / resetHist()),否则历史会无限膨胀——这条口径在第 19 篇完整展开。
3.6 现象:同一份 MPh 脚本在 Windows 上快、在 Linux/macOS 上慢很多
- 现象:求解时间一样,但遍历模型树、读属性这类操作慢一个数量级。
- 根因:MPh 在 Windows 上默认可以走 stand-alone client(一切在同一进程内),在 Linux/macOS 上因共享库找不到而默认走 client-server 模式(thin client 经回环网络套接字连服务器),每次 API 调用都要走进程间通信。
- 解法:在 Linux/macOS 上把 COMSOL 共享库目录写入 LD_LIBRARY_PATH / DYLD_LIBRARY_PATH 后改用 stand-alone(mph.option('session', 'stand-alone')),或接受 client-server 的开销。经验法则:脚本里尽量减少逐节点访问(用批量接口、缓存属性),比换模式更划算——但求解时间本身不受影响,所以这条只影响"导航型"脚本。
四、动手练习
练习 1:用 Java Shell window 建几何并回跳树节点(仅 Windows)
步骤:Home 选项卡 → Layout 组 → Windows 菜单 → Java Shell;粘贴 2.1 节 run() 体内的代码(不带类头),Run。
判定成功标准(客观):
练习 2:MPh 与 Java 双版本对照验证
步骤:在 Python 里跑 2.2 节代码,保存 model.mph;再用 COMSOL Desktop 打开该文件。
判定成功标准(客观):
练习 3:命令行编译与运行(跨平台口径练习)
步骤:把 2.1 节保存为 HelloWorld.java,用 comsolcompile -jdkroot PATH_TO_JDK HelloWorld.java 编译,再用 batch 命令加载 .class。
判定成功标准(客观):
思考题(无标准答案,附验证要点清单):为什么 Method Editor 必须"自动编译",而命令行路线允许把编译环节交给你?对照检查这三点:① app 的最终用户是否可能没有 JDK;② .class 产物能否被 COMSOL Desktop 直接打开;③ 集成进现有 Java 代码库时编译环节归谁控制。
思考题二(无标准答案,附验证要点清单):如果一段代码需要同时在 Windows 与 Linux 上运行,四条路径里哪两条是安全的?对照检查这三点:① 该路径是否声明"仅 Windows";② MPh 在非 Windows 平台默认走哪种会话模式(mph.option('session') 的默认值);③ 依赖共享库的环境变量(LD_LIBRARY_PATH / DYLD_LIBRARY_PATH)是否已经配置。
五、小结与下一篇预告
本篇的核心是四句话:COMSOL API(com.comsol.model 包)是唯一底座;四条执行路径是同一底座的四个宿主;边界由"是否需要 JDK 编译环节、是否仅限 Windows、同一进程能否开第二个 client"三件事决定;GUI 的每个操作都对应一次 API 调用,Record Method 与 Copy as Code to Clipboard 只是把这个映射显性化。
本篇引用的铁律:铁律 1(API 底座唯一,跨宿主复制代码必须处理类型差异)、铁律 2(tag 是主键,用 name/label 引用可规避)、铁律 9(同一进程只能一个 client,多核并行必须多进程)。顺带点到的口径(完整表述在对应篇):命令行默认忽略 Batch/Cluster Sweep(第 10 篇)、.class 不与 dbmodel:/// 混用(第 16 篇)、批量脚本必须关历史(第 19 篇)。
下一篇(第 2 篇):从空白模型到 runAll 的第一个完整可运行脚本——完整 1D 热传导脚本逐行剖析、clearModel、模型历史与 tag 主键的实际后果。编译链路的完整细节(Eclipse 配置、ModelUtil.initStandalone(false)、External Class 在研究中的调用)在第 14 篇详述;MPh 的 CSV 驱动与数据管线在第 8 篇详述;报错排查的完整方法(dir()、mph.inspect()、32/64 位与 JPype 类型问题)在第 15 篇详述。
本篇认知问题回显(FAQ)
Q1:COMSOL 二次开发有几套 API?Method Editor、MPh、LiveLink 是不是不同接口?
A:只有一套。COMSOL API 就是 com.comsol.model 包,Application Builder 方法、Java Shell、LiveLink for MATLAB、LiveLink for Excel、MPh 都操作同一批支撑用户界面的类和数据结构,差别只在宿主形态。
Q2:COMSOL 的 Method Editor 和 Java Shell window 有什么区别?都能跑 Java 代码,选哪个?
A:Method Editor 产出可复用的方法、可绑定 app 事件;Java Shell window 是 6.3 引入的命令提示符、粘贴即执行且输出对象可双击跳回模型树节点。两者都仅 Windows 可用。
Q3:为什么 comsolcompile 还需要 JDK?Method Editor 不是自动编译吗?
A:Method Editor 把编译环节藏在宿主里(仅 Windows),命令行路线要求显式用 JDK 把 .java 编译成 .class;comsolcompile 的价值是自动为 Java 编译器设置 COMSOL API 路径,免去手工拼 plugins 下的 JAR classpath。
Q4:MPh 为什么不用安装 Java?它是另一套 COMSOL API 吗?
A:MPh 经 JPype 桥接访问 COMSOL 的 Java API,COMSOL 自带 JRE 所以无需另装 Java、也无需 comsolcompile。它不是另一套 API:.java 属性直通 ModelUtil 与 model,完整 Java API 都可用。
Q5:Java 代码复制到 MPh(Python)时哪些写法必须改?为什么 new String[]{…} 不行?
A:Python 没有 new 关键字,JPype 需要 list 才能转成 String[],所以 new String[]{"0.1","0.2","0.5"} 必须写成 ["0.1","0.2","0.5"];另外同一 Java/Python 进程只能有一个 COMSOL client,不能重复调用 mph.start()。
Q6:COMSOL server 的默认端口是多少?client-server 分离需要什么许可?
A:默认端口是 2036(server 用 -port、COMSOL Server 服务端口用 -serviceport 可改,被占用会顺延到 2037)。跨机器的 client-server 分离需要 Floating Network License(FNL),单机任意许可可跑 client + server。
Q7:MPh 里为什么建议用 name/label 而不是 tag 引用节点?
A:tag 是实现细节且不能自由修改——feature 删除后重建可能拿到不同 tag,引用它的代码要同步改;name/label 同样被强制唯一,所以按名字引用更稳,这也是 MPh 刻意隐藏 tag 的原因。
Q8:Method Editor 和 Java Shell window 为什么只在 Windows 可用?同一份 MPh 代码在 Windows 和 Linux 上性能为什么不同?
A:Method Editor 与 Java Shell window 是官方明确仅 Windows 版可用的功能,Linux/macOS 只能走命令行与 MPh。MPh 在 Linux/macOS 上因 stand-alone 模式不可用而默认走 client-server 模式,频繁访问 API 时明显慢于 Windows 的 stand-alone,但求解时间不受影响。
网硕互联帮助中心





评论前必须登录!
注册