新闻详情

新闻详情

首页 / 资讯中心 / 详情

Comsol动网格仿真捕风机:转动瞬态建模与避坑指南

发布时间:2026/10/1 18:05:24来源:尧图网络
Comsol动网格仿真捕风机:转动瞬态建模与避坑指南
看到这个标题估计不少朋友第一反应是捕风机有什么好仿真的动网格不是搞流固耦合才用得上吗两者凑一块能碰撞出什么东西我去年接了一个通风除尘设备的验证项目客户要模拟捕风机在启动过程中入口流场随叶轮位置的变化一开始我也觉得用稳态算就完事了后来被结果教育了一顿才真正把Comsol的动网格和捕风机这个组合摸透。今天就把整个过程捋一遍为什么捕风机的问题绕不开动网格、几何和网格怎么准备、实际模型怎么一步步搭、以及我踩过的那些报错和坑。想入门移动网格、又对旋转机械瞬态仿真感兴趣的朋友这篇应该能帮你省下不少试错时间。1. 为什么捕风机的仿真里必须动网格1.1 捕风机里的“运动”才是真正的物理本质先明确一下这里说的捕风机是什么。捕风机不是某种单一设备它泛指用来捕集、引导和输送气流的旋转风机常见的有离心式、轴流式和混流式三种结构上通常包括集流器、叶轮、蜗壳或风筒、导叶、出口扩散段这些部件。核心运动部件是叶轮叶片绕着轴持续旋转把机械能转化为气体的压力能和动能。这个“旋转”在仿真里是个很麻烦的事。叶片一转计算域的形状随时间变化流道进口的位置相对也在变叶片与蜗壳之间的间隙、叶片与集流器之间的间隙都处于动态变化中。说得直白一点流场的边界本身在动入口风量、压力脉动、叶片通过频率都跟叶轮的瞬时角度强相关。如果你把网格固定住去算这种问题等于把“叶片转过了某个位置”这个信息直接丢掉了算出来的只是某个时间平均意义上的流场根本看不到捕风机里真正发生的事。这就像拍一部动画片你却只给观众看一帧静态背景画面里的角色全没了当然不对。我在实际项目里遇到的情况更具体客户想验证捕风机从静止加速到额定转速的过程中入口段的回流会不会造成集流器振动以及叶片扫过蜗壳舌部时压力脉动的幅度。这些全是瞬态问题静态计算完全回答不了只能在动网格模型里让叶片真的转起来。1.2 固定网格的几种替代方案都差在哪可能有人会说旋转机械不一定要动网格业界不是有MRF、滑移网格这些成熟方法吗确实是但这些方法都有各自的适用边界放到捕风机这类问题上未必合适。先看多参考系法也就是MRF或者冻结转子。它把计算域分成旋转域和静止域旋转域里加上科里奥利力和离心力网格本身不动适用于叶轮与蜗壳之间干涉不强烈的定工况设计计算。但MRF本质上是“冻结”转子在某个固定位置只能得到一个位置下的稳态流场看不到叶片相对静止部件运动产生的瞬态效应更不用说启动、变速、挡板调节这种动态过程。再看滑移网格在Comsol里可以通过旋转域配合一致对上的连续性条件来实现。滑移网格思路是旋转域网格独立转动交界面两侧通过映射传递通量。它能模拟瞬态但交界面上的通量插值会引入数值耗散而且当交界面两侧网格尺寸差异较大时映射误差会积累有时候为了提高交界面精度网格加密成本比动网格还高。还有一种做法是用固定网格但给叶片表面施加切向速度边界条件让叶片附近的流体被“拖着走”。这个方法看起来便宜实际上问题更大叶片几何完全没转动叶轮流道形状没变间隙始终不变压力分布自然也不对连定性的趋势都可能是错的。我在早期尝试时用这个方法算出来的压升和实测差了将近30%从那以后就彻底放弃了。所以真正要捕捉“叶片扫过入口”“间隙随转动变化”这类物理过程动网格几乎是唯一靠谱的选择。它的核心思想很直接让计算网格跟着叶片的运动一起变形、转动网格节点坐标本身就是求解量彻底解决移动边界问题。1.3 动网格的本质让网格跟物体一起动很多新手一听动网格就慌觉得这是高难度玩法。其实Comsol里的动网格接口逻辑比想象中简单。它基于ALE方法也就是任意拉格朗日-欧拉描述——流体控制方程仍然在欧拉框架下写但网格节点的坐标用拉格朗日方式更新网格节点可以看作带有独立自由度的“虚拟物质点”。具体到捕风机这个场景叶轮表面是我们知道运动规律的位置可以给定强制位移比如按照转速旋转叶片周围的网格节点通过平滑方程把边界位移扩散到整个变形区域静止壁面则保持不动远场和出口区域如果变形影响很小甚至可以完全不参与移动。这样网格就形成了“外静内动中间过渡”的分层结构大幅降低变形计算量。动网格接口本身内置了几种变形平滑算法Laplace、Winslow和Yeoh。Laplace最平稳但大变形容忍度一般Winslow对边界正交性的保持有改善Yeoh由于引入了非线性刚度处理大变形时的网格质量维持能力更好。我做捕风机时首选Yeoh特别是在交界面附近的过渡带Yeoh能明显推迟负网格的出现。这里需要额外区分一个概念动网格不等于“重新画网格”。Comsol的移动网格支持网格重剖分当一个旋转周期内网格扭曲严重到无法继续计算时可以重新生成一套网格并把旧网格上的解插值到新网格上。这个能力和网格变形配合使用才能真正撑住叶片多圈旋转。单纯依赖变形不重剖分通常转一圈以内还行转两圈以上基本必崩。2. 动手前先把捕风机模型拆明白2.1 几何简化决定后面少流汗做仿真的人都知道几何模型越精细网格越漂亮但越漂亮的网格越容易在动网格里给找麻烦。捕风机叶片本来就带扭曲和倒角实体CAD模型导进来之后哪怕不运动在叶片前缘做边界层网格都够头疼的一动起来那些细小圆角附近的单元会第一时间产生大畸变计算直接卡死。我的习惯是第一版几何必须做减法。叶片用中面代替实体叶片的厚度通过网格层数去体现所有倒角、圆角、加强筋这些细节先删掉等模型趋势验证对了再逐步加回去轮毂按回转体简化蜗壳型线保留但入口导圆直接简化为直角过渡。这样做的原因很简单动网格的难点是控制变形量几何细节越多局部网格就越密允许的变形裕度反而越小。还有一点容易被忽略进口和出口一定要延长。捕风机出口如果直接接到一个很短的边界上出口回流会把计算结果搅得一塌糊涂。我一般把出口段延长5到10倍风筒直径入口段延长3到5倍直径。延长段会额外增加一些网格量但和省下来的调试时间相比非常划算。2.2 旋转域与静止域划分是动网格成功的一半捕风机模型的流域划分决定动网格能不能撑得住。我的做法是把计算域拆成两个部分一个是只包住叶轮的旋转域包含叶片、轮毂以及叶片四周的流体另一个是静止域包含集流器入口、蜗壳/风筒、进出口延长段。这个划分不是简单为了画网格而是为了让动网格的变形区域尽量小变形只集中在旋转域和静止域之间的过渡环带。在Comsol里这两个域通过“装配”或“一致对”放在一起交界面用“连续性”边界条件耦合。动网格的运动设定为叶轮表面和旋转域内部按刚体转动静止壁面位移为零交界面附近的环带做自由变形。这样整个模型真正承受大变形的地方只有交界面的过渡环带变形控制、计算资源和重剖分频率都大幅下降。这里有个经验交界面环带的厚度不能太小。如果过渡带太薄旋转域和静止域之间的网格拉伸斜率会很陡容易负网格。我用过的经验值是过渡带厚度取叶轮半径的10%到15%左右网格尺寸取内域网格的1.5倍左右计算稳定性和精度能取得比较好的平衡。2.3 网格尺寸怎么定才能撑住大变形动网格对网格尺寸的要求比普通静态计算苛刻得多。静态计算只需要关注解的精度动网格还必须保证网格单元在最大位移状态下不发生翻转。换句话说网格既要足够细来解析流场又要在大变形时仍然保持质量。以我做的离心捕风机为例叶轮直径500mm叶片弦长100mm转速1450rpm。第一版网格直接按静态标准加密结果转不到半圈就报负雅可比。后来我把网格调整成“分层加密”的策略用一个参数表把各区域控制住。我建议你这样设置捕风机的网格参数区域单元类型尺寸控制说明叶片表面边界层网格第一层0.02mm8~10层增长率1.2靠近壁面解析粘性底层旋转域内部自由四面体最大单元3mm约为叶片弦长的1/30过渡环带自由四面体最大单元4mm承担主要变形不可过粗静止域主体自由四面体棱柱最大单元10mm远离叶轮只求解流动进出口延长段六面体扫掠最大单元15mm结构网格拉伸出流场很多人担心四面体在动网格里不耐用其实只要把尺寸控制住四面体比想象的稳。关键指标是单元质量系数在初始网格刚生成时就要保证最差单元质量在0.3以上如果低于0.2动起来必崩。这里还要顺便提一句Y估算。空气密度取1.2 kg/m³黏度1.8e-5 Pa·s叶片圆周速度πDn/60算下来约38m/s以叶片弦长0.1m为特征长度雷诺数大约是2.5e5属于充分发展的湍流。如果湍流模型里要求壁面附近Y在30左右那么第一层网格高度可以按y1 Y·μ/(ρ·uτ)粗算。这里摩擦速度uτ一般取来流速度的5%左右算出来大约1.9m/s第一层高度大概0.2mm量级。所以上面表格里的0.02mm是给低雷诺数模型准备的如果直接用壁面函数模型可以放宽到0.2mm左右。网格策略不是越细越好要和湍流模型匹配。3. 核心实操在Comsol里把动网格捕风机模型搭起来3.1 物理场与湍流模型选型模型加入的物理场接口是“流体流动”里的“湍流k-ε”或“湍流k-ω”。很多新手在这两个之间犹豫。我可以给一个基于实际经验的建议如果你只关心风机的宏观性能比如风量压升曲线、效率这些默认的k-ε接口已经足够而且收敛稳定计算量小如果你想分析叶片表面分离、蜗壳舌部附近的流动细节或者指望预测压力脉动幅值那么k-ω SST更合适它对逆压梯度区边界层的解析更细但代价是更吃网格和求解时间。我建议的路线是先用k-ε粗网格跑通趋势验证动网格设置没问题再用k-ω SST和加密网格做最终确认。这样做不是为了偷懒而是动网格模型本身就是瞬态非线性问题求解参数耦合在一起你很难判断报错来自网格还是来自湍流模型先把最稳定的组合跑通再逐步升级能少走很多弯路。顺带提一个版本相关的体验我在Comsol 6.4里发现湍流接口增加了一些代数模型和混合长度模型选项。这类模型用于快速预览非常香在大尺寸捕风机模型上用混合长度模型做前几步预估速度和稳定性的提升都很明显。当然最终结果还是要回到k-ω SST这类主流模型来确认。3.2 移动网格设置叶片旋转、过渡带变形、重剖分三层配合物理场接口添加完毕后在模型树里添加“动网格”接口。这个接口下要分三层去设置。第一层是旋转域。捕风机的叶轮和周围流体作为一个整体绕轴刚体旋转所以旋转域内部的网格节点相对位置不发生变化整个域做刚体平移旋转变换。在边界上我给叶片表面、轮毂表面设定指定位移。如果旋转中心在(x0c, y0c)当前时刻的旋转角度θωt那么叶片表面上某个初始坐标为(x0, y0)的点的位移分量可以写成ux (x0 - x0c)*(cos(theta) - 1) - (y0 - y0c)*sin(theta) uy (x0 - x0c)*sin(theta) (y0 - y0c)*(cos(theta) - 1)这里的theta建议通过全局参数定义比如 RPM1450[1/min]theta RPM*360[deg/min]*t 换算到弧度后使用。这样后期做参数扫描直接改转速参数就行。第二层是过渡变形区。旋转域的外边界和静止域之间的环带使用自由变形不施加额外约束由Comsol的平滑算法自动把旋转域边缘的位移扩散到静止域。这里我把平滑方法选成Yeoh并加大变形容差。注意过渡带的内部网格节点也要参与动网格不要把它们设成固定位移。第三层是网格重剖分。在动网格接口中启用“网格重剖分”节点触发条件可以设为“当网格单元质量低于阈值时”或“当雅可比行列式为负时”也可以设置固定的重剖分时间点。我这边是两种方式并行如果模型自己检测到网格质量恶化到0.2以下就触发重剖分同时我也在每转90度的整数倍时刻强制重剖分一次保证长时间旋转时网格质量始终在可控范围。网格重剖分后物理场变量需要从旧网格插值到新网格这一步会在计算中自动完成。但插值过程不是无损的旋度和压力梯度这类派生变量会有一定抹平。为了降低插值损耗重剖分频率不是越高越好我试过一个周期内不重新剖分完全可行的情况关键是初始网格质量要足够高过渡带变形设置要合理。3.3 求解器配置把时间步长和监测量管起来动网格是瞬态问题求解器配置直接影响能不能算完。我用的研究类型是“瞬态”求解器默认的BDF方法在多数情况够用但要把最大步长限制住。叶片转速1450rpm每秒转24圈如果每时间步转1度时间步长应该是dt 60 / (RPM * 360) 60 / (1450 * 360) ≈ 1.15e-4 秒所以我将初始步长设为1e-4秒最大步长设为2e-4秒这样能保证叶片在一个时间步内最多转过约1.7度网格更新幅度不会过大。如果你用自适应时间步且不设上限求解器会为了满足容差把步长拉到很大然后网格一步变形过多直接崩掉。后处理监测量也要提前布置。我在入口截面定义一个积分算子提取瞬时风量在叶片表面定义一个积分算子提取作用在叶片上的气动力矩。把这两个量作为全局变量输出算完之后直接绘制随时间的变化曲线能清楚看到启动过程中的风量脉动和力矩脉动。Comsol动网格做参数扫描也方便。如果想看不同转速下捕风机的动态响应可以直接用参数化扫描定义RPM数组。如果要用Matlab批量控制可以通过LiveLink for MATLAB做循环比如这样model mphload(fan_model.mph); rpmList [700, 1000, 1450, 1800]; for k 1:length(rpmList) model.param.set(RPM, num2str(rpmList(k))); model.study(std1).run(); data mpheval(model, spf.U, edim, 2); save([result_rpm_ num2str(rpmList(k)) .mat], data); end这套流程跑起来之后你会看到后处理动画里叶片真的在转入口流线和叶轮相对位置不断变化那种“模型活了”的感觉确实很奇妙。4. 常见报错与我的排查方法4.1 负雅可比和翻转单元报错动网格计算最经典的报错就是“负雅可比行列式”或者“网格包含翻转单元”通常出现在叶片转到某个角度后突然计算中断。我在捕风机项目中遇到这个报错的概率非常高基本可以分几种原因排查。报错现象高概率原因处理方案旋转一小段后报负雅可比局部网格太细或变形步长太大减小最大时间步长检查过渡带网格尺寸报错位置集中在叶片前缘边界层随叶片旋转被挤压将边界层与变形区分开前缘网格适当放大报错发生在重剖分之后立刻出现新旧网格映射变量失败改用更高阶插值设置或调整重剖分触发质量阈值网格单元被拉伸成细长条过渡带太薄变形无法疏散加厚过渡环带用Yeoh平滑从我的经验来看最容易被忽略的是时间步长。很多报错根本不是网格质量问题而是每步旋转角度太大网格节点位移超过局部单元尺寸的一半直接翻过去了。控制步长比加密网格更省事优先检查这个。4.2 入口回流导致压力振荡捕风机在启动阶段或低转速工况下吸入口经常会出现回流。回流会造成局部流场反复振荡计算结果很难收敛压力曲线像锯齿一样跳。这种情况我遇到过不止一次排查结论几乎都指向边界位置设得太近。处理办法有三个一是把入口延长段加长到5倍直径以上让回流发生在边界远处二是把入口边界条件从压力入口换成速度入口并加“抑制回流”约束三是如果你确实需要在入口设置压力边界把压力参考点放在远离回流区的位置。三种方法原理一样核心就是让数值边界远离物理上的复杂扰动区。另外捕风机叶片前缘在动网格旋转时强分离也会造成压力监测点振荡。这时候不要急着加数值耗散先检查叶片网格在旋转方向上的分辨率是否足够叶片前缘至少要有3到5层网格来容纳分离涡。4.3 边界层网格随变形成一锅粥叶片附近的边界层网格在旋转时容易变形纠缠特别是在重剖分之后原来整整齐齐的边界层经常被重新生成的四面体打乱。我的处理习惯是把边界层区域和变形区域在设置上做严格区分叶片壁面附近的边界层在重剖分时保留层数信息变形缓冲带放在边界层外侧让大变形尽量发生在边界层之外。在Comsol的网格重剖分节点里可以给边界层单独设置保留选项。我建议开启“重剖分时保留边界层”相关的设置项并指定边界层层数和增长率。重剖分前后对比一下叶片附近的单元质量如果发现边界层退化明显就把边界层区域排除在自由变形域之外只让外部网格参与变形。4.4 计算时间太长怎么控制成本完整的3D瞬态动网格模型计算成本确实不低。我曾经一个捕风机模型网格数接近500万单转速算8个旋转周期在8核机器上跑了将近三天。这个成本可以接受但如果你要扫描多组工况就必须想办法省时间。我常用的方法一是先做2D轴对称或2.5D模型把动网格设置和求解器配置调通再升级到3D这个方法能省掉至少一半的调试时间。二是用k-ε代替k-ω SST做前期工况扫描趋势对之后再用高精度模型验证少数关键工况。三是在求解器里关闭不必要的输出频率只保存最后一个旋转周期和关键监测点数据动画输出也控制在叶片每转5度输出一帧避免IO成为瓶颈。还有一个小技巧算之前先做“半圈预演”只算半个旋转周期观察网格质量曲线和监测量是否平滑如果没有明显问题再放开全计算时间段。这样能快速筛选掉那些一开始就会崩的参数组合。4.5 跨平台和批量计算的小建议有朋友问过Comsol在Linux上能不能跑动网格。我的实际体验是完全可以而且Linux服务器批量计算比桌面Windows更稳。用无界面模式提交批量任务把模型文件和管理脚本放好让它通宵跑也没问题。注意Linux集群上如果有许可证浮动的限制要配置好许可证环境变量这些是运维层面的事不影响模型本身。批量扫描工况时我习惯把模型做成参数化模型状态结果全部通过参数扫描输出而不是手动一个个改转速重跑。动网格加参数扫描加后处理动画一条流水线跑完这部分自动化做好了能节省大量重复劳动。5. 动网格和捕风机还能玩出哪些花样5.1 从纯净气体到含尘气流、气液两相流捕风机这个名字本身就暗示了它要和“捕集物”打交道可能是粉尘、水雾或者是车间里的油气混合物。这就涉及到两相流了。动网格在这个场景下依然负责叶轮旋转而液滴或气泡的界面追踪可以用水平集或相场方法做。不过两个瞬态非线性问题叠在一起计算量很容易爆。以我的经验颗粒尺寸分布比较宽的时候不要一股脑上相场模型可以考虑用粒子追踪接口处理颗粒相把连续气相用湍流模型求解颗粒受力按拖曳力计算。这种解耦方式精度不差稳定性却好很多。这里也回应一个很多人问的问题气液两相流的话Comsol和Fluent哪个更适用答案是看问题侧重。Fluent在高雷诺数湍流两相流方面数值格式成熟、收敛策略丰富Comsol的优势在于方程全开放可以自定义相间作用力而且和结构、传热、电磁模块无缝耦合。对捕风机这种后续很可能要做多物理场分析的场景Comsol的组合能力更有吸引力。5.2 与传热、电磁场耦合的多物理场扩展捕风机不总是吹常温空气。有些场合它是用来冷却带电部件或者帮助电弧快速熄灭的。这种时候动网格算出来的瞬态速度场会和温度场、甚至电流分布耦合在一起。在Comsol里加传热接口把流体域和固体域的温度联立求解边界上的对流换热系数由动网格得到的实时流场决定做出来的结果比稳态热分析要真实很多。这类模型计算量非常大但可以分步做先用绝热动网格算出速度场再把速度场作为已知背景流场去求解温度场最后再做真正的双向耦合。对工程验证来说分步耦合的精度通常已经够用。5.3 从动网格到流固耦合的延伸如果你想分析捕风机叶片在气动力下的变形和疲劳寿命动网格只是第一步。动网格求解得到的压力场可以提取出来作为叶片表面载荷导入结构模块做单向流固耦合分析。这样的话叶片应力、固有频率和疲劳寿命都有了依据。双向流固耦合要慎重。捕风机叶片在正常工作转速下变形通常很小但对流场的反馈可以忽略因此单向耦合的性价比很高。只有在研究颤振、异常振动或者柔性叶片大变形时才需要把结构力学接口和动网格真正双向耦合起来这时候计算资源消耗会成倍增加我建议非必要不上。我在实际做捕风机动网格仿真时最深的体会是动网格不是“越猛越好”核心是把网格位移管住。很多看似神秘的报错本质都是位移量超过了网格承受能力不是物理模型出了问题。所以我的习惯一直没变——第一遍先做稳态或冻结转子假设把风量和压升曲线标定准确再切换到动网格一上来就追求叶片真实转动视角往往会被网格问题淹没。最后再分享一个小技巧无论用哪个版本先在小转速、小角度下跑通一个完整周期再放开全转速计算这个做法能帮你省掉大量调试时间。动网格加捕风机这个组合入门不难难的是把细节抠到位抠到位之后你会发现计算的乐趣和工程价值都翻倍。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Rust所有权详解:Move、Borrow与Lifetime图解 2026/10/1 18:45:03

