您有没有想过在 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 ® 是一款接口产品,允许您将 Excel 数据连接到 COMSOL Multiphysics。如果您是 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 模型。
借助随 Excel ® 电子表格软件安装的编辑器,我们可以在 Excel ® 工作簿中编写和编辑 VBA 脚本。可以通过几种不同的方式访问编辑器窗口。例如,如果我们右键单击 Excel ® 工作表选项卡并选择 View Code ,则会显示编辑器。如果我们创建或编辑宏,也会显示编辑器。还可以在 Excel ® 电子表格软件的工具栏中启用 开发工具 选项卡,其中包含用于访问编辑器和其他开发相关功能的按钮。
我们可以使用下面的声明在 VBA 中创建
comsolcom.comsolutil
和
comsolcom.modelutil
对象的动态实例。
这个声明的优点是版本独立。最新安装的
comsolcom.comsolutil
和
comsolcom.modelutil
是在运行时使用。
也可以使用下面含静态 COM 引用的声明
comsolcom.comsolutil
和
comsolcom.modelutil
使用此声明的一个优点是,在使用定义的类型时,可以在 VBA 中获得帮助。
为了能够为
comsolutil
和
modelutil
定义静态类型,我们必须向
ComsolCom
添加一个 COM. 我们可以通过在 Excel
®
电子表格软件中打开 VBA 编辑器,选择
Tools
菜单,选择
References
,然后为已安装的版本选择
ComsolCom
来实现。
以下简短的 VBA 脚本说明了如何启动 COMSOL Multiphysics Server,连接到已启动的服务器,以及断开与服务器的连接。行调用
comsolutil.TimeOutHandle(True)
应用超时处理程序,该处理程序将等待长时间运行的命令返回给 Excel
®
。
如果你有将 COMSOL API 用于 Java® 或在应用程序方法中编写代码的经验,那么你应该了解一下语法差异。例如,在检索模型特征列表时,模型中的研究语法类似。因此,为了检索模型中的研究,以下语法有效:
model.study()
但是,在访问特定研究时,语法是不同的。例如,当
std1
使用 COMSOL API 检索带有研究标签 std,以与 Java® 或应用程序中的代码一起使用时,语法
model.study("std1")
有效。但是,对于 VBA
®
和 LiveLink™
for
Excel
®
,必须改用以下语法:
VBA 和 LiveLink™ for Excel ® 中的一个常见应用是检索和更新 COMSOL Multiphysics 模型中的参数。接下来,我们将看到如何轻松实现。
下图中的 VBA 脚本启动了 COMSOL Multiphysics server,连接到启动的服务器,从与 Excel ® 工作簿相同的目录中加载了 母线板焦耳热(使用 LiveLink™ for Excel ® ) 模型,使用更新的长度参数求解模型, 以及用另一个文件名保存更新后的模型。
下图显示的 VBA 脚本提取了模型中参数的参数数据并将它们插入到了 Excel ® 工作簿中。
在接下来的一个示例中,我们将创建一个 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) 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 Function6. 输入代码后,点击 Solve 按钮。这将执行宏中定义的 VBA 脚本并根据 Excel ® 工作簿中的形状创建模型。模型求解后,图形被插入到工作表中。
如果更改 Simulation Region 形状并将 Heat Source 形状移动到 Simulation Region 内的另一个位置,则模型和结果将不同。
很容易想象我们如何根据 Excel ® 工作簿中的其他形状、图表和数据来控制和编辑此模型。还可以从 COMSOL Multiphysics 模型中提取数值结果并生成例如用于报告中的 Excel ® 工作簿内容。
可以将 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 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) ' 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 End Sub这篇博客仅介绍了可以使用 VBA 和 Excel ® 执行的一些简单操作。如果您是 COMSOL 用户,可以通过访问整个 COMSOL API,来访问所有模型设置和参数。这样,就可以定义任何类型的模型并在使用 COMSOL Multiphysics 求解后提取其数据。
观看关于 LiveLink™ for Excel ® 的视频Microsoft、Excel 和 Visual Basic 是 Microsoft Corporation 在美国和/或其他国家/地区的注册商标或商标。
Oracle 和 Java 是 Oracle 和/或其附属公司的注册商标。