Skip to content
Gains Summary
Main Navigation 首页 / Home
C++ 编程 / C++ Programming
系统与高性能 / Systems & Performance
Web 开发 / Web Development
人工智能 / Artificial Intelligence
工业软件 / Industrial Software
其他内容 / Other Topics
C++ 编程 / C++系统与性能 / SystemsWeb 开发 / Web人工智能 / AI工业软件 / Industrial

外观

Sidebar Navigation

← 工业软件 / Industrial Software

求解器工程 / Solver Engineering

1. 工业求解器工程:从物理方程到体系结构优化 / Industrial Solver Engineering from Physical Equations to Architecture Optimization

2. 生产级求解器架构:模块边界、执行管线与可观测性 / Production Solver Architecture with Module Boundaries, Pipelines, and Observability

3. 结构求解器设计:从单元积分到非线性、动力与接触 / Structural Solver Design from Element Integration to Contact and Dynamics

4. CFD 求解器设计:有限体积、压力速度耦合与并行时间推进 / CFD Solver Design with Finite Volumes and Pressure-Velocity Coupling

5. 求解器数据结构:稀疏矩阵、场布局、Cache、NUMA 与 Ghost 数据 / Solver Data Structures for Sparse Matrices, Fields, NUMA, and Ghost Data

6. CAE 性能分析:CPU、GPU、NUMA、MPI 与 I/O 工具链 / CAE Performance Analysis Across CPUs, GPUs, NUMA, MPI, and I/O

7. 体系结构驱动优化:从性能证据到算法、数据与并行处理 / Architecture-Driven Optimization from Evidence to Algorithms and Parallelism

8. 求解器优化案例:结构算子、CFD 通量与 MPI 强扩展 / Solver Optimization Cases for Structural Kernels, CFD Fluxes, and MPI Scaling

本页目录

生产级求解器架构:模块边界、执行管线与可观测性 / Production Solver Architecture with Module Boundaries, Pipelines, and Observability ​

📅 创建时间:2026-07-23
🏷️ 标签:#求解器架构 #数据模型 #线性代数后端 #可观测性
📚 前置知识:[[00-solver-engineering-overview]] [[../01-foundations-and-architecture/09-architecture]]
📚 相关知识:[[../marine-cae/02-cae-workflow]] [[/01-cpp/10-plugin-engineering/07-plugin-interface-design]]


1. 从算法原型到工业软件 ​

教学程序常把所有步骤写在一个函数里:

text
读网格 → 组装矩阵 → 调求解器 → 写结果
1

工业软件需要长期支持不同单元、物理模型、硬件后端、文件格式和版本,因此必须把“变化原因不同”的部分分开:

  • 网格拓扑不应依赖具体线性求解器;
  • 物理模型不应直接调用某个厂商的稀疏矩阵 API;
  • 非线性收敛控制不应散落在材料和单元代码中;
  • 输出模块不应决定计算过程中的数据布局;
  • MPI、GPU 等执行细节应通过清晰边界进入,而不是形成全局条件分支。

2. 端到端执行管线 ​

text
1. 解析输入和版本迁移
2. 单位、拓扑、材料和边界条件检查
3. 建立网格、区域、场变量和自由度
4. 构造邻接关系与稀疏模式
5. 初始化物理模型、求解器和并行环境
6. 进入载荷步、时间步或耦合步
7. 组装矩阵/右端项,或计算残差/算子作用
8. 求解线性或特征值子问题
9. 更新状态并检查非线性、时间和物理收敛
10. 输出监控量、结果和 Checkpoint
11. 完成统计、释放资源并生成运行摘要
1
2
3
4
5
6
7
8
9
10
11

这个流程应由高层 Driver 控制。底层 Kernel 只完成局部计算,不决定什么时候结束整个求解。

3. 核心数据模型 ​

3.1 Mesh ​

负责节点、单元、Cell、Face、拓扑连接、边界区域、材料区域、分区、局部到全局编号以及 Ghost/Halo 实体。面积、体积、法向和 Jacobian 等几何量可以按生命周期缓存。

Mesh 不应该存储所有物理场的业务含义,否则每增加一个模型都要修改网格核心。

3.2 Field ​

Field 描述“某个量在哪里、多少分量、什么精度、由谁拥有”:

text
Field
├─ Location: Node / Element / Cell / Face / IntegrationPoint
├─ Components: 1 / 3 / 6 / ...
├─ ScalarType: float / double
├─ MemorySpace: Host / Device
├─ Ownership: Owned / Ghost
└─ Layout: AoS / SoA / AoSoA
1
2
3
4
5
6
7

温度、位移、速度、压力、应力和湍流变量可以使用同一套 Field 抽象,但具有不同语义和更新规则。

3.3 DofManager ​