Rust所有权详解:Move、Borrow与Lifetime图解

刚接触 Rust 的人,十有八九第一道坎就是所有权。我记得自己第一次遇到borrow of moved value这个错误时,整个人是懵的:我明明只是把一个变量赋值给了另一个变量,凭啥原来那个就不能用了?后来又陆续被借用检查器教育了无…

阅读更多 →
KMP算法详解:从next数组手推到代码实现,彻底搞懂字符串匹配 2026/10/1 18:45:03

KMP算法详解:从next数组手推到代码实现,彻底搞懂字符串匹配

我在学习字符串匹配的时候,第一次接触 KMP 算法,说实话是有心理阴影的。网上帖子看了不少,next 数组的计算方法五花八门,有说从 1 开始的,有说从 0 开始的,还有说整体右移再补负一的,同一段代码…

阅读更多 →
综合能源系统多能流计算:统一牛顿-拉夫逊求解与Matlab实践 2026/10/1 18:45:03

综合能源系统多能流计算:统一牛顿-拉夫逊求解与Matlab实践

先说两句题外的。 “区域综合能源系统电气热能流计算”这个名字看着吓人,拆开之后做的事情其实很单纯:一个网络里同时跑着电、天然气、热三种能量,我们要把它们的稳态分布一次性算出来。传统电力潮流算的是电压和相角,气网算的是…

