Technical note
为什么 CAD/CAE 软件需要一个自己的几何智能层
从几何内核直接驱动业务时遇到的问题出发,讨论为什么 CAD/CAE 软件在 B-Rep 之上还需要一层长期稳定的拓扑、接口、区域与特征表达,以及它如何连接几何修复、共形网格和后续仿真流程。
背景:直接使用 B-Rep,在项目早期其实很自然
大多数 CAD 功能刚开始做时,最直接的方式就是操作几何内核里的 Shape。
例如:
STEP
↓
B-Rep Shape
↓
遍历 Solid / Face / Edge
↓
业务算法
对于建模、导入导出、简单修复,这种方式非常有效。
几何内核已经提供了成熟的:
拓扑结构;
曲线曲面;
Boolean;
Sewing;
求交;
离散;
几何检查。
项目早期完全没有必要再造一层抽象。
但随着功能越来越多,我开始反复遇到一个问题:
不同模块都在从同一份 B-Rep 里,
重新推断自己需要的业务关系。
显示模块在推断 Face 和 Triangle 的关系。
模型修复在推断缺陷区域。
网格模块在推断哪些 Body 接触、相交或共享接口。
聚合模块又在重新判断哪些拓扑应该保留、切分或融合。
这些判断看起来属于不同功能,但底层其实有很多重叠。
于是我开始思考:
在几何内核和上层业务之间,
是不是还缺少一层长期存在的数据结构?
几何内核知道拓扑,但不一定知道业务关系
B-Rep 本身已经是拓扑结构。
这一点不能混淆。
Solid、Shell、Face、Wire、Edge、Vertex 都是明确的拓扑对象。
所以这里说的“增加一层拓扑”,不是重新发明 B-Rep。
真正缺少的是:
面向当前软件业务的稳定解释。
例如几何内核可以告诉我们:
Face A 和 Edge E 邻接;
Solid A 包含哪些 Face;
两个 Shape 做 Boolean 后生成了哪些新 Shape。
但它不会天然告诉我们:
Face A 和 Face B 是仿真接口;
这一片薄面应该被视为一个可简化特征;
这几条碎边在网格阶段应该作为同一条逻辑边;
这个接触关系必须保留,但不一定需要立刻物化成新 B-Rep;
这个区域修改以后,哪些属性需要迁移。
这些信息不是底层几何事实。
它们是建立在几何事实之上的工程语义。
我越来越不希望每个模块重新遍历 Shape
当显示、修复、聚合和网格都直接访问底层 Shape 时,工程里很容易出现重复工作。
例如为了知道两个 Solid 是否需要进一步处理,不同模块可能分别执行:
包围盒判断;
Face 遍历;
几何距离;
Section;
共享拓扑判断。
同一份信息被多次计算。
更麻烦的是,不同模块可能使用不同判断标准。
于是会出现:
修复模块认为这两个区域相关;
聚合模块认为不相关;
网格模块再次做一套自己的判断。
这类问题如果只靠“统一函数”解决,能改善一部分。
但如果关系本身需要跨流程保存,就更适合把它变成数据。
例如:
Body A
└─ Interface 17
├─ Body B
├─ involved faces
├─ relation type
├─ confidence
└─ current state
这时上层模块消费的是同一份关系,而不是每次从零推导。
第一层应该还是忠实表达 Topology
如果真的增加一层几何底座,我觉得第一层仍然应该非常克制。
它不应该一开始就塞入大量“智能判断”。
最基础的一层仍然是:
Topology。
也就是对底层 B-Rep 的稳定引用和组织。
它至少需要表达:
Object;
Solid;
Shell;
Face;
Edge;
Vertex;
邻接;
包含;
来源;
版本。
这层的目的不是替代 OCCT。
而是把上层业务从:
TopoDS_Shape
这种具体实现类型里逐渐解耦出来。
对业务来说,更重要的应该是:
这是哪个拓扑对象;
它和谁邻接;
当前版本是否仍然有效;
如何回到底层几何。
至于底层实际由哪个 Kernel 提供,可以晚一点再决定。
Interface 是我认为最值得单独保存的一类关系
最近在处理共形网格和多体聚合时,我越来越觉得:
Interface
不应该只是某次算法运行中的临时结果。
两个 Body 之间可能存在:
共享面;
接触;
相交;
重叠;
间隙;
近接触。
这些关系对后续流程都有影响:
是否需要切分;
是否需要共形;
是否可以保持独立;
是否存在重复体域风险;
网格约束怎样传播。
如果这些关系每次都通过 Boolean 或 Section 重新计算,成本很高。
更重要的是,它们不一定都需要立即转换成真实拓扑修改。
这让我开始逐渐接受一种思路:
先保存关系,
再决定是否物化几何。
也就是类似 Interface Graph 的结构。
节点是几何域。
边描述几何域之间的接口关系。
这样后续算法可以围绕:
Interface
做局部处理,而不是每次对整个装配体执行一次全局 Boolean。
虚拓扑的价值不是“假的拓扑”
第一次接触虚拓扑这个概念时,很容易理解成:
不修改真实模型,
只在外面记几个 tag。
但真正有价值的地方不是“省掉一次 Boolean”。
而是把:
计算需要的拓扑
和
原始 CAD 必须物化的拓扑
分开。
例如为了生成共形网格,我们可能需要知道:
这两张面逻辑上属于同一个接口;
这条长边可以在网格层视为若干逻辑分段;
这几条碎边对网格来说应该作为一条连续边。
这些关系对网格很重要。
但不一定每一个都值得修改原始 B-Rep。
如果能让 Mesh 层理解这些逻辑关系,就可以避免一部分高风险的几何重建。
当然,这要求网格器也能够消费这种拓扑语义。
所以虚拓扑并不是一个单独功能。
它更像是几何底座和网格底座之间的一份协议。
Domain 解决的是“谁属于一个计算区域”
Interface 关注关系。
Domain 更关注:
计算区域。
一个 CAD 装配里,原始零件层次不一定等于最终仿真域。
例如:
多个零件可能共同形成一个材料域;
一个空气区域可能包围多个实体;
一些几何体只是辅助构造;
某些相交体需要在仿真前拆成互斥体域。
所以:
CAD Object
和
Simulation Domain
不应该强绑定。
Domain 层如果建立起来,上层就可以围绕“仿真区域”做操作,而不是永远围绕导入时的 Solid 列表。
这一点对后续网格、材料和求解准备都很重要。
Feature 不应该只理解成建模历史特征
这里的 Feature 也不是传统参数化 CAD 里的:
Extrude;
Fillet;
Chamfer。
至少在当前阶段,我更关心几何分析得到的结构特征。
例如:
小面;
薄桥;
狭长区域;
特征边;
孔;
局部高曲率区;
可能影响网格质量的碎特征。
这些 Feature 可以服务:
修复;
简化;
网格控制;
显示标记。
把它们单独保存以后,很多算法就不必反复从 Face/Edge 重新识别。
当然,Feature 是最容易“过度智能化”的一层。
所以我认为它应该建立在稳定几何证据上,并且允许明确表达:
置信度;
来源;
失效条件。
而不能把一次启发式判断永久当成事实。
四层之间的关系
如果把目前的想法简化,我更倾向于把几何底座理解成几类不同数据:
Topology
↓
底层几何和拓扑事实
Interface
↓
对象之间的接触、相交和共享关系
Domain
↓
面向计算的区域组织
Feature
↓
面向修复、简化和网格的结构特征
它们不是严格单向层级。
更准确地说,它们是围绕同一份几何建立的不同视图。
例如:
一个 Feature
可能引用若干 Face。
一个 Interface
也引用双方的 Face。
一个 Domain
包含若干 Topology Object,并由多个 Interface 和其他 Domain 相连。
这样上层业务就不再必须从 B-Rep 的树结构出发。
Geometry Kernel 仍然应该保留在最底层
有了这些中间结构,并不意味着几何内核不重要了。
恰恰相反。
真正的:
曲线;
曲面;
求交;
Boolean;
Sewing;
几何投影;
拓扑构造;
仍然需要成熟 Kernel。
我现在更倾向于把目标理解成:
隐藏依赖
而不是:
替代几何内核。
理想状态可能是:
Application
↓
Geometry Intelligence Layer
↓
Kernel Adapter
↓
OCCT / Other Kernel
这样业务代码逐渐依赖自己的数据契约。
底层 Kernel 仍然负责最困难的几何计算。
这和显示引擎把 OSG 隐藏在 SDK 后面其实有些类似。
这层数据必须支持版本和失效
几何关系不是永远不变的。
一次 Boolean、Repair 或局部重建以后:
Face 可能 Split;
Edge 可能 Merge;
Solid 可能替换;
Interface 可能失效;
Feature 可能消失。
所以如果建立自己的几何底座,不能把它当成静态缓存。
必须从一开始考虑:
版本;
依赖;
失效;
局部重建;
Topology History。
否则数据量越大,旧关系越容易残留。
这种脏数据比重新计算慢更危险。
因为它可能导致错误的网格或者错误的修复判断。
现在还不适合把它写成最终架构
目前这套想法还在快速演进。
尤其是:
Interface 如何稳定定义;
虚拓扑和真实拓扑如何切换;
Domain 怎样持久化;
Mesh 到底消费哪些中间层数据;
都还需要更多案例验证。
所以我现在不会把它称为某个已经定型的“标准架构”。
更准确的说法应该是:
随着显示、修复和网格功能越来越复杂,
我们开始逐渐把重复出现的几何关系从算法内部抽出来,
形成一个长期存在的中间层。
这一步是否最终会发展成完整的几何底座,还需要继续做。
但方向已经越来越清楚。
小结
直接使用 B-Rep 并没有错。
对于很多功能,它仍然是最可靠、最清楚的方式。
真正的问题出现在:
越来越多模块开始围绕同一份 B-Rep,
重复推导相同或相似的工程关系。
这时继续堆工具函数,能够解决局部问题,但很难形成长期稳定的数据基础。
所以我现在越来越希望在几何内核之上逐渐建立几类自己的表达:
Topology 表达几何事实;
Interface 表达对象关系;
Domain 表达计算区域;
Feature 表达工程特征。
底层 Kernel 继续负责真正的几何计算。
上层业务逐渐依赖自己的稳定语义。
如果这条路最终能够走通,那么显示、修复、聚合和网格就不再只是几套各自操作 Shape 的模块,而会开始共享同一份几何理解。
对我来说,这可能比再实现一个新的 Boolean 或 Repair 算法更重要。