您有没有想过在 COMSOL Multiphysics® 软件中自定义或创建一个基于 Excel® 电子表格数据的模型?许多科学和工程应用中都采用 Excel 电子表格来呈现、协作和存储数据。借助 COMSOL 接口产品 Microsoft® Visual Basic® for Applications(VBA)和 LiveLink™ for Excel®,我们可以在 Excel® 电子表格软件中创建模型和设置参数。这篇博客,我们将演示如何实现这个过程,并列举了一些示例。
编者注: 这篇博客更新于 2024 年 6 月 20 日, 以反映 COMSOL Multiphysics® 软件 6.0 版本的功能和特性。
在仿真中使用 LiveLink™ for Excel® 和 VBA
LiveLink™ for Excel® 是一款接口产品,允许您将 Excel 数据连接到 COMSOL Multiphysics。如果您是 LiveLink™ for Excel® 的新用户,可以阅读以下文档(安装软件后访问)开始使用:
- LiveLink™ for Excel® 简介
- LiveLink™ for Excel® 用户指南
Excel® 电子表格软件还提供了在 Excel® 工作簿中定义和运行 VBA 的功能。虽然 VBA 脚本可以手动编写 ,但也可以使用 COMSOL Multiphysics® 的用户界面由现有模型中生成。正如些示例的帮助文档中所显示的,通过 LiveLink™ for Excel® 可以轻松地实现 VBA 的使用。接下来,我们将通过一个常见的应用来看看如何检索和更新 COMSOL Multiphysics 模型中的参数。
这个功能很有用,但 VBA 和 LiveLink™ for Excel® 还有更多的用途。例如,我们将了解如何构建 COMSOL Multiphysics 模型并使用 Excel® 工作簿中插入的一些基本形状定义模型几何。
注意:此处讨论的示例是使用 Excel® 2019 版本显示的,但在其他版本中的操作是一样的。
使用 VBA 可以为组件对象模型 (Component Object Model ,COM) 提供接口。当您安装 LiveLink™ for Excel® 后,还会安装一个 COM 接口组件,用于和 COMSOL Multiphysics® 连接。COMSOL Multiphysics Server 和 COMSOL Multiphysics 模型之间两个用作接口的基本 COM 对象是:
comsolcom.comsolutil comsolcom.modelutil
通过使用 comsolcom.comsolutil
,可以启动 COMSOL Multiphysics Server、连接和断开与服务器的连接。使用 comsolcom.modelutil
,我们可以连接 COMSOL Multiphysics 模型。
使用 VBA 编辑器
借助随 Excel®电子表格软件安装的编辑器,我们可以在 Excel® 工作簿中编写和编辑 VBA 脚本。可以通过几种不同的方式访问编辑器窗口。例如,如果我们右键单击 Excel® 工作表选项卡并选择 View Code,则会显示编辑器。如果我们创建或编辑宏,也会显示编辑器。还可以在 Excel® 电子表格软件的工具栏中启用 开发工具 选项卡,其中包含用于访问编辑器和其他开发相关功能的按钮。
在 VBA 中访问 COM 组件
我们可以使用下面的声明在 VBA 中创建 comsolcom.comsolutil
和 comsolcom.modelutil
对象的动态实例。
设置 comsolutil = CreateObject("comsolcom.comsolutil") 设置 modelutil=CreateObject("comsolcom.modelutil")
这个声明的优点是版本独立。最新安装的 comsolcom.comsolutil
和 comsolcom.modelutil
是在运行时使用。
也可以使用下面含静态 COM 引用的声明 comsolcom.comsolutil
和 comsolcom.modelutil
Dim comsolutil As comsolutil Set comsolutil = CreateObject("comsolcom.comsolutil") Dim modelutil As modelutil Set modelutil = CreateObject("comsolcom.modelutil")
使用此声明的一个优点是,在使用定义的类型时,可以在 VBA 中获得帮助。
为了能够为 comsolutil
和 modelutil
定义静态类型,我们必须向 ComsolCom
添加一个 COM. 我们可以通过在 Excel® 电子表格软件中打开 VBA 编辑器,选择 Tools 菜单,选择 References,然后为已安装的版本选择 ComsolCom
来实现。
使用 VBA 启动 COMSOL Multiphysics Server,连接和断开连接
以下简短的 VBA 脚本说明了如何启动 COMSOL Multiphysics Server,连接到已启动的服务器,以及断开与服务器的连接。行调用 comsolutil.TimeOutHandle(True)
应用超时处理程序,该处理程序将等待长时间运行的命令返回给 Excel®。
Set comsolutil = CreateObject("comsolcom.comsolutil") Set modelutil = CreateObject("comsolcom.modelutil") 调用 comsolutil.TimeOuthandler(True) 调用 comsolutil.StartComsolServer(True) 调用 modelutil.connect 调用 modelutil.Disconnect
从 COMSOL API 迁移使用 Java® 和应用程序方法
如果你有将 COMSOL API 用于 Java® 或在应用程序方法中编写代码的经验,那么你应该了解一下语法差异。例如,在检索模型特征列表时,模型中的研究语法类似。因此,为了检索模型中的研究,以下语法有效:
model.study()
但是,在访问特定研究时,语法是不同的。例如,当 std1
使用 COMSOL API 检索带有研究标签 std,以与 Java® 或应用程序中的代码一起使用时,语法 model.study("std1")
有效。但是,对于 VBA® 和 LiveLink™ for Excel®,必须改用以下语法:
model.get_study("std1")
COMSOL Multiphysics 模型中的接口参数
VBA 和 LiveLink™ for Excel® 中的一个常见应用是检索和更新 COMSOL Multiphysics 模型中的参数。接下来,我们将看到如何轻松实现。
下图中的 VBA 脚本启动了 COMSOL Multiphysics server,连接到启动的服务器,从与 Excel®工作簿相同的目录中加载了母线板焦耳热(使用 LiveLink™ for Excel®)模型,使用更新的长度参数求解模型, 以及用另一个文件名保存更新后的模型。
下图显示的 VBA 脚本提取了模型中参数的参数数据并将它们插入到了 Excel® 工作簿中。
如何使用 Excel® 和 Visual Basic® 构建 COMSOL Multiphysics 模型
在接下来的一个示例中,我们将创建一个 COMSOL Multiphysics 模型并使用 固体传热 接口求解一个二维模拟。这个过程包括在 Excel® 中通过添加带有一些说明的文本框、外部温度边界、内部温度边界和用于求解模拟的按钮来定义几何。模型求解后,结果图会插入 Excel® 工作簿中。下面给,我们来详细介绍一下这些步骤。
1. 首先,创建一个带有说明文本的文本框并将其插入到 Excel® 工作簿中。
2. 然后,为模拟定义一个区域。我们选择一个自由形状并将其插入 Excel® 工作簿中。然后,选择 SimulationRegion 作为形状的名称。通过右键单击形状并选择 Edit Polygon 使多边形可编辑。然后编辑形状,如下所示。
3. 创建了一个温度更高的内部边界。为此,我们使用椭圆形状,创建一个圆形,并将其插入自由形状中。选择 HeatSource 作为形状的名称。椭圆形状必须位于 Simulation Region 形状内。
4. 然后添加一个带有文本 Solve 的文本框形状以用作按钮。右键单击该按钮,选择 Assign Macro,然后创建一个名为 Solve_Click 的新宏。
5. 接下来,我们在 VBA 编辑器中打开指定的宏并用以下脚本替换内容:
Option Explicit Sub Solve_Click() Dim node Dim coordinates Dim index Dim newPolygonTable() As Double Dim newHeatSource(1 To 2) As Double Dim model As ModelImpl newHeatSource(1) = Sheets("Sheet1").Shapes("HeatSource").DrawingObject.Left + (Sheets("Sheet1").Shapes("HeatSource").DrawingObject.Width / 2) newHeatSource(2) = Sheets("Sheet1").Application.Height - (Sheets("Sheet1").Shapes("HeatSource").DrawingObject.Top + (Sheets("Sheet1").Shapes("HeatSource").DrawingObject.Height / 2)) Dim nNodes As Long nNodes = Sheets("Sheet1").Shapes("SimulationRegion").Nodes.Count ReDim Preserve newPolygonTable(1 To nNodes, 1 To 2) For Each node In Sheets("Sheet1").Shapes("SimulationRegion").Nodes coordinates = node.points index = index + 1 newPolygonTable(index, 1) = coordinates(1, 1) newPolygonTable(index, 2) = Sheets("Sheet1").Application.Height - coordinates(1, 2) Next Set model = SetModel(newPolygonTable, newHeatSource) Call model.get_study("std1").Run If Not ContainsTag(model.result().tags(), "pg1") Then Call model.result().Create("pg1", "PlotGroup2D") Call model.get_result("pg1").feature().Create("surf1", "Surface") Call model.get_result("pg1").Label("Temperature (ht)") Call model.get_result("pg1").set("data", "dset1") Call model.get_result("pg1").get_feature("surf1").Label("Surface") Call model.get_result("pg1").get_feature("surf1").set("colortable", "ThermalLight") Call model.get_result("pg1").get_feature("surf1").set("data", "parent") Call model.get_result("pg1").get_feature("surf1").Run End If Call model.get_result("pg1").Run Call Range("J10").Select Dim tempPng As String tempPng = Environ("Temp") & "\PolygonHeat" & Format(Now(), "yyyymmddhhmmss") & ".png" Dim exportTag As String exportTag = model.result().Export.uniquetag("export") Call model.result().Export().Create(exportTag, "Image2D") Call model.result().get_export(exportTag).set("plotgroup", "pg1") Call model.result().get_export(exportTag).set("pngfilename", tempPng) Call model.result().get_export(exportTag).Run If Dir(tempPng) "" Then Call Application.ActiveSheet.Pictures.Insert(tempPng) SetAttr tempPng, vbNormal Kill tempPng End If Call model.result().Export().Remove(exportTag) End Sub Private Function SetModel(ByRef newPolygonTable() As Double, ByRef newHeatSource() As Double) As Variant Dim comsolutil As comsolutil Set comsolutil = CreateObject("comsolcom.comsolutil") Dim modelutil As modelutil Set modelutil = CreateObject("comsolcom.modelutil") Dim model As ModelImpl If Not IsConnected(modelutil) Then Call ConnectServer(comsolutil, modelutil) End If If Not ContainsTag(modelutil.tags(), "PolygonHeatModel") Then Set SetModel = CreateModel(modelutil, "PolygonHeatModel", newPolygonTable, newHeatSource) Exit Function End If Set model = modelutil.model("PolygonHeatModel") Call model.get_geom("geom1").get_feature("pol1").set("table", newPolygonTable) Call model.get_geom("geom1").get_feature("c1").set("x", newHeatSource(1)) Call model.get_geom("geom1").get_feature("c1").set("y", newHeatSource(2)) Call model.get_geom("geom1").runAll Set SetModel = model End Function Private Function CreateModel(ByRef modelutil As modelutil, ByRef modelTag As String, ByRef newPolygonTable() As Double, ByRef newHeatSource() As Double) As Variant Dim model As ModelImpl Set model = modelutil.Create(modelTag) Call model.ModelNode().Create("comp1") Call model.geom().Create("geom1", 2) Call model.mesh().Create("mesh1", "geom1") Call model.get_geom("geom1").Create("pol1", "Polygon") Call model.get_geom("geom1").get_feature("pol1").set("source", "table") Call model.get_geom("geom1").get_feature("pol1").set("table", newPolygonTable) Call model.get_geom("geom1").Selection().Create("csel1", "CumulativeSelection") Call model.get_geom("geom1").get_feature("pol1").set("contributeto", "csel1") Call model.get_geom("geom1").get_run("pol1") Call model.get_geom("geom1").Create("c1", "Circle") Call model.get_geom("geom1").get_feature("c1").set("r", 0.01) Call model.get_geom("geom1").get_feature("c1").set("x", newHeatSource(1)) Call model.get_geom("geom1").get_feature("c1").set("y", newHeatSource(2)) Call model.get_geom("geom1").Selection().Create("csel2", "CumulativeSelection") Call model.get_geom("geom1").get_feature("c1").set("contributeto", "csel2") Call model.get_geom("geom1").Run Call model.get_geom("geom1").get_run("fin") Call model.Material().Create("mat1", "Common", "comp1") Call model.get_material("mat1").set("family", "copper") Call model.get_material("mat1").get_propertyGroup("def").set("heatcapacity", "385[J/(kg*K)]") Call model.get_material("mat1").get_propertyGroup("def").set("density", "8960[kg/m^3]") Call model.get_material("mat1").get_propertyGroup("def").set("thermalconductivity", "400[W/(m*K)]") Call model.Physics().Create("ht", "HeatTransfer", "geom1") Call model.get_physics("ht").Create("temp1", "TemperatureBoundary", 1) Call model.get_physics("ht").Create("temp2", "TemperatureBoundary", 1) Call model.get_physics("ht").get_feature("temp2").set("T0", "293.15[K]+20") Call model.get_physics("ht").get_feature("temp1").Selection().named("geom1_csel1_bnd") Call model.get_physics("ht").get_feature("temp2").Selection().named("geom1_csel2_bnd") Call model.study().Create("std1") Call model.get_study("std1").Create("stat", "Stationary") Call model.get_study("std1").Run Call model.result().Create("pg1", "PlotGroup2D") Call model.get_result("pg1").Label("Temperature (ht)") Call model.get_result("pg1").set("data", "dset1") Call model.get_result("pg1").feature().Create("surf1", "Surface") Call model.get_result("pg1").get_feature("surf1").Label("Surface") Call model.get_result("pg1").get_feature("surf1").set("colortable", "ThermalLight") Call model.get_result("pg1").get_feature("surf1").set("data", "parent") Set CreateModel = model End Function Private Function IsConnected(modelutil As modelutil) As Boolean 'Try to access model tags. If not connected to a server this will throw an error. On Error GoTo ErrorHandler Call modelutil.tags IsConnected = True Exit Function ErrorHandler: IsConnected = False End Function Private Function ConnectServer(comsolutil As comsolutil, modelutil As modelutil) On Error GoTo ErrorHandler Call modelutil.connect If Not comsolutil.isGraphicsServer() Then MsgBox prompt:="The running COMSOL Multiphysics Server is not a graphics server. Exporting results will not work.", Buttons:=vbOKOnly, Title:="COMSOL" End If Exit Function ErrorHandler: Call comsolutil.TimeOuthandler(True) Call comsolutil.StartComsolServer(True) Call modelutil.connect End Function Private Function ContainsTag(tags() As String, tag As String) As Boolean ContainsTag = False If (UBound(Filter(tags, tag)) > -1) Then ContainsTag = True End If End Function
6. 输入代码后,点击 Solve 按钮。这将执行宏中定义的 VBA 脚本并根据 Excel® 工作簿中的形状创建模型。模型求解后,图形被插入到工作表中。
如果更改 Simulation Region 形状并将 Heat Source 形状移动到 Simulation Region 内的另一个位置,则模型和结果将不同。
很容易想象我们如何根据 Excel® 工作簿中的其他形状、图表和数据来控制和编辑此模型。还可以从 COMSOL Multiphysics 模型中提取数值结果并生成例如用于报告中的 Excel® 工作簿内容。
在 VBA 中使用 COMSOL LiveLink 功能区函数
可以将 LiveLink™ for Excel® 功能区与 COMSOL Multiphysics 模型交互的简便性与使用 VBA 的 COMSOL API 的所有功能结合起来。这可以通过 RibbonUtil 类的函数来实现。LiveLink™ for Excel®功能区上的几乎每个按钮都有相应的 VBA 命令。功能区按钮的工具提示包含简短的 VBA 代码片段,可显示相应的 VBA 功能。
COMSOL 案例库中的 busbar_llexcel.mph 模型是这种方法如何降低通过 VBA 访问 COMSOL Multiphysics 模型的复杂性的最佳示例。在该模型中,由 Update 按钮运行的 VBA 代码结合了 RibbonUtil 函数与通用 API,来加载和修改扫描、运行和更新所有链接结果,并从下图所示的简短代码中提取新的扫描插值数据:
Sub busbarUpdate() Dim ModelUtil As ModelUtil Dim ComsolUtil As ComsolUtil Dim RibbonUtil As IRibbonUtil Set index ModelUtil = CreateObject("comsolcom.modelutil") Set ComsolUtil = CreateObject("comsolcom.comsolutil") Set RibbonUtil = ComsolUtil.GetRibbonUtil ' Allow long running jobs ComsolUtil.TimeOutHandler True ' Open linked model (if ribbon not already connected to the COMSOL server) If Not RibbonUtil.IsConnected Then RibbonUtil.OpenLinkedModel End If ' Create a link with the model with the tag Model in the COMSOL server Set Model = ModelUtil.Model("Model") ' Update model parameter set in A4 Sheets("Sheet1").Activate Range("A4").Select RibbonUtil.UpdateDefinitions ' Keep only the fifth first columns for both sweep parameters Range("G9:J10").Clear ' Update parametric sweep parameters set in A8 Range("A8").Select RibbonUtil.Sweep "std1", , True ' Enable progress bar ModelUtil.ShowProgress True ' Compute solution Model.get_study("std1").Run ' Displa plot group pg3 Model.get_result("pg3").Run Sheets("Sheet1").Range("L4").Select ' Insert graphics of plot group pg3 RibbonUtil.InsertGraphics "pg3" ' Update all numerical results in current sheet RibbonUtil.UpdateAllResults ' Retreive parametric sweep data (for later formating) Vtot = Sheets("Sheet1").Range("B10:F10").Value For I = 0 To 4 If Not IsEmpty(Sheets("Sheet1").Range("B9").Offset(, I).Value) Then wbbLength = I + 1 End If If Not IsEmpty(Sheets("Sheet1").Range("B10").Offset(, I).Value) Then VtotLength = I + 1 End If Next wbb = Sheets("Sheet1").Range(Cells(9, 2), Cells(9, 2 + wbbLength)).Value wbb = Sheets("Sheet1").Range(Cells(10, 2), Cells(10, 2 + VtotLength)).Value ' Clear Sheet2 except interpolation coordinates Sheets("Sheet2").Activate Range("D1:AB21").Delete ' Update interpolation results For I = 0 To wbbLength - 1 Range("D4").Offset(, I * VtotLength).Select RibbonUtil.ResultsInterpolation "dset2", "ht.Qtot", "A4:C21", , "wbb", wbb(1, I + 1) Next ' Set cell format Cells(1, 4) = "Qtot [W]" Cells(1, 4).Font.Bold = True Cells(1, 4).HorizontalAlignment = xlCenter Cell2 = 4 + VtotLength * wbbLength - 1 Range(Cells(1, 4), Cells(1, Cell2)).Merge For I = 0 To wbbLength - 1 Idx = I * wbbLength Title = "wbb = " & wbb(1, I + 1) & "[m]" Cell1 = 4 + VtotLength * I Cell2 = 4 + (I + 1) * VtotLength - 1 Cells(2, Cell1) = Title Cells(2, Cell1).Font.Bold = True Cells(2, Cell1).HorizontalAlignment = xlCenter Range(Cells(2, Cell1), Cells(2, Cell2)).Merge Range(Cells(2, Cell1), Cells(2, Cell2)).Borders.Weight = xlThick For j = 1 To VtotLength Idx2 = I * VtotLength + j - 1 Title = "Vtot = " & Vtot(1, j) & "[m]" Range("d3").Offset(, Idx2).Value = Title Range("d3").Offset(, Idx2).Font.Bold = True Range("d3").Offset(, Idx2).Borders.Weight = xlThick Next Next End Sub
这篇博客仅介绍了可以使用 VBA 和 Excel® 执行的一些简单操作。如果您是 COMSOL 用户,可以通过访问整个 COMSOL API,来访问所有模型设置和参数。这样,就可以定义任何类型的模型并在使用 COMSOL Multiphysics 求解后提取其数据。
Microsoft、Excel 和 Visual Basic 是 Microsoft Corporation 在美国和/或其他国家/地区的注册商标或商标。
Oracle 和 Java 是 Oracle 和/或其附属公司的注册商标。
评论 (0)