结构、热、电磁等有限元问题需要 DofManager:

  • 注册节点或单元上的自由度;
  • 生成局部到全局方程编号;
  • 处理固定、耦合和多点约束;
  • 提供单元自由度列表;
  • 为稀疏模式构造邻接;
  • 在 MPI 中维护本地与全局编号映射。

编号策略会影响矩阵带宽、Fill-in、Cache 局部性和分区通信,因此它不是纯粹的“编号工具”。

4. 计算组件边界 ​

4.1 PhysicsModel ​

定义材料本构、湍流模型、源项和边界通量。它应输入明确的局部状态,输出局部残差、切线矩阵或通量,不直接管理全局求解循环。

4.2 Operator ​

Operator 表示代数作用:

text
y = A(x)
1

它可以由显式稀疏矩阵实现,也可以是 Matrix-Free 算子。Krylov 求解器只依赖 Operator 接口,因而不必知道矩阵是否真正存储。

4.3 Assembler ​

Assembler 负责把局部贡献放入全局对象:

  • 单元矩阵和载荷组装;
  • CFD 面通量对 owner/neighbour 的贡献;
  • 边界条件修改;
  • 原子操作、图着色或线程局部缓冲;
  • MPI 本地贡献与 Ghost 更新。

局部物理计算和全局写入策略应分开,便于在串行、OpenMP 和 GPU 后端之间切换。

4.4 LinearSolver 与 Preconditioner ​

典型接口需要区分不同生命周期:

text
analyzePattern(A)     稀疏结构分析,可跨多个载荷步复用
factorize/setup(A)    数值分解或预条件建立
solve(A, x, b)        求解
update(A)             系数变化后的增量更新
statistics()          迭代、残差、时间和内存
1
2
3
4
5

直接法尤其需要区分符号分解和数值分解;多右端项场景中复用因子可以带来巨大收益。

4.5 NonlinearDriver ​

负责载荷增量、Newton 或 Picard 外层迭代、状态提交与回滚、收敛准则、线搜索、阻尼和失败重试。

材料模型只提供试算状态与切线。是否提交塑性历史变量由 Driver 决定,否则失败迭代可能污染状态。

4.6 TimeIntegrator ​

时间积分器负责时间离散系数和历史状态,例如 Newmark、Generalized-α、BDF 和 Runge-Kutta。它不应直接实现具体物理残差,而是向物理模型提供当前时间、阶段和历史组合。

5. 高层伪代码 ​

cpp
Model model = ModelReader::load(config.input);
ValidationReport report = validate(model, config);
report.requireNoFatalErrors();

Runtime runtime(config.parallel);
SolverContext ctx(model, runtime);
ctx.initializeFields();
ctx.buildTopology();
ctx.buildAlgebra();

