你有没有尝试过在你的模型中加入一个随机创建的几何对象?比如你想要模拟一种天然材料,或者一种在尺寸变化上具有某些已知的统计分布的零件排列。当遇到这类情况时,你可能希望在 COMSOL Multiphysics® 软件中创建一个随机几何对象。对此,我们可以借助模型方法来实现这个需求。本文我们将以一块美味的奶酪为例,介绍具体的操作方法。
建立瑞士奶酪模型
选出世界上最好的奶酪,这是一项艰巨且难以抉择的任务,但是我必须发表自己的意见:优质的埃曼塔(Emmentaler)奶酪绝不输给其他任何种类。一位奶酪制作大师或许会开玩笑说,其实是奶酪上的蜂窝状圆孔增加了风味。所以如果要在 COMSOL Multiphysics 中建立一个逼真的圆盘状奶酪模型,我们必须加上这些小孔。
埃曼塔奶酪的模型,小孔的位置与大小均为随机设定。
事实证明,瑞士奶酪中的小孔形成的原因相当复杂,所以我们不会去模拟小孔的形成,而是简单地建立一个如上图所示的奶酪模型。我们计划在奶酪中添加一组随机分布的小孔,其半径在上限值和下限值之间随机浮动。在 COMSOL Multiphysics 5.3 版本中,我们可以借助全新的模型方法 功能创建这个随机几何对象。该如何实现呢?
在 COMSOL Multiphysics® 中引入模型方法
在 Windows® 平台上运行 COMSOL Multiphysics® 5.3 版本并使用模型开发器时,功能区会显示开发工具 选项卡,如下方的截图所示。其中一个选项是录制方法。单击此选项后,会提示您输入新方法的名称 和方法类型。您可以输入任意符合要求的字符串来定义方法名称,方法类型则可选 App 方法 或模型方法。
App 方法 适用于 COMSOL App——链接中的教学视频介绍了它的使用过程。模型方法 适用于 COMSOL Multiphysics 的底层模型,可以处理现有的模型数据(并添加信息)。
开发工具选项卡,显示了 录制方法和 运行模型方法按钮。
单击录制方法 对话框中的确定 按钮后,你会看到一圈高亮的红线环绕整个图形用户界面。随后所有的执行操作都会录制到此方法中,直到单击停止录制 按钮。然后,你可以切换到App 开发器来查看录制好的方法。下方截图显示了录制完单个几何对象的创建过程后的App 开发器和方法。这是一个标签为 cyl1
、半径为 40 cm、高度为 20 cm 的圆柱体,对于圆盘奶酪而言,这是一个良好的初始近似值。
App 开发器显示了用于建立几何对象的模型方法代码。
在模型开发器中,借助开发工具 选项卡下的运行模型方法 按钮,我们能够在其他任何模型文件(只要模型文件的几何序列中不存在带有 cyl1
标签的现有对象)中调用这个模型方法。当然,这个简单的模型方法仅仅创建了一个圆柱体。如果要模拟小孔,我们需要在方法中引入一些随机性。下文将研究这一点。
创建随机的几何特征集
在模型方法中,您可以调用包含 Math.random 类在内的标准 Java® 类,这种类会返回大于或等于 0.0 且小于 1.0 的双精度数。我们将使用这种类,加上新编写的少量代码,在圆盘奶酪模型中建立指定数量的、位置和大小随机设定的小孔。
假设整块奶酪中随机分布 1000 个小孔,每个小孔的半径随机分布在 0.1 cm 到 1 cm 之间。我们还要记得,埃曼塔奶酪有一层天然的坚硬外皮,其上不会形成小孔。所以,我们需要添加一些逻辑,确保 1000 个小孔实际存在于奶酪内部。下方的完整模型方法(添加了行号,并将文本字符串标记为红色)展示了具体步骤。
1 int NUMBER_OF_HOLES = 1000; 2 int ind = 0; 3 double hx, hy, hz, hr = 0.0; 4 double CHEESE_HEIGHT = 20.0; 5 double CHEESE_RADIUS = 40.0; 6 double RIND_THICKNESS = 0.2; 7 double HOLE_MIN_RADIUS = 0.1; 8 double HOLE_MAX_RADIUS = 1.0; 9 model.component("comp1").geom("geom1").lengthUnit("cm"); 10 model.component("comp1").geom("geom1").selection().create("csel1", "CumulativeSelection"); 11 while (ind < NUMBER_OF_HOLES) { 12 hx = (2.0*Math.random()-1.0)*CHEESE_RADIUS; 13 hy = (2.0*Math.random()-1.0)*CHEESE_RADIUS; 14 hz = Math.random()*CHEESE_HEIGHT; 15 hr = Math.random()*(HOLE_MAX_RADIUS-HOLE_MIN_RADIUS)+HOLE_MIN_RADIUS; 16 if ((Math.sqrt(hx*hx+hy*hy)+hr) > CHEESE_RADIUS-RIND_THICKNESS) {continue; } 17 if (((hz-hr) < RIND_THICKNESS) || ((hz+hr) > CHEESE_HEIGHT-RIND_THICKNESS)) {continue; } 18 model.component("comp1").geom("geom1").create("sph"+ind, "Sphere"); 19 model.component("comp1").geom("geom1").feature("sph"+ind).set("r", hr); 20 model.component("comp1").geom("geom1").feature("sph"+ind).set("pos", new double[]{hx, hy, hz}); 21 model.component("comp1").geom("geom1").feature("sph"+ind).set("contributeto", "csel1"); 22 ind++; 23 } 24 model.component("comp1").geom("geom1").create("cyl1", "Cylinder"); 25 model.component("comp1").geom("geom1").feature("cyl1").set("r", CHEESE_RADIUS); 26 model.component("comp1").geom("geom1").feature("cyl1").set("h", CHEESE_HEIGHT); 27 model.component("comp1").geom("geom1").create("dif1", "Difference"); 28 model.component("comp1").geom("geom1").feature("dif1").selection("input").set("cyl1"); 29 model.component("comp1").geom("geom1").feature("dif1").selection("input2").named("csel1"); 30 model.component("comp1").geom("geom1").run();
让我们一起逐行分析上述模型方法:
1. 初始化并定义奶酪内的小孔总数。
2. 初始化并定义用于后续步骤的索引计数器。
3. 初始化一组用于保留每个小孔的 xyz 位置和半径的双精度数。
4–8. 初始化并定义一组数字,以 cm 为单位定义奶酪的高度、半径、环线粗细以及小孔的最大和最小半径。
9. 将几何的长度单位设置为 cm。
10. 创建一个新选择集,并添加标签 csel
和名称 CumulativeSelection
。请注意:如果类似的选择集已存在,此时该方法将失败。如果您要在同一个文件中反复运行该方法,可以通过修改方法来解决这个问题。
11. 初始化 while 循环,创建指定数量的孔。
12–14. 通过调用随机方法和缩放输出来定义小孔的 xyz 坐标,使小孔的 xyz 坐标位于奶酪模型的笛卡尔坐标的外部界限之内。
15. 在规定的限值范围内定义小孔半径。
16–17. 检查小孔的位置和尺寸是否会使其脱离奶酪。如果是,继续执行 while 循环的下一次迭代,停止执行循环中的剩余代码。此检查可在一行中进行,也可以分为三行,这取决于您的编程风格。
18. 创建一个球体,使其名称基于当前的索引值。
19–20. 指定新创建球体的半径和位置。虽然半径可以通过一个双精度数直接传递,但位置必须使用双精度数组。
21. 指定此球体特征属于名为 csel1
的选择集的一部分(做出了贡献)。
22–23. 对索引运行迭代,表明已创建球体,并结束 while 循环。
24–26. 创建一个代表圆盘奶酪的圆柱体。
27–29. 建立一个布尔差集运算。要添加的对象是圆柱体,要减去的对象是所有球体的选择。
30. 运行整个几何序列,将所有球体从圆柱体中切除,最终形成圆盘奶酪。
我们可以在新的(和空的)模型文件中运行这个方法,从而创建一个圆盘奶酪模型。每一次重新运行此方法,我们就会得到一个不同的模型。模型文件中的几何序列包含全部球体、圆柱体以及布尔运算。
如果有必要,我们还可以在模型方法中添加一些额外的代码,直接输出最终模型(奶酪)的几何文件。您可以使用 COMSOL Multiphysics 格式或者 STL 文件格式编写模型文件,也可以使用任何包含 Parasolid® 软件内核的可选模块,并以 Parasolid® 软件或 ACIS® 软件文件格式导出。相比于直接处理完整的几何序列,在处理最终几何之前先进行导出和重新导入的效率更高。
下图是我们的努力成果。看上去很美味!
将被吃掉的埃曼塔圆盘奶酪的模型。
借助模型方法创建随机几何对象的结语
上面,我们通过一个简单示例介绍了如何使用模型方法来创建位置和大小随机分布的几何对象。我们留下了一些尚未解决的问题,例如,如何确保小孔不重叠,或者如何模拟密集堆积的布局等。但是这些问题本质上都是各自领域中的一些数学难题。
当然,模型方法还有许多其他用途,我们会在另一篇文章中详述。而且我们还有其他用于创建随机几何对象的方法,例如定义参数化的表面。
相关资源
- 阅读博客:生成可视化和可听的仿真结果,了解模型方法的另一种用途
- 在发布亮点页面中查看 COMSOL Multiphysics® 5.3 版本的新功能
ACIS 是 Spatial 公司的注册商标。Oracle 和 Java 是甲骨文公司和/或其附属公司的注册商标。Parasolid 是西门子产品生命周期管理软件公司或其子公司在美国和其他国家/地区的商标或注册商标。Microsoft 和 Windows 是微软公司在美国和/或其他国家/地区的注册商标或商标。
评论 (11)
凯戈 俞
2022-04-21作者你好,我想对代码提出我的问题:while循环中的if语句的条件为什么这么设置,能在解释的具体点吗?谢谢
hao huang
2022-04-21 COMSOL 员工此条代码用来判断小孔是否存在于奶酪中,如果超出奶酪范围了,可以忽略。
女士 田
2022-06-09您好!有两个问题想请教:
1. 请问如何调整代码使小孔的分布呈正太分布呢?查询到有内置函数randomnormal但是脚本总是提示语法错误。
2. 是否可以设置小孔不相交?
谢谢!
Qihang Lin
2022-06-22 COMSOL 员工您的问题与具体代码有关,建议您联系技术支持根据具体问题查找原因:http://cn.comsol.com/support
文 锐
2023-11-17求助这个randomnormal怎么用
爽 王
2024-06-11请问,怎么保证球体之间不相交,用什么函数呢?
Yuqing Ge
2024-06-14 COMSOL 员工您好,这需要您手动写一些判断条件,当生成的球与已存在的球相交时,将其舍弃;或者您可以取所有小球的交集,当交集不为空时,将其舍弃。
秋花 王
2024-07-24您好,如果奶酪中的孔隙大小按照一定的幂函数形式分布,应该怎么样控制呢?
Hao Li
2024-07-31 COMSOL 员工Math.random()是生成[0,1]均匀随机函数,其他形式分随机分布可以参考概率论的内容,通过均匀随机函数组合的方式写为任意分布,如 Math.pow(Math.random(), a) 形式即表示幂指数分布。
Ran Li
2024-11-02请问内置函数randomnormal()的参数是代表什么?
Yuqing Ge
2024-11-07 COMSOL 员工randomnormal 函数是呈正态分布,平均值为 0,标准偏差为 1的随机函数,它的参数可以是任意维度的数值,如randomnorma(0,-100)。可以参考博客:https://cn.comsol.com/blogs/sampling-random-numbers-from-probability-distribution-functions