使用 Excel® 和 Visual Basic® 构建 COMSOL Multiphysics 模型

2017年 3月 15日

您有没有想过在 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® 电子表格软件的工具栏中启用 开发工具 选项卡,其中包含用于访问编辑器和其他开发相关功能的按钮。

一个 Excel 表格的屏幕截图。

在 VBA 中访问 COM 组件

我们可以使用下面的声明在 VBA 中创建 comsolcom.comsolutil comsolcom.modelutil 对象的动态实例。

设置 comsolutil = CreateObject("comsolcom.comsolutil")
设置 modelutil=CreateObject("comsolcom.modelutil")

屏幕截图显示了如何获取 VBA 中的 COM 组件。

这个声明的优点是版本独立。最新安装的 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 中的 COM 组件的另一种方式。

使用此声明的一个优点是,在使用定义的类型时,可以在 VBA 中获得帮助。

屏幕截图显示了用于定义类型的 VBA 中的帮助选项。

为了能够为 comsolutilmodelutil 定义静态类型,我们必须向 ComsolCom 添加一个 COM. 我们可以通过在 Excel® 电子表格软件中打开 VBA 编辑器,选择 Tools 菜单,选择 References,然后为已安装的版本选择 ComsolCom 来实现。

屏幕截图显示了如何添加一个 COM 参考。

使用 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 Multiphysics Server 的 Visual Basic  屏幕截图。

从 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®)模型,使用更新的长度参数求解模型, 以及用另一个文件名保存更新后的模型。

用于启动并连接到 COMSOL Multiphysics Server 的 VBA 脚本。

下图显示的 VBA 脚本提取了模型中参数的参数数据并将它们插入到了 Excel® 工作簿中。

用于提取 COMSOL Multiphysics 模型参数并添加到一个 Excel表格中的VBA 脚本。

如何使用 Excel® 和 Visual Basic® 构建 COMSOL Multiphysics 模型

在接下来的一个示例中,我们将创建一个 COMSOL Multiphysics 模型并使用 固体传热 接口求解一个二维模拟。这个过程包括在 Excel® 中通过添加带有一些说明的文本框、外部温度边界、内部温度边界和用于求解模拟的按钮来定义几何。模型求解后,结果图会插入 Excel® 工作簿中。下面给,我们来详细介绍一下这些步骤。

1. 首先,创建一个带有说明文本的文本框并将其插入到 Excel® 工作簿中。

向 Excel 工作簿中插入一个文本框。

2. 然后,为模拟定义一个区域。我们选择一个自由形状并将其插入 Excel® 工作簿中。然后,选择 SimulationRegion 作为形状的名称。通过右键单击形状并选择 Edit Polygon 使多边形可编辑。然后编辑形状,如下所示。

在一个 Excel 工作簿中编辑一个多边形。

3. 创建了一个温度更高的内部边界。为此,我们使用椭圆形状,创建一个圆形,并将其插入自由形状中。选择 HeatSource 作为形状的名称。椭圆形状必须位于 Simulation Region 形状内。

在 Excel 工作簿中创建一个多边形的内部边界。

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® 工作簿中的形状创建模型。模型求解后,图形被插入到工作表中。

一个 COMSOL Multiphysics 模型被插入 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 功能。

VBA 中 LiveLink 功能区函数的屏幕截图。

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)

正在加载...
浏览 COMSOL 博客