for (Step step : ctx.stepController()) {
    ctx.beginStep(step);

    while (!ctx.convergence().finished()) {
        ctx.restoreTrialState();
        ctx.assembler().evaluate(ctx.state(), ctx.system());
        ctx.boundaries().apply(ctx.system());

        SolveReport linear = ctx.linearSolver().solve(
            ctx.system().op, ctx.increment(), ctx.system().rhs);

        ctx.updateState(linear);
        ctx.convergence().observe(ctx.metrics());
    }

    ctx.commitStep();
    ctx.resultWriter().writeIfScheduled(ctx);
    ctx.checkpoint().saveIfScheduled(ctx);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

这里最重要的不是类名,而是状态生命周期、求解控制和局部计算之间存在明确边界。

6. 线性代数后端适配 ​

工业求解器可能支持自研 CSR/BSR、Eigen、oneMKL、PETSc、Trilinos、Hypre、cuSPARSE、cuSOLVER、MUMPS 或 PARDISO。

推荐分层:

text
Physics / Discretization
        ↓
项目内部 Vector / Operator / Solver 接口
        ↓
Backend Adapter
        ↓
PETSc / Hypre / MKL / CUDA / 自研实现
1
2
3
4
5
6
7

适配层统一生命周期、错误码、精度、索引宽度和统计信息。不要把 PETSc 对象或 CUDA 指针扩散到所有业务模块,否则后端替换和单元测试都会很困难。

7. 插件和注册机制 ​

适合插件化的变化点包括单元类型、积分规则、材料本构、边界条件、湍流与多相模型、线性求解器、预条件器、派生结果和输出格式。

注册信息至少应包括:

  • 稳定名称和版本;
  • 支持的空间维度与分析类型;
  • 所需输入字段和生成字段;
  • 参数 Schema、默认值和单位;
  • CPU/GPU、单精度/双精度等能力约束;
  • 序列化和版本迁移规则。

加载时先验证兼容性,避免求解中途才发现字段缺失。

8. 状态、失败和恢复 ​

8.1 状态分层 ​

text
Committed State  上一个成功步,允许恢复
Trial State      当前非线性迭代的试算状态
Scratch State    Kernel 临时量,不进入 Checkpoint
1
2
3

接触、塑性、损伤和湍流模型都可能有历史状态。必须明确何时复制、何时提交、何时回滚。

8.2 失败分类 ​

类型例子合理处理
输入错误单位缺失、负材料参数求解前停止并定位字段
数值失败线性不收敛、矩阵奇异输出矩阵和收敛证据,尝试受控降步
资源失败内存不足、设备丢失保存可恢复状态,报告资源需求
通信失败Rank 异常、文件系统超时一致终止或从 Checkpoint 重启
软件缺陷NaN 来源不明、越界保留最小复现和诊断快照

不能用无限重试掩盖输入或软件错误。失败处理必须确保所有 MPI Rank 对下一步动作达成一致。

9. Checkpoint 与可重复性 ​

Checkpoint 应记录:

  • 网格和分区标识;
  • 当前步、时间和非线性迭代状态;
  • 所有必须的历史场;
  • 求解器配置、容差和版本;
  • 随机种子以及必要的并行元数据。

并行浮点归约次序不同可能产生微小差异。对强非线性或混沌问题,应定义“允许的工程等价”,而不是要求每一位二进制完全相同。回归测试同时比较残差历史、关键工程量和守恒误差。

10. 可观测性 ​

10.1 阶段级 ​

  • 网格读取、初始化、组装、线性求解、后处理、I/O;
  • 每个载荷步或时间步的耗时;
  • 峰值内存和显存。

10.2 算法级 ​

  • 外层与线性迭代次数;
  • 残差和增量历史;
  • 预条件建立和应用时间;
  • 接触对、非正交修正次数等模型指标。

10.3 并行级 ​

  • 各线程或 Rank 的工作量;
  • Halo、集合通信和 Barrier 时间;
  • GPU Kernel、拷贝和同步时间;
  • 各 Rank 输出量与 I/O 等待。

所有统计应能关联到同一个 Run ID、输入摘要和配置快照。高频 Kernel 中不要逐实体输出日志,应使用计数器、采样或聚合统计。

11. 配置快照与可追溯性 ​

一次可复现实验至少保存:

text
输入模型哈希
求解器版本和 Git 提交
编译器与优化选项
依赖库、驱动和 MPI 版本
硬件拓扑
线程、Rank 和设备绑定
求解容差与停止准则
输出与 Checkpoint 策略
1
2
3
4
5
6
7
8

只保存图形界面截图不能复现实验。GUI 最终应生成稳定、可比较的配置表示。

12. 架构审查清单 ​

  • [ ] 网格拓扑是否独立于具体物理模型?
  • [ ] 物理 Kernel 是否能在不启动完整求解器时测试?
  • [ ] 非线性 Driver 是否统一管理试算、提交和回滚?
  • [ ] 线性代数后端是否通过适配层隔离?
  • [ ] 是否区分符号结构、数值系数和右端项的生命周期?
  • [ ] CPU/GPU 之间是否存在隐式数据拷贝?
  • [ ] 并行归约和错误处理是否保持所有 Rank 一致?
  • [ ] Checkpoint 是否足以从失败点继续?
  • [ ] 日志是否能解释一次求解为什么慢或为什么不收敛?
  • [ ] 性能计时是否默认关闭昂贵的逐实体日志?

13. 架构诊断三问 ​

观察到什么说明什么下一步检查什么
更换线性代数后端需要修改物理模块后端类型已经泄漏到业务层Operator、Vector 和 Adapter 边界
失败增量后材料状态异常Trial/Committed 生命周期混乱状态提交、回滚和深浅复制
Restart 后结果与连续运行差异大Checkpoint 信息不完整或恢复顺序错误历史场、时间积分状态和配置快照
无法解释某一步突然变慢可观测性粒度不足Step、方程、Rank 和 Kernel 统计
GPU 版本存在大量隐式拷贝Field 所有权与 MemorySpace 不清晰Host/Device 权威副本和同步点

14. 核心结论 ​

生产级求解器的核心不是“调用一个线性求解库”,而是管理数据、状态、算法和硬件的生命周期。好的边界使物理模型、数值算法和执行后端能够独立演进;坏的边界会把一次局部优化变成全系统重写。

下一篇:[[02-structural-solver-design]]

最后更新于:

Pager
上一篇1. 工业求解器工程:从物理方程到体系结构优化 / Industrial Solver Engineering from Physical Equations to Architecture Optimization
下一篇3. 结构求解器设计:从单元积分到非线性、动力与接触 / Structural Solver Design from Element Integration to Contact and Dynamics

持续记录,持续成长

Copyright © Tidenflow