阅读更多 →
DirectX9c示例包实战:从zip解压到D3D9渲染环境搭建 2026/10/1 18:44:56

DirectX9c示例包实战:从zip解压到D3D9渲染环境搭建

简介:DirectX 9c初始化示例项目,面向DirectX 9初学者和传统游戏编程爱好者,展示如何通过Visual Studio 2012搭建基础游戏框架并完成Direct3D初始化。资源包共8个文件、仅5KB大小,涵盖cpp源码、vcxproj工程配置、filters源文件组织…

阅读更多 →
Qoder AI IDE 完全上手:安装配置、Credits计费与高效开发实战 2026/10/1 18:44:55

Qoder AI IDE 完全上手:安装配置、Credits计费与高效开发实战

Qoder 这段时间在开发者圈子里讨论度挺高,特别是前端和全栈方向的朋友,很多从 Codex 或 Cursor 转过来的。我自己的主力编辑器从 VS Code 切到 Qoder 已经跑了两个多月,中间踩过不少坑,也摸清了它那套 credits 和模型调度的脾气。…

阅读更多 →
Hindsight 智能体记忆:MCP 协议与 Docker 部署实战 2026/10/1 18:44:42

Hindsight 智能体记忆:MCP 协议与 Docker 部署实战

1. 从“hindsight”这个词说起:为什么它值得单独拿出来聊 第一次看到“hindsight”作为项目名,我脑子里蹦出来的不是词典释义,而是自己踩过的一个坑。早些年做对话系统,用户问“我上周说的那个偏好还算数吗”,系统一脸…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