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

2017年 3月 15日

您有没有想过在 COMSOL Multiphysics®软件中自定义或创建一个基于 Excel®电子表格数据的模型?许多科学和工程应用中都采用 Excel 电子表格来呈现、协作和存储数据。借助 COMSOL 接口产品 Microsoft®Visual Basic®for Applications(VBA)和 LiveLink™forExcel®,我们可以在 Excel®电子表格软件中创建模型和设置参数。这篇博客,我们将演示如何实现这个过程,并列举了一些示例。

编者注: 这篇博客更新于 2024 年 6 月 20 日, 以反映 COMSOL Multiphysics®软件 6.0 版本的功能和特性。

在仿真中使用 LiveLink™forExcel®和 VBA

LiveLink™forExcel®是一款接口产品,允许您将 Excel 数据连接到 COMSOL Multiphysics。如果您是 LiveLink™forExcel®的新用户,可以阅读以下文档(安装软件后访问)开始使用:

  • LiveLink™forExcel®简介
  • LiveLink™forExcel®用户指南

Excel®电子表格软件还提供了在 Excel®工作簿中定义和运行 VBA 的功能。虽然 VBA 脚本可以手动编写 ,但也可以使用 COMSOL Multiphysics®的用户界面由现有模型中生成。正如些示例的帮助文档中所显示的,通过 LiveLink™forExcel®可以轻松地实现 VBA 的使用。接下来,我们将通过一个常见的应用来看看如何检索和更新 COMSOL Multiphysics 模型中的参数。

这个功能很有用,但 VBA 和 LiveLink™forExcel®还有更多的用途。例如,我们将了解如何构建 COMSOL Multiphysics 模型并使用 Excel®工作簿中插入的一些基本形状定义模型几何。

注意:此处讨论的示例是使用 Excel®2019 版本显示的,但在其他版本中的操作是一样的。

使用 VBA 可以为组件对象模型 (Component Object Model ,COM) 提供接口。当您安装 LiveLink™forExcel®后,还会安装一个 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.comsolutilcomsolcom.modelutil对象的动态实例。

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

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

这个声明的优点是版本独立。最新安装的comsolcom.comsolutilcomsolcom.modelutil是在运行时使用。

也可以使用下面含静态 COM 引用的声明comsolcom.comsolutilcomsolcom.modelutil

DimcomsolutilAscomsolutilSetcomsolutil = CreateObject("comsolcom.comsolutil")DimmodelutilAsmodelutilSetmodelutil = 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®

Setcomsolutil = CreateObject("comsolcom.comsolutil")Setmodelutil = 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™forExcel®,必须改用以下语法:

model.get_study("std1")

COMSOL Multiphysics 模型中的接口参数

VBA 和 LiveLink™forExcel®中的一个常见应用是检索和更新 COMSOL Multiphysics 模型中的参数。接下来,我们将看到如何轻松实现。

下图中的 VBA 脚本启动了 COMSOL Multiphysics server,连接到启动的服务器,从与 Excel®工作簿相同的目录中加载了母线板焦耳热(使用 LiveLink™forExcel®)模型,使用更新的长度参数求解模型, 以及用另一个文件名保存更新后的模型。

用于启动并连接到 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 编辑器中打开指定的宏并用以下脚本替换内容:

