资讯详情

Abaqus参数化隧道建模实战:从脚本框架到批量计算

📅 2026/10/13 23:36:15 | 华诺云谱 👁 阅读
Abaqus参数化隧道建模实战:从脚本框架到批量计算
开写之前先说点题外话。我最早做隧道数值模拟的时候完全是手动党在界面上一个一个画部件、切布尔、布种子、划网格。单个模型还好最怕的是方案比选。有一次某个暗挖隧道的埋深和衬砌厚度同时调整我在界面里重复操作了整整一天最后导出的结果还得重新整理对比表。那次之后我就下了决心把模型全面转为Abaqus参数化建模。今天这篇就以隧道方向为例把我积累的脚本框架、建模细节和踩坑经验完整写出来希望能让正在做类似工作的朋友少走几趟弯路。这篇文章适合三类人一是刚接触Abaqus做岩土方向的研究生想搞懂隧道模型到底该怎么组织二是已经会建单模型、但苦于反复改尺寸做工况对比的工程师三是想把隧道建模流程沉淀成团队通用工具的进阶用户。文章侧重点不在某个复杂构件的精细模拟而是把参数化这条主线讲透——你只需要改几个数值就能重新生成整个隧道模型并跑完计算。1. 隧道数值模型的重复劳动困局为什么我坚决转投参数化1.1 手动建模到底慢在哪先说一个很多人没意识到的问题隧道有限元模型几何并不复杂真正耗时的是几何参数之间的联动关系。以最常见的浅埋暗挖隧道为例你要先建一个土体矩形再在指定位置建一个隧道洞室形状然后做布尔切割生成包含洞室的土体部件再生成衬砌部件。光这一步就涉及至少五六个几何参数土体宽度、土体高度、隧道埋深也就是隧道中心到地表的距离、隧道半径或跨度、衬砌厚度。手动建模时每改一个参数后面所有依赖于它的几何特征都要跟着改一遍。我算过一笔账一个中等复杂度的二维隧道模型从建模到提交计算手熟的人大约需要两个小时如果是三维盾构隧道模型加上管片分块可能得半天到一天。而参数化之后同一模型我只写了一次脚本之后每次换工况只需要改动脚本最前面的参数字典重新运行一遍模型生成、网格划分、材料赋值、边界条件、分析步设置全部自动完成跑一个二维模型从建模到出结果压缩到几分钟三维模型也基本在十几分钟内完成前处理。这种效率差距在需要对比几十组工况的时候就是数量级的差别。1.2 参数化建模要解决的三类变量我在脚本里把隧道参数化需要控制的变量分成三类。第一类是几何参数包括土体范围、隧道中心位置、隧道断面形状与尺寸、衬砌厚度、仰拱深度等。这类参数直接决定部件的形状和装配关系。第二类是材料参数包括围岩的弹性模量、泊松比、黏聚力、内摩擦角、容重以及衬砌的弹性模量、泊松比、厚度有时厚度单独作为几何参数等。材料参数决定了本构模型的行为也直接影响开挖后的变形和应力分布。第三类是计算控制参数包括网格种子间距、分析步的时长、最大增量步数、是否考虑流固耦合、接触设置与否等。这类参数常常被忽略但它们恰恰是批量计算时最容易出问题的地方——同样的几何和材料网格密度一变收敛性可能就完全不一样。这三类变量在脚本里都应该是独立、显式的不要在代码中间随意写死数值。写死的数值一旦需要修改就得满篇找反而比手动建模更痛苦。这是参数化建模的第一条原则所有可能变化的量全部提到脚本头部集中定义。2. 参数化脚本的总体框架从设计参数到CAE模型的一一映射2.1 参数清单的定义方式我习惯用Python字典来存储全部参数。这样结构清晰后期新增参数也非常方便。下面是一个二维隧道模型的参数定义示例import numpy as np params { # 几何参数 soil_width: 80.0, # 土体宽度m soil_height: 50.0, # 土体高度m tunnel_depth: 20.0, # 隧道中心到地表距离m tunnel_r: 5.0, # 隧道半径m liner_thickness: 0.4, # 衬砌厚度m mesh_size_tunnel: 1.0, # 隧道周边网格尺寸m mesh_size_global: 4.0, # 远处土体网格尺寸m # 围岩参数摩尔-库仑 soil_E: 80e6, # 弹性模量Pa soil_nu: 0.3, # 泊松比 soil_cohesion: 25e3, # 黏聚力Pa soil_friction: 25.0, # 内摩擦角° soil_density: 2000.0, # 容重kg/m^3 k0: 0.65, # 侧压力系数 # 衬砌参数 liner_E: 30e9, # 砼弹性模量Pa liner_nu: 0.2, # 砼泊松比 liner_density: 2500.0, # 砼容重kg/m^3 }这段代码里的每个数值都直接对应一个建模操作。比如tunnel_depth变了洞室位置就跟着变liner_thickness变了衬砌内外轮廓就跟着变。在写脚本时所有后续建模代码都引用这个字典而不是重新赋值。这既是脚本规范也是一种自我保护——避免改了一半漏改另一处的意外。2.2 模型树结构的自动化生成Abaqus的Python API操作逻辑是先创建模型Model再依次创建部件Part、材料Material、截面Section、装配Assembly、分析步Step、载荷与边界Load/BC、网格Mesh最后提交作业Job。我的脚本严格按这个顺序来组织函数from abaqus import * from abaqusConstants import * def create_parts(model, p): # 创建土体部件并进行布尔切割 model.ConstrainedSketch(name__profile__, sheetSize200.0) # ...创建土体矩形外轮廓和隧道圆形内轮廓 # ...拉伸生成土体部件 def create_liner_part(model, p): # 创建衬砌部件圆环 pass def create_materials_and_sections(model, p): # 定义围岩和衬砌材料、截面并赋予 pass def assemble_model(model, p): # 装配 pass def setup_steps_and_output(model, p): # 设置分析步、场输出和历史输出 pass def mesh_model(model, p): # 网格种子、划分、单元类型 pass函数名直接描述它做什么参数p就是前面的params字典。这样做的好处是每个函数职责单一任一步骤出了问题都能精准定位到对应的函数去排查而且当你想把二维模型扩展成三维时只需要替换create_parts和mesh_model的具体实现其余函数基本可以复用。2.3 为什么用Python脚本驱动而不是CAE宏录制很多朋友会问我为什么不直接在Abaqus/CAE里操作一遍然后导出宏文件作为脚本基础宏录制确实是一种快速入门方式但宏文件的问题在于它记录了太多的界面操作冗余信息例如视图操作、默认对话框确认动作等而且宏录制的代码通常直接使用固定坐标值几乎没有参数化能力。我更推荐的方式是先用宏录制搭出基本流程然后把它当作操作路线图再从头手写一套精简的参数化脚本。手写脚本的代码量大约只有宏的30%~50%而且每个数值都是显式参数别人拿到你的脚本后一看就懂改起来也非常方便。这里我建议初学者不要跳过手写脚本这一步因为只有手写你才能真正理解Abaqus对象模型之间的依赖关系——为什么先建材料再建截面、为什么截面必须关联到区域、为什么装配之后才能设边界条件。这些顺序逻辑在界面上是被隐藏的但在脚本里全部暴露出来。3. 隧道模型脚本化实现几何、地应力、开挖与支护的完整链路3.1 几何创建与布尔切割隧道模型的几何核心是土体矩形与隧道洞室圆形的布尔差集运算。Abaqus里布尔运算需要把多个草图特征在同一个Part内完成或者先建两个独立Part再用装配体的布尔操作完成切割。我通常采用第一种方式在同一个土体Part的草图中先画外部的土体矩形再画内部的圆形洞室轮廓拉伸之后直接形成带孔洞的部件。def create_soil_part(model, p): sketch model.ConstrainedSketch(nameSoilProfile, sheetSize200.0) # 外部矩形 sketch.rectangle(point1(-p[soil_width]/2.0, -p[soil_depth]), point2(p[soil_width]/2.0, 0.0)) # 内部隧道圆 sketch.CircleByCenterPerimeter(center(0.0, -p[tunnel_depth]), point1(p[tunnel_r], -p[tunnel_depth])) part model.Part(nameSoil, dimensionalityTWO_D_PLANAR, typeDEFORMABLE_BODY) part.BaseShell(sketchsketch) return part这里有一个关键细节土体底面坐标我用了-p[soil_depth]也就是说我把土体顶面定义在地表y0往下为负。这样处理的好处是地表位移、沉降提取时直接看y方向分量语义清晰。很多新手建模时坐标随手起最后提取地表沉降还要做坐标换算纯属给自己挖坑。衬砌部件就简单很多它是一个内半径tunnel_r、外半径tunnel_r liner_thickness的圆环。def create_liner_part(model, p): sketch model.ConstrainedSketch(nameLinerProfile, sheetSize50.0) sketch.CircleByCenterPerimeter(center(0.0, -p[tunnel_depth]), point1(p[tunnel_r], -p[tunnel_depth])) sketch.CircleByCenterPerimeter(center(0.0, -p[tunnel_depth]), point1(p[tunnel_r] p[liner_thickness], -p[tunnel_depth])) part model.Part(nameLiner, dimensionalityTWO_D_PLANAR, typeDEFORMABLE_BODY) part.BaseShell(sketchsketch) return part后续在装配中要把衬砌圆心移动到与土体洞室同心位置。因为两个Part的草图原点都是(0,0)而圆心在(0, -tunnel_depth)所以装配时需要给衬砌实例一个translate操作让它和洞室对齐。3.2 材料本构与截面赋参隧道围岩我一般用摩尔-库仑本构这是岩土工程中最常用的弹塑性模型之一。它的两个关键强度参数就是黏聚力和内摩擦角。Abaqus中设置摩尔-库仑模型需要先定义弹性阶段再定义塑性阶段def create_materials(model, p): # 围岩材料 soil_mat model.Material(nameSoilMC) soil_mat.Elastic(table((p[soil_E], p[soil_nu]),)) soil_mat.MohrCoulombPlasticity(table((p[soil_friction]*np.pi/180.0, 0.0),)) soil_mat.mohrCoulombStressInit(angle0.0, table((0.0,),)) # 可选配合地应力 # 通常还需要设置密度用于自重应力 soil_mat.Density(table((p[soil_density],),)) # 衬砌材料 liner_mat model.Material(nameLinerElastic) liner_mat.Elastic(table((p[liner_E], p[liner_nu]),)) liner_mat.Density(table((p[liner_density],),))这里要特别注意角度单位问题。Abaqus的摩尔-库仑模型要求内摩擦角以弧度传入而我在参数定义时用角度所以必须在传入时做转换p[soil_friction]*np.pi/180.0。这个细节非常容易踩坑一旦漏掉结果会完全不对——你以为是25度实际是25弧度塑性区形态直接乱套。截面赋参也很直接围岩用实体截面衬砌在二维平面应变模型里也是实体截面。如果做的是梁单元衬砌模型则需要用BeamSection并指定梁方向。二维隧道模拟中衬砌既可以用实体单元模拟也可以用梁单元模拟。实体单元的好处是能直接看衬砌内部的应力分布梁单元的好处是提取轴力和弯矩非常方便。参数化脚本里我通常预留一个liner_element_type开关方便切换。3.3 初始地应力平衡的实现细节隧道数值模拟里初始地应力平衡是决定成败的一步。基本原理是真实地层在自重作用下已经固结完成位移为零。但数值模型中你刚给材料赋上密度和弹性参数时模型还没经历过自重作用。如果你直接开挖计算得到的位移会包含土体在自重下瞬间压缩的那部分位移导致地表沉降和拱顶沉降的数值严重偏大无法判断是开挖引起的还是自重引起的。解决地应力平衡的通用做法是设置一个Geostatic分析步。在这个分析步中只施加重力载荷不施加任何其他作用让模型在自重下变形然后把这次变形后的应力场作为后续分析的初始应力状态同时把位移清零。在脚本里的实现大致如下def setup_steps(model, p): model.GeostaticStep(nameGeostatic, initialInc0.1, maxInc0.1, minInc1e-5) model.StaticStep(nameExcavation, previousGeostatic, initialInc0.01, maxInc0.1, minInc1e-8, maxNumInc1000)施加重力时ABAQUS用Gravity载荷注意施加载荷必须对整个装配体作用。重力加速度方向朝下即负y方向model.Gravity(stepGeostatic, comp2-9.8, distributionTypeUNIFORM, field)关于初始应力状态的预定义我通常用InitialConditions里的Stress类型按侧压力系数手动给出水平应力和竖向应力的梯度分布。竖向应力按土体容重乘以深度计算水平应力等于竖向应力乘以侧压力系数K0。# 以场变量方式定义初始应力 model.InitialConditions.Field(region..., stress...)不过更推荐的做法是分两步先跑一个纯土体自重模型提取稳定后的应力场再把该应力场作为开挖模型的初始应力。这样能严格保证初始应力场与模型完全匹配但操作路径较长。对于有隧道洞室的模型直接在带洞模型的Geostatic步中平衡也是可行的——洞室周边应力集中确实会使局部区域难以达到全模型位移完美归零但只要位移量级控制在毫米级以下对后续开挖结果的影响通常可以接受。我在脚本里加了一个检查计算完成后读取Geostatic步末的最大位移如果超过预设阈值就自动弹提醒。3.4 开挖与衬砌支护的step逻辑隧道开挖的数值模拟逻辑是在Excavation分析步中把洞室范围内的土体单元杀死用Model Change移除同时把衬砌单元激活。Abaqus中移除和激活都通过ModelChange来实现# 开挖移除洞内土体集合 model.ModelChange(stepExcavation, nameExcavate, regionsoil_excavation_region, activeFalse) # 支护激活衬砌集合 model.ModelChange(stepExcavation, nameActivateLiner, regionliner_region, activeTrue, stateACTIVE)这里要强调集合(Set)的创建时机。Set必须在网格划分完成后才能基于几何区域或单元区域创建。我通常是先划分网格再从土体中按几何位置选出洞室内部的单元创建为ExcavationSet衬砌部件的所有单元创建为LinerSet。选择单元时注意二维模型的单元是面单元要保证洞室边界上的单元也被选入开挖集合否则删除不干净模型会存在畸形单元影响收敛。实际隧道工程中初支和二衬的施作时序很复杂——先开挖、喷锚、立钢架再施作二衬。在参数化脚本里如果你需要模拟多步开挖可以按开挖进尺分成多个分析步每步删除一段围岩、激活一段衬砌。我的经验是二维模型中分一次开挖即可三维模型中如果模拟台阶法或分步开挖则需要把模型沿纵向切分成多个区段每个区段设置独立的Collection并在不同分析步中分别控制。这部分代码逻辑会复杂一些但参数化的思路不变每个区段的起始里程、进尺长度、衬砌施作滞后步数都是参数。4. 网格划分的参数化策略隧道周边加密与单元类型选择4.1 分区布种策略隧道模型的网格划分最忌讳一刀切。隧道开挖后围岩应力和变形主要集中在洞室周边约1~2倍洞径范围内这个区域必须加密远离洞室的土体应力梯度很小网格可以放得很粗否则计算量白白增加收敛性还可能变差。我在土体Part创建时就预先用草图线段把土体分成了两个区域近场区以隧道中心为圆心、半径为tunnel_r*3的圆形范围和远场区外部矩形减去近场区。分区可以有两种方式一种是在草图里画辅助分割线让面Part天然带有多个face另一种是直接在草图上画多个闭合区域。我更推荐后者因为后续布种非常方便。# 近场区种子 part.seedPartBySize(sizep[mesh_size_tunnel], deviationFactor0.1, constraintFINITE, regions(near_field_face,)) # 远场区种子 part.seedPartBySize(sizep[mesh_size_global], deviationFactor0.1, constraintFINITE, regions(far_field_face,))这样设置之后隧道附近网格自然加密向外逐渐过渡。网格尺寸的比值不宜超过1:4否则过度区域会产生畸变单元计算精度反而下降。经验上近场区1m、远场区4m的搭配针对半径5m的隧道效果很好比值为1:4刚好在经验范围内。4.2 单元类型的选择与剪切锁死问题二维平面应变隧道模型中土体和衬砌通常都用CPE4R四节点双线性平面应变缩减积分单元或CPE3三节点三角形单元。我一般首选CPE4RCPE4是全积分四节点单元在弯曲变形明显时容易出现剪切锁死导致单元刚度过大位移计算偏小CPE4R是缩减积分单元能有效缓解剪切锁死问题但需要注意沙漏模式网格不能太粗CPE3三角形单元刚度偏大精度较低建议只在形状复杂的过渡区域使用。如果模型涉及大变形或接触单元类型可能需要调整为CPE4RT带温度自由度或CPE4RP带孔隙压力自由度流固耦合时用。对于衬砌如果采用梁单元模拟则是B21二维两节点梁单元需要在截面设置中指定梁截面形状和方向。在进行网格划分时还要确认单元阶次。ABAQUS默认的线性单元可以更好地处理接触问题二阶单元精度高但在岩土开挖和接触模拟中容易导致接触收敛困难。所以隧道模型我几乎都用线性单元。这是一个基于实际收敛性的取舍而不是理论上的最优解。4.3 网格质量对收敛性的实际影响我调试过很多不收敛的隧道模型最后发现有一半的问题出在网格质量上。网格质量差主要表现为一是单元畸形。过度区域的长宽比过大或者角度过小导致雅可比行列式为负求解器在开始迭代前就报错。二是单元过度扭曲。开挖区域边界上的网格如果形状不规则删掉洞内单元后保留的边界单元可能形成尖角影响后续接触计算。三是单元尺寸跳跃过大。如果近场区0.5m、远场区10m这种1:20的跳跃会在过渡区产生极差的单元甚至直接失败。参数化脚本里我加了一个单元质量检查步骤用网格统计命令统计最小单元角度和最大长宽比如果超出阈值则打印警告并自动加密。这个检查在手动建模时很容易忽略但在批量计算中非常必要——你不可能每跑一个工况都打开界面看一遍网格。5. 批量工况计算与结果提取让参数化真正闭环5.1 多参数组合循环提交参数化的终极目标是批量计算。我的做法是在脚本主体中维护一个工况列表每个工况是参数字典的变体然后循环生成模型并提交分析。def generate_variations(base_params): variations [] for depth in [15.0, 20.0, 25.0, 30.0]: for cohesion in [20e3, 25e3, 30e3]: p base_params.copy() p[tunnel_depth] depth p[soil_cohesion] cohesion variations.append(p) return variations这里要注意Python字典的copy()是浅拷贝如果参数值中包含列表或嵌套字典需要深拷贝或显式重建。我在实际中碰到过一次两个工况共用了同一个母参数修改一个后另一个也跟着变排查了半天才发现是浅拷贝的问题。批量提交时可以选择串行或并行。如果模型规模不大我倾向于直接用ABAQUS的queue功能排队提交多个Job每个Job对应一组参数。但要注意Abaqus默认的临时文件和odb文件命名要唯一否则后一个Job会覆盖前一个结果。所以Job名必须包含参数标识例如tunnel_d20_c25这样的命名约定方便后期识别。5.2 从ODB批量提取围岩变形与衬砌内力计算完成后最耗时的其实是数据提取。如果每个Job都打开界面看云图、手动读数据批量计算节省的时间会在后处理阶段被找补回来。所以我的脚本里还包含了一段后处理代码用Abaqus的Odb模块批量读取结果。from odbAccess import openOdb def extract_results(job_name): odb_path job_name .odb odb openOdb(pathodb_path, readOnlyTrue) last_step odb.steps[odb.steps.keys()[-1]] frame last_step.frames[-1] # 提取拱顶下沉拱顶节点位移 # 提取地表沉降槽地表节点竖向位移 # 提取衬砌轴力如果需要通过截面输出 odb.close() return results提取节点位移时有一个常见的坑ABAQUS的位移输出默认包含刚体位移分量如果模型没有完全约束刚体位移提取的节点位移可能包含整体平移项。隧道模型里土体底部固定、两侧水平约束整体刚体位移已经被约束但数值由于地应力平衡不完美可能仍然有几个毫米的残余位移。做结果后处理时最好减去Geostatic步末的位移值这样得到的才是隧道开挖引起的净位移变化。对于衬砌内力的提取如果你用的是实体单元需要把衬砌某截面上各节点的应力分量做积分得到轴力和弯矩如果用梁单元ABAQUS的SectionPrint功能可以直接输出截面内力方便得多。我在参数化脚本里预留了liner_element_type参数就是为了在需要大量提取衬砌内力时切换到梁单元模型直接拿到轴力弯矩。5.3 结果自动整理成对比表提取完数据后我会让脚本自动生成一张汇总表以CSV格式输出。每个工况一行列包括埋深、黏聚力、拱顶沉降、拱底隆起、地表最大沉降、沉降槽宽度、衬砌轴力最大值、衬砌弯矩最大值等。有了这张表方案比选的效率就完全不一样了。以前我要翻十几个模型结果文件手动抄数据既慢又容易错现在脚本自动输出几十行结果直接用Excel透视表或绘图工具就能看出参数变化的规律。这里也分享一个经验批量计算之前先用一个基准参数组跑通全过程确认结果合理后再放量计算。不要一上来就生成几十个Job结果发现脚本有个小bug几十个模型全部白算。这个习惯帮我省下过无数时间。6. 参数化建模容易踩的坑收敛失败、地应力平衡失真、单位系统混乱6.1 收敛性调试的排查顺序隧道模型不收敛时一定不要慌张地调整某个参数而要按顺序排查。第一步看错误信息。ABAQUS的.msg和.sta文件会给出最直接的原因例如Too many attempts made for this increment说明增量步缩小到极限仍未收敛。第二步检查模型是否存在刚体位移或过约束。隧道开挖模拟中如果衬砌在某个方向没有受到足够约束它可能作为一个刚体滑移导致不收敛。这时检查边界条件和接触设置。第三步检查网格质量。特别是删除开挖区单元后坑内边界附近的单元是否有畸形。这个排查顺序能解决绝大多数收敛问题。还有一个经验把初始增量步调小从0.01降到0.001很多收敛问题会自动消失。隧道开挖是非线性很强的过程初始增量步设得太大迭代直接发散设小了虽然计算步骤多但至少能算完。6.2 地应力平衡效果怎么判断地应力平衡的好坏直接看Geostatic步末的最大竖向位移。如果位移在1毫米以内说明平衡效果很好如果达到厘米级说明平衡失败后续计算得到的位移都会严重失真。如果平衡失败常见原因有三个初始应力场与模型材料不匹配、侧压力系数取值不合理、边界条件约束过强或不足。我在脚本里固定了检查逻辑Geostatic步完成后自动读取最大位移如果超过1e-3米就打印警告提示需要调整初始应力设置。这里还要提一个要点带洞室模型的地应力平衡比纯土体模型更难平衡。因为洞室边界上的应力集中很严重即使初始应力整体合理洞室周边也难以完全零位移。工程上可以接受小量级残余位移但对结果影响必须心中有数。如果需要高质量结果建议采用先纯土体平衡、后开挖的两阶段策略虽然步骤多但效果最稳。6.3 单位系统与参数量纲这是所有参数化脚本里最容易出大问题、但又最容易被忽略的地方。Abaqus本身没有单位全靠用户自洽。我的隧道模型统一使用国际单位制长度用米应力用帕斯卡密度用千克每立方米时间用秒。这样重力加速度是9.8米每秒平方弹性模量是帕量级容重是2000千克每立方米量级。很多朋友习惯在界面里用毫米或兆帕这本身没问题但一旦进入参数化脚本单位不一致的后果会被批量放大你可能一个工况能用另一组参数就出现弹性模量差1000倍的bug。我在脚本头部用注释明确标注了所有参数的单位并且加了一组自动断言检查关键参数是否在物理合理区间内assert 1e7 p[soil_E] 1e10, 围岩弹性模量单位疑似错误 assert 5.0 p[soil_friction] 60.0, 内摩擦角度数疑似超出合理范围这组断言在批量计算前能拦截掉大部分低级错误强烈推荐大家在自己脚本里也加上。在实际操作中我个人还有一个小习惯每个参数化脚本都维护一个版本号并把版本号写入每个Job的名称后缀。这样一旦某批计算结果异常能追溯到具体是哪个版本的脚本生成的排查问题会非常快。参数化建模最大的好处是可复现——同一个脚本、同一组参数任何时候重新运行都能得到一致的结果这在学术研究和工程复盘中都极其珍贵。希望这篇基于隧道案例的实战分享能帮你把Abaqus建模从手工体力活真正变成参数化流水线。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