云计算百科
云计算领域专业知识百科平台

COMSOL Multiphysics 二次开发系列教程(1):执行边界与三条运行时——同一个 API 底座的四个宿主

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 侧的两个入口。

〇、本篇要解决的认知问题

  • COMSOL 的二次开发接口到底是几套?为什么官方文档说"你在 Application Builder 里写方法、在 MATLAB 里驱动仿真、在 Excel 里跑模型,其实操作的是同一批类和数据结构"?
  • Method Editor 和 Java Shell window 都能跑 Java 代码,差别到底在哪——是编译方式不同,还是"方法"与"即执行"的语义不同?
  • comsolcompile 存在的意义是什么?既然 Method Editor 已经自动编译,为什么命令行路线还多出一个 JDK 编译环节?
  • MPh 为什么不需要我安装 Java?它到底是另一套 API,还是同一套 Java API 的 Python 门面?
  • 同一份代码从 Java 复制到 Python 会踩什么坑?为什么 new String[]{"0.1","0.2","0.5"} 在 Python 里必须写成 list?
  • COMSOL server 的默认端口是多少?client-server 分离需要什么许可?
  • MPh 为什么建议用 name/label 而不是 tag 引用节点?这条取舍和铁律 2 是什么关系?
  • 四条路径在平台上不等价:为什么 Method Editor 和 Java Shell window 只在 Windows 可用?同一份 MPh 代码为什么在 Windows 上比在 Linux/macOS 上更快?
  • 读完这八问,你就有了全系列反复回扣的心智模型:一个底座、四个宿主、边界由"编译环节 + 平台 + 进程"三件事决定。

    一、机制解析

    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 EditorJava Shell window
    产物 方法(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 二次开发"路线,也是四条里唯一多出一个编译环节的:

  • 用 JDK 把 .java 源文件编译成 .class(字节码);
  • 用 batch 命令加载 .mph 或 .class 运行。
  • 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> 形式):

    命令(Linux/macOS 形式)Windows 形式作用
    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。

    命令名的平台差异是必须记住的坑,这里一次性列清(同一份脚本在两个平台上的可执行形态不同):

    用途Windows 命令Linux/macOS 命令
    批处理运行 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 跨宿主复制代码的三处必改点

    从 Java 到 Python 的写法差异为什么必须改
    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))。

    反直觉点集中列一遍,供自查:

  • 数值用字符串承载。set("size", new String[]{"0.1","0.2","0.5"}) 不是历史遗留,而是 COMSOL 允许任何数值位置写表达式,API 必须用同一类型承载"纯数值"与"表达式"。Python 侧必须传 list 或 tuple(MPh 官方示例两种都可用),但不能传其他形式——JPype 需要能转成 String[] 的形态。
  • tag 与 name 是两套主键。Java 路线用 tag(geom1、blk1),MPh 封装层用 name/label(Geometry 1、Block 1)。同一个节点两种叫法,混用会报错。
  • 一个进程一个 client。这不是 MPh 的限制,是 COMSOL API 的限制;JPype 只是让"绕过它"变得不可能。
  • 录制代码不等于干净代码。模型历史包含误操作,Compact History 才让导出代码接近可读——但开发过程中故意不压缩有时更好,因为改动集中在末尾。
  • 三、常见报错与排查

    六条问题按"现象 → 根因 → 解法"三段式列出,全部来自官方文档与官方知识库记录的真实问题。本篇引用三条铁律(铁律 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。

    判定成功标准(客观):

  • Model Builder 树上出现 geom1 与 blk1 节点;
  • 在 Java Shell 输出里双击该对象标签,能跳到树上对应节点(或工具栏 Go to Node 生效);
  • 几何被 finalize(run("fin") 后 blk1 状态为已构建,非"待建")。
  • 练习 2:MPh 与 Java 双版本对照验证

    步骤:在 Python 里跑 2.2 节代码,保存 model.mph;再用 COMSOL Desktop 打开该文件。

    判定成功标准(客观):

  • 生成的 model.mph 能在 GUI 打开,几何里恰好一个 Block;
  • model.property('geometries/Geometry 1/Block 1', 'size') 读回三个值与写入值一致(0.1 / 0.2 / 0.5);
  • 同一进程里尝试第二次 mph.start(),得到"不能重复实例化 Client"类错误——这即铁律 9 的可观察证据。
  • 练习 3:命令行编译与运行(跨平台口径练习)

    步骤:把 2.1 节保存为 HelloWorld.java,用 comsolcompile -jdkroot PATH_TO_JDK HelloWorld.java 编译,再用 batch 命令加载 .class。

    判定成功标准(客观):

  • 当前目录出现 HelloWorld.class;
  • batch 运行后输出 .mph 文件,打开后几何与练习 1 完全一致;
  • 能说出三处平台差异:comsolcompile vs comsol compile、comsolbatch vs comsol batch、以及 -port(server)与 -serviceport(COMSOL Server)都默认 2036。
  • 思考题(无标准答案,附验证要点清单):为什么 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,但求解时间不受影响。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » COMSOL Multiphysics 二次开发系列教程(1):执行边界与三条运行时——同一个 API 底座的四个宿主
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!