OptionExplicitSubSolve_Click()DimnodeDimcoordinatesDimindexDimnewPolygonTable()As DoubleDimnewHeatSource(1To2)As DoubleDimmodelAsModelImpl 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))DimnNodesAs LongnNodes = Sheets("Sheet1").Shapes("SimulationRegion").Nodes.CountReDim PreservenewPolygonTable(1TonNodes, 1To2)For EachnodeInSheets("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)NextSetmodel = SetModel(newPolygonTable, newHeatSource)Callmodel.get_study("std1").RunIf NotContainsTag(model.result().tags(),"pg1")ThenCallmodel.result().Create("pg1","PlotGroup2D")Callmodel.get_result("pg1").feature().Create("surf1","Surface")Callmodel.get_result("pg1").Label("Temperature (ht)")Callmodel.get_result("pg1").set("data","dset1")Callmodel.get_result("pg1").get_feature("surf1").Label("Surface")Callmodel.get_result("pg1").get_feature("surf1").set("colortable","ThermalLight")Callmodel.get_result("pg1").get_feature("surf1").set("data","parent")Callmodel.get_result("pg1").get_feature("surf1").RunEnd IfCallmodel.get_result("pg1").RunCallRange("J10").SelectDimtempPngAs StringtempPng = Environ("Temp") &"\PolygonHeat"& Format(Now(),"yyyymmddhhmmss") &".png"DimexportTagAs StringexportTag = model.result().Export.uniquetag("export")Callmodel.result().Export().Create(exportTag,"Image2D")Callmodel.result().get_export(exportTag).set("plotgroup","pg1")Callmodel.result().get_export(exportTag).set("pngfilename", tempPng)Callmodel.result().get_export(exportTag).RunIfDir(tempPng)""ThenCallApplication.ActiveSheet.Pictures.Insert(tempPng) SetAttr tempPng, vbNormal Kill tempPngEnd IfCallmodel.result().Export().Remove(exportTag)End SubPrivate FunctionSetModel(ByRefnewPolygonTable()As Double,ByRefnewHeatSource()As Double)As VariantDimcomsolutilAscomsolutilSetcomsolutil = CreateObject("comsolcom.comsolutil")DimmodelutilAsmodelutilSetmodelutil = CreateObject("comsolcom.modelutil")DimmodelAsModelImplIf NotIsConnected(modelutil)ThenCallConnectServer(comsolutil, modelutil)End IfIf NotContainsTag(modelutil.tags(),"PolygonHeatModel")ThenSetSetModel = CreateModel(modelutil,"PolygonHeatModel", newPolygonTable, newHeatSource)Exit FunctionEnd IfSetmodel = modelutil.model("PolygonHeatModel")Callmodel.get_geom("geom1").get_feature("pol1").set("table", newPolygonTable)Callmodel.get_geom("geom1").get_feature("c1").set("x", newHeatSource(1))Callmodel.get_geom("geom1").get_feature("c1").set("y", newHeatSource(2))Callmodel.get_geom("geom1").runAllSetSetModel = modelEnd FunctionPrivate FunctionCreateModel(ByRefmodelutilAsmodelutil,ByRefmodelTagAs String,ByRefnewPolygonTable()As Double,ByRefnewHeatSource()As Double)As VariantDimmodelAsModelImplSetmodel = modelutil.Create(modelTag)Callmodel.ModelNode().Create("comp1")Callmodel.geom().Create("geom1", 2)Callmodel.mesh().Create("mesh1","geom1")Callmodel.get_geom("geom1").Create("pol1","Polygon")Callmodel.get_geom("geom1").get_feature("pol1").set("source","table")Callmodel.get_geom("geom1").get_feature("pol1").set("table", newPolygonTable)Callmodel.get_geom("geom1").Selection().Create("csel1","CumulativeSelection")Callmodel.get_geom("geom1").get_feature("pol1").set("contributeto","csel1")Callmodel.get_geom("geom1").get_run("pol1")Callmodel.get_geom("geom1").Create("c1","Circle")Callmodel.get_geom("geom1").get_feature("c1").set("r", 0.01)Callmodel.get_geom("geom1").get_feature("c1").set("x", newHeatSource(1))Callmodel.get_geom("geom1").get_feature("c1").set("y", newHeatSource(2))Callmodel.get_geom("geom1").Selection().Create("csel2","CumulativeSelection")Callmodel.get_geom("geom1").get_feature("c1").set("contributeto","csel2")Callmodel.get_geom("geom1").RunCallmodel.get_geom("geom1").get_run("fin")Callmodel.Material().Create("mat1","Common","comp1")Callmodel.get_material("mat1").set("family","copper")Callmodel.get_material("mat1").get_propertyGroup("def").set("heatcapacity","385[J/(kg*K)]")Callmodel.get_material("mat1").get_propertyGroup("def").set("density","8960[kg/m^3]")Callmodel.get_material("mat1").get_propertyGroup("def").set("thermalconductivity","400[W/(m*K)]")Callmodel.Physics().Create("ht","HeatTransfer","geom1")Callmodel.get_physics("ht").Create("temp1","TemperatureBoundary", 1)Callmodel.get_physics("ht").Create("temp2","TemperatureBoundary", 1)Callmodel.get_physics("ht").get_feature("temp2").set("T0","293.15[K]+20")Callmodel.get_physics("ht").get_feature("temp1").Selection().named("geom1_csel1_bnd")Callmodel.get_physics("ht").get_feature("temp2").Selection().named("geom1_csel2_bnd")Callmodel.study().Create("std1")Callmodel.get_study("std1").Create("stat","Stationary")Callmodel.get_study("std1").RunCallmodel.result().Create("pg1","PlotGroup2D")Callmodel.get_result("pg1").Label("Temperature (ht)")Callmodel.get_result("pg1").set("data","dset1")Callmodel.get_result("pg1").feature().Create("surf1","Surface")Callmodel.get_result("pg1").get_feature("surf1").Label("Surface")Callmodel.get_result("pg1").get_feature("surf1").set("colortable","ThermalLight")Callmodel.get_result("pg1").get_feature("surf1").set("data","parent")SetCreateModel = modelEnd FunctionPrivate FunctionIsConnected(modelutilAsmodelutil)As Boolean'Try to access model tags. If not connected to a server this will throw an error.On Error GoToErrorHandlerCallmodelutil.tags IsConnected =TrueExit FunctionErrorHandler: IsConnected =FalseEnd FunctionPrivate FunctionConnectServer(comsolutilAscomsolutil, modelutilAsmodelutil)On Error GoToErrorHandlerCallmodelutil.connectIf Notcomsolutil.isGraphicsServer()ThenMsgBox prompt:="The running COMSOL Multiphysics Server is not a graphics server. Exporting results will not work.", Buttons:=vbOKOnly, Title:="COMSOL"End IfExit FunctionErrorHandler:Callcomsolutil.TimeOuthandler(True)Callcomsolutil.StartComsolServer(True)Callmodelutil.connectEnd FunctionPrivate FunctionContainsTag(tags()As String, tagAs String)As BooleanContainsTag =FalseIf(UBound(Filter(tags, tag)) > -1)ThenContainsTag =TrueEnd IfEnd Function

6. 输入代码后,点击Solve按钮。这将执行宏中定义的 VBA 脚本并根据 Excel®工作簿中的形状创建模型。模型求解后,图形被插入到工作表中。

一个 COMSOL Multiphysics 模型被插入 Excel 工作簿中。

如果更改Simulation Region形状并将Heat Source形状移动到Simulation Region内的另一个位置,则模型和结果将不同。

很容易想象我们如何根据 Excel®工作簿中的其他形状、图表和数据来控制和编辑此模型。还可以从 COMSOL Multiphysics 模型中提取数值结果并生成例如用于报告中的 Excel®工作簿内容。

在 VBA 中使用 COMSOL LiveLink 功能区函数

可以将 LiveLink™forExcel®功能区与 COMSOL Multiphysics 模型交互的简便性与使用 VBA 的 COMSOL API 的所有功能结合起来。这可以通过 RibbonUtil 类的函数来实现。LiveLink™forExcel®功能区上的几乎每个按钮都有相应的 VBA 命令。功能区按钮的工具提示包含简短的 VBA 代码片段,可显示相应的 VBA 功能。

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

COMSOL 案例库中的busbar_llexcel.mph模型是这种方法如何降低通过 VBA 访问 COMSOL Multiphysics 模型的复杂性的最佳示例。在该模型中,由Update按钮运行的 VBA 代码结合了RibbonUtil函数与通用 API,来加载和修改扫描、运行和更新所有链接结果,并从下图所示的简短代码中提取新的扫描插值数据:

SubbusbarUpdate()DimModelUtilAsModelUtilDimComsolUtilAsComsolUtilDimRibbonUtilAsIRibbonUtilSetindex ModelUtil = CreateObject("comsolcom.modelutil")SetComsolUtil = CreateObject("comsolcom.comsolutil")SetRibbonUtil = ComsolUtil.GetRibbonUtil' Allow long running jobsComsolUtil.TimeOutHandlerTrue' Open linked model (if ribbon not already connected to the COMSOL server)If NotRibbonUtil.IsConnectedThenRibbonUtil.OpenLinkedModelEnd If' Create a link with the model with the tag Model in the COMSOL serverSetModel = ModelUtil.Model("Model")' Update model parameter set in A4Sheets("Sheet1").Activate Range("A4").SelectRibbonUtil.UpdateDefinitions' Keep only the fifth first columns for both sweep parametersRange("G9:J10").Clear' Update parametric sweep parameters set in A8Range("A8").SelectRibbonUtil.Sweep"std1", ,True' Enable progress barModelUtil.ShowProgressTrue' Compute solutionModel.get_study("std1").Run' Displa plot group pg3Model.get_result("pg3").Run Sheets("Sheet1").Range("L4").Select' Insert graphics of plot group pg3RibbonUtil.InsertGraphics"pg3"' Update all numerical results in current sheetRibbonUtil.UpdateAllResults' Retreive parametric sweep data (for later formating)Vtot = Sheets("Sheet1").Range("B10:F10").ValueForI = 0To4If NotIsEmpty(Sheets("Sheet1").Range("B9").Offset(, I).Value)ThenwbbLength = I + 1End IfIf NotIsEmpty(Sheets("Sheet1").Range("B10").Offset(, I).Value)ThenVtotLength = I + 1End IfNextwbb = 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 coordinatesSheets("Sheet2").Activate Range("D1:AB21").Delete' Update interpolation resultsForI = 0TowbbLength - 1 Range("D4").Offset(, I * VtotLength).SelectRibbonUtil.ResultsInterpolation"dset2","ht.Qtot","A4:C21", ,"wbb", wbb(1, I + 1)Next' Set cell formatCells(1, 4) ="Qtot [W]"Cells(1, 4).Font.Bold =TrueCells(1, 4).HorizontalAlignment = xlCenter Cell2 = 4 + VtotLength * wbbLength - 1 Range(Cells(1, 4), Cells(1, Cell2)).MergeForI = 0TowbbLength - 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 =TrueCells(2, Cell1).HorizontalAlignment = xlCenter Range(Cells(2, Cell1), Cells(2, Cell2)).Merge Range(Cells(2, Cell1), Cells(2, Cell2)).Borders.Weight = xlThickForj = 1ToVtotLength 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 = xlThickNextNextEnd Sub

这篇博客仅介绍了可以使用 VBA 和 Excel®执行的一些简单操作。如果您是 COMSOL 用户,可以通过访问整个 COMSOL API,来访问所有模型设置和参数。这样,就可以定义任何类型的模型并在使用 COMSOL Multiphysics 求解后提取其数据。

Microsoft、Excel 和 Visual Basic 是 Microsoft Corporation 在美国和/或其他国家/地区的注册商标或商标。

Oracle 和 Java 是 Oracle 和/或其附属公司的注册商标。

博客分类


评论 (0)

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