初始化项目:规划文档 + FFU 洁净室流场实时仿真 Demo
- 规划文档 01-07:愿景 / 架构 / 路线图 / 数据与AI / 风险 / 参考 / FFU Demo - 演示图:系统架构、数据流水线、里程碑路线图(SVG + PNG) - FFU Demo:D2Q9 LBM 高保真(360x270, 76s)+ 粗网格快速代理(120x90, 7.3s, 16-27x 加速) - 趋势验证:速度 r=0.95 / 粒子 r=0.83 / 温度 r=0.68 - 工具链:仿真脚本、快速代理、图表生成器、SVG->PNG 转换
@@ -0,0 +1,18 @@
|
|||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
|
||||||
|
# 仿真输出(可由脚本重新生成)
|
||||||
|
docs/assets/*.npz
|
||||||
|
|
||||||
|
# Node
|
||||||
|
node_modules/
|
||||||
|
package-lock.json
|
||||||
|
|
||||||
|
# 编辑器 / 系统
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
# PhysicsAiFluentAnalysis
|
||||||
|
|
||||||
|
> 物理世界流场实时仿真 —— 用 AI 神经算子把高保真 CFD 压缩到毫秒级推理。
|
||||||
|
|
||||||
|
[]()
|
||||||
|
[]()
|
||||||
|
[]()
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一句话定义
|
||||||
|
|
||||||
|
以 **Ansys Fluent 高保真仿真为真值**,训练 **物理约束的 AI 代理模型(神经算子)**,在保持物理一致性的前提下,把小时级的流场计算压缩到 **毫秒级实时推理**,支撑数字孪生、工程设计优化与沉浸式可视化。
|
||||||
|
|
||||||
|
## 为什么这件事"炸裂"又"难"
|
||||||
|
|
||||||
|
| 维度 | 现状 | 目标 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 单次 CFD 仿真 | 小时 ~ 天级 | 毫秒级(>1000x 加速) |
|
||||||
|
| 参数扫描 | 一次一个工况 | 全参数空间连续可微 |
|
||||||
|
| 交互性 | 离线出报告 | 实时拖拽参数、流场即时响应 |
|
||||||
|
| 物理可信度 | 求解器保证 | 物理约束 + 守恒律 + 不确定性量化 |
|
||||||
|
|
||||||
|
**难在哪:** Navier-Stokes 方程的非线性与多尺度特性、湍流闭合问题、AI 长时推演的误差累积与发散、复杂几何的泛化、3D 大网格的实时推理算力瓶颈。这不是"套一个模型"能解决的,需要 **数据流水线 + 物理约束 + 工程加速 + 严格验证** 四线并进。
|
||||||
|
|
||||||
|
## 已落地 Demo:FFU 洁净室流场实时仿真
|
||||||
|
|
||||||
|
FFU(风机过滤单元)下方洁净区的流场、温湿度、压力与尘埃粒子输运 —— 同场景提供 **高保真真值** 与 **快速代理** 两条路径。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
| | 高保真真值 | 快速代理 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 网格 / 步数 | 360×270 / 14000 步 | 120×90 / 5000 步(同源求解器) |
|
||||||
|
| 端到端耗时 | **76 s** | **7.3 s(16-27x 加速)** |
|
||||||
|
| 趋势一致性 | 基准 | 速度 **r=0.95** · 粒子 r=0.83 · 温度 r=0.68 |
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**主要趋势**:单向下扫排污染 → 绕障/缝隙加速(局部 ~1.5 m/s)→ 背风侧死角堆积滞留 → 送风正压/回风负压(< 2 Pa)。
|
||||||
|
|
||||||
|
详见 [docs/07-ffu-demo.md](docs/07-ffu-demo.md)。
|
||||||
|
|
||||||
|
## 系统总览
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 技术路线(一图流)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 里程碑路线图
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 文档导航
|
||||||
|
|
||||||
|
| 文档 | 内容 |
|
||||||
|
| --- | --- |
|
||||||
|
| [docs/01-vision.md](docs/01-vision.md) | 愿景、痛点、目标与非目标、成功标准(KPI) |
|
||||||
|
| [docs/02-architecture.md](docs/02-architecture.md) | 分层架构、数据流、技术选型与对比 |
|
||||||
|
| [docs/03-roadmap.md](docs/03-roadmap.md) | 7 个阶段的目标、交付物与验收标准 |
|
||||||
|
| [docs/04-data-and-ai.md](docs/04-data-and-ai.md) | Fluent 数据工厂、模型选型、训练与评估方案 |
|
||||||
|
| [docs/05-risks.md](docs/05-risks.md) | 技术风险、缓解策略与开放问题 |
|
||||||
|
| [docs/06-references.md](docs/06-references.md) | 论文、开源工具与数据集索引 |
|
||||||
|
| [docs/07-ffu-demo.md](docs/07-ffu-demo.md) | FFU 洁净室 Demo:物理设置、趋势验证、复现命令 |
|
||||||
|
|
||||||
|
## 仓库结构
|
||||||
|
|
||||||
|
```
|
||||||
|
PhysicsAiFluentAnalysis/
|
||||||
|
├── README.md # 项目总览(本文件)
|
||||||
|
├── package.json # 图件工具依赖 (SVG -> PNG)
|
||||||
|
├── docs/ # 规划文档 + 演示图
|
||||||
|
│ ├── 01-vision.md ~ 07-ffu-demo.md
|
||||||
|
│ └── assets/ # PNG / SVG 演示图
|
||||||
|
│ ├── architecture.* 系统架构图
|
||||||
|
│ ├── pipeline.* 数据与训练流水线
|
||||||
|
│ ├── roadmap.* 里程碑路线图
|
||||||
|
│ ├── ffu-flow-field.* FFU 流场(高保真 LBM)
|
||||||
|
│ ├── ffu-scalar-fields.png 温湿度/压力/粒子(高保真)
|
||||||
|
│ ├── ffu-particle-tracks.png 粒子轨迹与滞留时间
|
||||||
|
│ ├── ffu-flow-field-fast.png 快速代理流场(7 s)
|
||||||
|
│ ├── ffu-scalar-fields-fast.png 快速代理标量场
|
||||||
|
│ └── ffu-validation.png 趋势验证对比图
|
||||||
|
└── tools/ # 可复现的生成脚本
|
||||||
|
├── ffu_simulation.py # 高保真 D2Q9 LBM + 标量输运(76 s)
|
||||||
|
├── ffu_surrogate.py # 快速代理(7 s)+ 趋势验证
|
||||||
|
├── gen_diagrams.py # 架构/流水线/路线图 SVG 生成
|
||||||
|
└── svg2png.js # SVG -> PNG 批量转换
|
||||||
|
```
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 依赖: Python 3.11+ (numpy / numba / matplotlib), Node 18+ (仅图件转换需要)
|
||||||
|
|
||||||
|
# 1) 快速代理出图(约 7 秒,含趋势验证对比图)
|
||||||
|
python tools/ffu_surrogate.py
|
||||||
|
|
||||||
|
# 2) 高保真真值(约 76 秒,输出 3 张图 + npz 场数据)
|
||||||
|
python tools/ffu_simulation.py
|
||||||
|
|
||||||
|
# 3) 规划图(架构 / 流水线 / 路线图)
|
||||||
|
python tools/gen_diagrams.py
|
||||||
|
|
||||||
|
# 4) SVG -> PNG
|
||||||
|
npm install && node tools/svg2png.js
|
||||||
|
```
|
||||||
|
|
||||||
|
## 当前状态
|
||||||
|
|
||||||
|
**P0 规划期**:愿景、架构、路线图、风险分析完成;FFU 洁净室 Demo 已可运行(高保真 + 快速代理双路径 + 趋势验证)。
|
||||||
|
**下一步**:进入 P1,完成 2D 圆柱绕流 POC(Fluent 数据 → FNO 基线 → 误差评估)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*本项目处于早期规划阶段,欢迎讨论与共建。*
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
# 01 · 愿景与目标
|
||||||
|
|
||||||
|
## 1. 背景
|
||||||
|
|
||||||
|
计算流体力学(CFD)是现代工程的核心工具:从飞行器气动设计、汽车风阻优化,到血管血流分析、城市风环境评估,都依赖数值求解 Navier-Stokes 方程。Ansys Fluent 是工业界最成熟的 CFD 求解器之一,提供从 RANS 到 LES 的完整湍流建模能力。
|
||||||
|
|
||||||
|
但 CFD 有一个根本性的效率瓶颈:
|
||||||
|
|
||||||
|
- **单次高保真仿真成本高**:一个复杂 3D 工况动辄数小时到数天;
|
||||||
|
- **参数扫描是组合爆炸**:设计空间中每换一组参数(雷诺数、攻角、几何、边界条件)就要重算一遍;
|
||||||
|
- **无法交互**:工程设计评审时无法"拖动参数、流场即时响应"。
|
||||||
|
|
||||||
|
与此同时,AI for Science 在过去五年出现了范式级突破:**神经算子(Neural Operator)** 能直接学习"函数 → 函数"的映射,一次训练即可在参数空间中连续插值/外推,推理成本只与网络前向相当。这为"实时流场"提供了物理上可信、工程上可行的新路径。
|
||||||
|
|
||||||
|
## 2. 愿景
|
||||||
|
|
||||||
|
> **让流场仿真像游戏一样实时、像求解器一样可信。**
|
||||||
|
|
||||||
|
构建一条从 **高保真仿真 → AI 代理模型 → 实时交互系统** 的完整闭环链路,使工程师可以在秒级完成过去需要数天的设计迭代,并在数字孪生场景中实现"传感器数据驱动 + 物理模型约束"的在线流场重建。
|
||||||
|
|
||||||
|
## 3. 目标
|
||||||
|
|
||||||
|
### 3.1 技术目标
|
||||||
|
|
||||||
|
| 编号 | 目标 | 量化指标 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| G1 | 训练数据自动化生产 | PyFluent 批量仿真流水线,无人值守生成 ≥ 10⁴ 工况 |
|
||||||
|
| G2 | 2D 基准问题精度 | 圆柱绕流速度场相对 L2 误差 < 5% |
|
||||||
|
| G3 | 3D 复杂几何精度 | 汽车/翼型外流场相对 L2 误差 < 8%,关键气动力系数误差 < 5% |
|
||||||
|
| G4 | 实时推理 | 单步推理延迟 < 10 ms(3D 中等规模网格),较 Fluent 加速 > 1000x |
|
||||||
|
| G5 | 长时稳定 | 自回归推演 1000+ 步不发散,质量/动量守恒误差 < 1% |
|
||||||
|
| G6 | 泛化能力 | 未见雷诺数/几何族上误差退化 < 2 倍 |
|
||||||
|
| G7 | 可信度 | 输出带不确定性量化,OOD 检测召回率 > 90% |
|
||||||
|
|
||||||
|
### 3.2 工程目标
|
||||||
|
|
||||||
|
- 提供可交互的实时可视化前端(WebGPU/WebGL),支持参数拖拽与流场即时更新;
|
||||||
|
- 推理服务容器化,支持边缘/云端两种部署形态;
|
||||||
|
- 全链路可复现:数据版本、模型版本、指标看板。
|
||||||
|
|
||||||
|
### 3.3 应用目标
|
||||||
|
|
||||||
|
| 场景 | 价值 |
|
||||||
|
| --- | --- |
|
||||||
|
| 数字孪生 | 传感器稀疏观测 + AI 流场重建,在线监测与预警 |
|
||||||
|
| 工程设计 | 实时参数扫描,设计空间探索效率提升 100x |
|
||||||
|
| 教学科研 | 流场"可触摸",物理直觉可视化 |
|
||||||
|
| 实时决策 | 风场/流场的秒级预测,支撑调度与控制 |
|
||||||
|
|
||||||
|
## 4. 非目标(明确不做)
|
||||||
|
|
||||||
|
- **不替代 Fluent 的求解精度**:本项目定位是"代理模型 + 加速器",最终验证与认证仍由传统 CFD 完成;
|
||||||
|
- **不做通用流体求解器**:聚焦特定问题族(外流场/内流场),不追求覆盖所有流体现象;
|
||||||
|
- **不做实时全分辨率 3D LES**:以潜空间/降阶 + 代理模型实现"感知级实时",而非"DNS 级实时";
|
||||||
|
- **首期不做多物理场耦合**:传热、燃烧、多相流为远期扩展。
|
||||||
|
|
||||||
|
## 5. 成功标准(阶段性 KPI)
|
||||||
|
|
||||||
|
**P1 结束(3 个月)——可行性验证通过**
|
||||||
|
|
||||||
|
- [ ] 完成 2D 圆柱绕流数据集(≥ 500 工况,Re ∈ [50, 300])
|
||||||
|
- [ ] FNO 基线模型相对 L2 误差 < 10%
|
||||||
|
- [ ] 单步推理 < 5 ms(2D,256² 网格)
|
||||||
|
- [ ] 评估报告:精度 / 稳定性 / 泛化三维度量化
|
||||||
|
|
||||||
|
**P2 结束(6 个月)——系统闭环**
|
||||||
|
|
||||||
|
- [ ] 端到端流水线自动化(采样 → 仿真 → 训练 → 部署)
|
||||||
|
- [ ] 3D 翼型/简化车体代理模型相对 L2 误差 < 10%
|
||||||
|
- [ ] 实时可视化 Demo:参数拖拽 → 流场更新延迟 < 100 ms
|
||||||
|
|
||||||
|
**P3 结束(12 个月)——数字孪生原型**
|
||||||
|
|
||||||
|
- [ ] 稀疏观测数据同化在线更新
|
||||||
|
- [ ] 不确定性量化 + OOD 检测上线
|
||||||
|
- [ ] 与 Fluent 真值在 ≥ 3 个工业场景对标验证
|
||||||
|
|
||||||
|
## 6. 判断准则(Decision Gates)
|
||||||
|
|
||||||
|
每个阶段结束设置 Go / No-Go 决策点:
|
||||||
|
|
||||||
|
1. **精度门禁**:误差指标未达标则不允许进入下一阶段,先做消融分析;
|
||||||
|
2. **物理门禁**:守恒律破坏或长时发散直接视为失败;
|
||||||
|
3. **成本门禁**:单工况训练数据成本需低于设定阈值(否则先优化数据策略);
|
||||||
|
4. **可复现门禁**:任何指标必须可由流水线一键复现。
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
# 02 · 系统架构与技术选型
|
||||||
|
|
||||||
|
## 1. 分层架构
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
系统分为六层,自下而上:
|
||||||
|
|
||||||
|
### L0 高保真真值层(Ground Truth)
|
||||||
|
|
||||||
|
| 组件 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| Ansys Fluent | 高保真求解器:RANS(k-ε / k-ω SST)、URANS、LES/DES |
|
||||||
|
| PyFluent | Python 驱动 Fluent,实现参数化建模、求解、后处理全自动化 |
|
||||||
|
| 参数化几何 | 基于参数脚本的几何生成(翼型族、车体变体、管道变径) |
|
||||||
|
| 边界条件库 | 入口速度/湍流度、出口压力、壁面函数等工况模板 |
|
||||||
|
|
||||||
|
### L1 数据与训练层
|
||||||
|
|
||||||
|
| 组件 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| 数据湖 | HDF5(训练样本)/ VTK(可视化)/ Zarr(云原生分块) |
|
||||||
|
| 数据工程 | 网格插值统一、无量纲化、场归一化、增广(旋转/镜像/缩放) |
|
||||||
|
| 训练框架 | PyTorch + AMP + DDP/FSDP,支持多卡与梯度检查点 |
|
||||||
|
| 主动学习 | 基于预测不确定性挑选高价值工况回灌 Fluent 重算 |
|
||||||
|
|
||||||
|
### L2 AI 模型层
|
||||||
|
|
||||||
|
| 模型 | 定位 | 适用场景 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| FNO(傅里叶神经算子) | 主力基线 | 规则网格、2D/3D 流场、分辨率无关 |
|
||||||
|
| DeepONet | 算子学习备选 | 低维参数空间、传感器布局优化 |
|
||||||
|
| PINN | 物理约束补充 | 少数据/无数据工况、反问题 |
|
||||||
|
| MeshGraphNet | 非结构网格 | 复杂几何、工业网格 |
|
||||||
|
| Transformer 算子 | 长程依赖 | 大尺度湍流结构、长时推演 |
|
||||||
|
|
||||||
|
### L3 推理服务层
|
||||||
|
|
||||||
|
| 组件 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| 推理引擎 | TensorRT / ONNX Runtime,FP16/INT8 量化 |
|
||||||
|
| 时间推进 | 自回归推演 + 潜空间动力学,支持变时间步 |
|
||||||
|
| 后处理 | 散度修正(投影法)、守恒律校正、平滑滤波 |
|
||||||
|
| 不确定性 | 深度集成(Deep Ensemble)/ MC-Dropout,输出置信区间 |
|
||||||
|
| 服务接口 | gRPC(内部)+ WebSocket(前端流式推送) |
|
||||||
|
|
||||||
|
### L4 交互与渲染层
|
||||||
|
|
||||||
|
| 组件 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| 前端渲染 | WebGPU(three.js / VTK.js),支持体渲染、流线、矢量场 |
|
||||||
|
| 参数控制 | 实时拖拽 Re/攻角/几何参数,防抖 + 插值过渡 |
|
||||||
|
| 场景管理 | 视角预设、剖切面、等值面、动画时间轴 |
|
||||||
|
|
||||||
|
### L5 应用层
|
||||||
|
|
||||||
|
数字孪生监控、工程设计优化、教学演示、实时决策支持。
|
||||||
|
|
||||||
|
## 2. 数据流
|
||||||
|
|
||||||
|
```
|
||||||
|
参数采样 ──► PyFluent 批量仿真 ──► 原始结果(VTK/CAS) ──► 数据工程(HDF5)
|
||||||
|
▲ │
|
||||||
|
│ ▼
|
||||||
|
主动学习回灌 ◄── 不确定性评估 ◄── 模型评估 ◄── 训练(PyTorch) ◄── 训练集构建
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
导出 ONNX ──► TensorRT 引擎 ──► 推理服务
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
WebSocket ──► WebGPU 前端实时渲染
|
||||||
|
```
|
||||||
|
|
||||||
|
**关键数据契约:**
|
||||||
|
|
||||||
|
| 阶段 | 格式 | 内容 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 仿真输出 | .cas/.dat + .vtk | 网格 + 稳态/瞬态场(U, p, k, ω) |
|
||||||
|
| 训练样本 | HDF5 | 输入参数 + 网格坐标 + 场量张量 + 元数据 |
|
||||||
|
| 模型产物 | .pt → .onnx → .engine | 权重 + 归一化统计 + 版本信息 |
|
||||||
|
| 服务响应 | 二进制帧 | 场量数组 + 时间戳 + 不确定性 |
|
||||||
|
|
||||||
|
## 3. 技术选型对比
|
||||||
|
|
||||||
|
### 3.1 模型路线
|
||||||
|
|
||||||
|
| 方案 | 精度潜力 | 推理速度 | 数据需求 | 复杂几何 | 结论 |
|
||||||
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
| 传统降阶(POD-Galerkin) | 中 | 极快 | 中 | 差 | 作为基线对照 |
|
||||||
|
| FNO | 高 | 快 | 中高 | 差(需规则网格) | **主力** |
|
||||||
|
| DeepONet | 中高 | 快 | 中 | 中 | 备选 |
|
||||||
|
| PINN | 中 | 慢(需重训) | 极低 | 中 | 物理约束模块 |
|
||||||
|
| MeshGraphNet | 高 | 中 | 高 | **优** | 复杂几何阶段主力 |
|
||||||
|
| Transformer 算子 | 高 | 中 | 极高 | 中 | 探索性 |
|
||||||
|
|
||||||
|
### 3.2 加速与部署
|
||||||
|
|
||||||
|
| 选项 | 收益 | 成本 | 结论 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| ONNX Runtime | 跨平台、易部署 | 加速有限 | 开发期默认 |
|
||||||
|
| TensorRT FP16 | 2-4x | 绑定 NVIDIA | **生产环境** |
|
||||||
|
| TensorRT INT8 | 4-8x | 精度损失需校准 | 边缘部署评估 |
|
||||||
|
| CUDA Graphs | 消除启动开销 | 实现复杂 | 小模型场景启用 |
|
||||||
|
|
||||||
|
### 3.3 可视化
|
||||||
|
|
||||||
|
| 选项 | 优势 | 劣势 | 结论 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| VTK.js + WebGPU | 科学可视化生态完整 | 学习曲线陡 | **首选** |
|
||||||
|
| three.js | 生态活跃、上手快 | 科学可视化组件少 | 轻量场景 |
|
||||||
|
| ParaView Web | 功能最强 | 部署重 | 内部调试 |
|
||||||
|
| Unity/UE | 渲染效果最好 | 工程链路重 | 演示视频 |
|
||||||
|
|
||||||
|
## 4. 工程规范
|
||||||
|
|
||||||
|
- **数据版本**:DVC 或 Git-LFS 管理数据集与模型产物;
|
||||||
|
- **实验追踪**:MLflow / Weights & Biases 记录全部训练配置与指标;
|
||||||
|
- **配置管理**:Hydra 管理实验配置,保证可复现;
|
||||||
|
- **测试**:单元测试(数据管道、归一化、守恒校正)+ 回归测试(固定工况误差阈值);
|
||||||
|
- **CI/CD**:模型训练与评估流水线化,指标不达标自动阻断发布。
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
# 03 · 路线图与里程碑
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**总周期:12 个月(7 个里程碑)**,采用"先窄后宽"策略:先在 2D 圆柱绕流这个经典基准上打通全链路,再横向扩展到 3D 与复杂几何。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## M0 · 立项与调研(第 1 个月)
|
||||||
|
|
||||||
|
**目标**:统一技术共识,完成可行性论证,锁定技术栈。
|
||||||
|
|
||||||
|
**任务**
|
||||||
|
- 调研神经算子家族(FNO / DeepONet / PINN / MeshGraphNet)在流体问题上的公开结果;
|
||||||
|
- 调研 PyFluent 自动化能力与许可要求;
|
||||||
|
- 确定首个基准问题:2D 圆柱绕流(Re ∈ [50, 300],含卡门涡街);
|
||||||
|
- 确定数据格式、存储与版本管理方案。
|
||||||
|
|
||||||
|
**交付物**
|
||||||
|
- [x] 本仓库规划文档(01-06 号文档)
|
||||||
|
- [ ] 技术选型评审纪要
|
||||||
|
- [ ] 基准问题定义书(几何、边界条件、网格、工况表)
|
||||||
|
|
||||||
|
**验收**:评审通过,环境就绪(Fluent + PyFluent + PyTorch + GPU)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## M1 · 数据工厂(第 2-3 个月)
|
||||||
|
|
||||||
|
**目标**:建成无人值守的 Fluent 批量仿真流水线。
|
||||||
|
|
||||||
|
**任务**
|
||||||
|
- PyFluent 脚本化:参数注入 → 网格生成 → 求解 → 场量导出;
|
||||||
|
- 参数空间设计:拉丁超立方采样 Re / 攻角 / 障碍物尺寸;
|
||||||
|
- 稳态 + 瞬态两类数据集:稳态用于场回归,瞬态用于时间推进;
|
||||||
|
- 数据质检:收敛性检查、网格无关性验证、异常工况剔除。
|
||||||
|
|
||||||
|
**交付物**
|
||||||
|
- 批量仿真调度器(支持断点续跑、失败重试、日志追踪)
|
||||||
|
- 2D 圆柱绕流数据集 v0.1:≥ 500 工况(稳态)+ 20 组瞬态序列
|
||||||
|
- 数据质检报告
|
||||||
|
|
||||||
|
**验收**:单工况端到端自动化成功率 > 95%;数据集通过网格无关性检查。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## M2 · 基线模型(第 4-6 个月)
|
||||||
|
|
||||||
|
**目标**:FNO 基线在 2D 基准上达到可用精度。
|
||||||
|
|
||||||
|
**任务**
|
||||||
|
- 数据工程:网格统一化、无量纲化、场归一化、训练/验证/测试划分;
|
||||||
|
- FNO 实现与调参(模态数、层数、宽度、教师强制 vs 自回归);
|
||||||
|
- 物理约束项:连续性方程残差、动量方程残差加入损失;
|
||||||
|
- 评估体系:相对 L2、涡量谱、长时推演稳定性、泛化测试。
|
||||||
|
|
||||||
|
**交付物**
|
||||||
|
- 训练与评估代码(Hydra 配置 + MLflow 追踪)
|
||||||
|
- FNO 基线模型(2D 稳态 + 瞬态两个版本)
|
||||||
|
- 评估报告:精度 / 稳定性 / 泛化
|
||||||
|
|
||||||
|
**验收(Go/No-Go)**
|
||||||
|
- 稳态:相对 L2 误差 < 5%
|
||||||
|
- 瞬态:自回归 1000 步不发散
|
||||||
|
- 单步推理 < 5 ms(256² 网格,FP16)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## M3 · 主动学习与数据飞轮(第 6-7 个月)
|
||||||
|
|
||||||
|
**目标**:用最少的 Fluent 算力换取最大的精度提升。
|
||||||
|
|
||||||
|
**任务**
|
||||||
|
- 深度集成(Deep Ensemble)不确定性估计;
|
||||||
|
- 不确定性驱动的采样策略(OOD 工况优先回灌仿真);
|
||||||
|
- 数据飞轮:训练 → 评估 → 采样 → 仿真 → 再训练 自动化。
|
||||||
|
|
||||||
|
**交付物**
|
||||||
|
- 主动学习调度器
|
||||||
|
- 数据飞轮 v1(一轮迭代周期 < 7 天)
|
||||||
|
- 数据效率对比报告(相同精度下数据量减少比例)
|
||||||
|
|
||||||
|
**验收**:达到同等精度所需仿真工况数减少 ≥ 40%。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## M4 · 3D 与复杂几何(第 7-9 个月)
|
||||||
|
|
||||||
|
**目标**:把能力扩展到工业级 3D 问题。
|
||||||
|
|
||||||
|
**任务**
|
||||||
|
- 3D 数据集:翼型族(NACA 系列)+ 简化车体(Ahmed body);
|
||||||
|
- MeshGraphNet 路线:非结构网格上的图神经网络建模;
|
||||||
|
- 混合策略:规则网格用 FNO,复杂几何用 GNN,统一评估口径;
|
||||||
|
- 分布式训练:多卡 FSDP,梯度检查点。
|
||||||
|
|
||||||
|
**交付物**
|
||||||
|
- 3D 代理模型(翼型 + 简化车体)
|
||||||
|
- 训练基础设施(多卡、可复现)
|
||||||
|
- 3D 评估报告(含气动力系数对比)
|
||||||
|
|
||||||
|
**验收**
|
||||||
|
- 速度场相对 L2 < 8%,升阻力系数误差 < 5%
|
||||||
|
- 3D 单步推理 < 50 ms(中等网格,TensorRT FP16)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## M5 · 实时推理系统(第 9-10 个月)
|
||||||
|
|
||||||
|
**目标**:把模型变成可服务化的毫秒级引擎。
|
||||||
|
|
||||||
|
**任务**
|
||||||
|
- ONNX 导出 → TensorRT 优化(FP16/INT8)→ CUDA Graphs;
|
||||||
|
- 散度修正与守恒校正后处理(投影法/质量修正);
|
||||||
|
- 推理服务:gRPC + WebSocket,多并发流式推送;
|
||||||
|
- 性能工程:批处理、内存池、零拷贝传输。
|
||||||
|
|
||||||
|
**交付物**
|
||||||
|
- 推理引擎与容器镜像
|
||||||
|
- 性能基准报告(延迟分布 P50/P99、吞吐、显存占用)
|
||||||
|
- 服务 API 文档
|
||||||
|
|
||||||
|
**验收**:3D 场景端到端延迟(请求 → 首帧流场)< 100 ms。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## M6 · 可视化与数字孪生原型(第 10-12 个月)
|
||||||
|
|
||||||
|
**目标**:完成可交互演示系统与数字孪生闭环。
|
||||||
|
|
||||||
|
**任务**
|
||||||
|
- WebGPU 前端:体渲染 / 流线 / 矢量场 / 剖切面;
|
||||||
|
- 参数实时拖拽 → 流场更新(含插值过渡与防抖);
|
||||||
|
- 数字孪生模式:稀疏传感器数据同化(集合卡尔曼 / 在线微调);
|
||||||
|
- 对标验证:≥ 3 个工业场景与 Fluent 真值对比。
|
||||||
|
|
||||||
|
**交付物**
|
||||||
|
- 实时可视化 Demo(浏览器可访问)
|
||||||
|
- 数字孪生原型(观测 → 重建 → 预测)
|
||||||
|
- 最终验证报告 + 演示视频
|
||||||
|
|
||||||
|
**验收**
|
||||||
|
- 交互延迟 < 100 ms
|
||||||
|
- 数字孪生重建误差 < 10%(稀疏观测场景)
|
||||||
|
- 对标场景全部通过误差门禁
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 工作流并行视图
|
||||||
|
|
||||||
|
| 工作流 | M0 | M1 | M2 | M3 | M4 | M5 | M6 |
|
||||||
|
| --- | :-: | :-: | :-: | :-: | :-: | :-: | :-: |
|
||||||
|
| 数据 | ● | ● | ● | ● | ● | | |
|
||||||
|
| 模型 | ● | | ● | ● | ● | ● | ● |
|
||||||
|
| 系统 | | ● | ● | ● | ● | ● | ● |
|
||||||
|
| 验证 | ● | ● | ● | ● | ● | ● | ● |
|
||||||
|
|
||||||
|
## 风险缓冲
|
||||||
|
|
||||||
|
- 每个里程碑预留 20% 时间缓冲;
|
||||||
|
- M2 为关键技术门禁:若 FNO 精度不达标,转向 DeepONet/MeshGraphNet 双路线并行的决策点;
|
||||||
|
- M4 若 3D 精度不达标,退守"2D + 切片 3D"策略保证 M5/M6 交付。
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
# 04 · 数据方案与 AI 模型
|
||||||
|
|
||||||
|
## 1. 数据工厂(Fluent → 数据集)
|
||||||
|
|
||||||
|
### 1.1 自动化链路
|
||||||
|
|
||||||
|
```
|
||||||
|
参数配置(YAML) → PyFluent 会话 → 几何/网格生成 → 求解设置 → 求解 → 场量导出(VTK/HDF5)
|
||||||
|
```
|
||||||
|
|
||||||
|
**关键工程点**
|
||||||
|
|
||||||
|
| 问题 | 方案 |
|
||||||
|
| --- | --- |
|
||||||
|
| 长时间批量运行 | 调度器 + 断点续跑 + 失败重试 + 日志追踪 |
|
||||||
|
| 网格不一致 | 参数化网格模板,统一拓扑或统一目标分辨率 |
|
||||||
|
| 收敛判据 | 残差 + 监测量(Cd/Cl/压降)双判据 |
|
||||||
|
| 异常剔除 | 收敛性、网格质量、物理合理性三重检查 |
|
||||||
|
| 存储 | HDF5 分块压缩(训练)+ VTK(可视化)双写 |
|
||||||
|
|
||||||
|
### 1.2 数据集设计
|
||||||
|
|
||||||
|
| 数据集 | 内容 | 规模(首期) | 用途 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| D1 稳态 2D | 圆柱绕流,Re ∈ [50, 300] | ≥ 500 工况 | 场回归基线 |
|
||||||
|
| D2 瞬态 2D | 同上,20 组时间序列 | 20 × 500 帧 | 时间推进 |
|
||||||
|
| D3 稳态 3D | NACA 翼型族,Ma/攻角扫描 | ≥ 300 工况 | 3D 泛化 |
|
||||||
|
| D4 稳态 3D | Ahmed body 简化车体 | ≥ 100 工况 | 工业场景 |
|
||||||
|
| D5 FFU 洁净室 | 送风/回风/热源/尘源参数 | ≥ 200 工况 | 数字孪生 Demo |
|
||||||
|
|
||||||
|
**样本结构(HDF5)**
|
||||||
|
|
||||||
|
```
|
||||||
|
/sample_0001
|
||||||
|
├── inputs/ # Re, 攻角, 几何参数, 边界条件 (标量 + 小数组)
|
||||||
|
├── coords/ # 网格坐标 (N, 2/3)
|
||||||
|
├── fields/ # U(N,3), p(N), k(N), omega(N) ...
|
||||||
|
└── meta/ # 工况 ID, 求解器设置, 收敛残差
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.3 数据工程规范
|
||||||
|
|
||||||
|
- **无量纲化**:以特征长度/来流速度归一化,降低分布漂移;
|
||||||
|
- **场归一化**:逐通道 z-score 或 min-max,统计量随模型一起版本化;
|
||||||
|
- **增广**:旋转、镜像、缩放(仅限物理对称场景);
|
||||||
|
- **划分**:按参数区间外推划分(OOD 测试集),而非随机划分。
|
||||||
|
|
||||||
|
## 2. 模型选型
|
||||||
|
|
||||||
|
### 2.1 主力:FNO(傅里叶神经算子)
|
||||||
|
|
||||||
|
- **原理**:在频域做谱卷积,学习函数空间之间的映射,分辨率无关;
|
||||||
|
- **输入**:参数 + 坐标网格(或隐式网格);
|
||||||
|
- **输出**:场量(U, p, ...);
|
||||||
|
- **优势**:训练稳定、推理快、对规则网格精度高;
|
||||||
|
- **局限**:非结构网格需插值,复杂几何边界处理弱。
|
||||||
|
|
||||||
|
### 2.2 备选与补充
|
||||||
|
|
||||||
|
| 模型 | 何时用 | 关键点 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| DeepONet | FNO 精度不足时并行验证 | 分支/主干网络,适合低维参数 |
|
||||||
|
| PINN | 数据稀缺、反问题 | NS 残差直接进损失,训练慢 |
|
||||||
|
| MeshGraphNet | 复杂几何、非结构网格 | 图消息传递,几何泛化好 |
|
||||||
|
| Latent Dynamics | 长时推演加速 | 自编码器 + 潜空间时间推进 |
|
||||||
|
| Transformer 算子 | 大尺度长程依赖 | 数据需求大,探索性 |
|
||||||
|
|
||||||
|
### 2.3 物理约束(贯穿所有模型)
|
||||||
|
|
||||||
|
| 约束 | 形式 | 作用 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 连续性 | ‖∇·u‖² 惩罚 | 抑制散度漂移 |
|
||||||
|
| 动量残差 | ‖∂u/∂t + (u·∇)u + ∇p/ρ - ν∇²u‖² | 物理一致性 |
|
||||||
|
| 边界条件 | 壁面/入口/出口损失项 | 边界精度 |
|
||||||
|
| 守恒校正 | 投影法后处理 | 硬约束兜底 |
|
||||||
|
| 频谱监督 | 涡量谱 / 能谱匹配 | 湍流结构保真 |
|
||||||
|
|
||||||
|
## 3. 训练策略
|
||||||
|
|
||||||
|
- **两阶段**:先纯数据预训练 → 再物理约束微调;
|
||||||
|
- **课程学习**:先稳态后瞬态,先低 Re 后高 Re;
|
||||||
|
- **长时推演**:教师强制 → 逐步放开自回归,配合 scheduled sampling;
|
||||||
|
- **分布式**:AMP + DDP,大模型用 FSDP + 梯度检查点;
|
||||||
|
- **实验管理**:Hydra 配置 + MLflow 指标追踪,全配置可复现。
|
||||||
|
|
||||||
|
## 4. 评估体系
|
||||||
|
|
||||||
|
| 维度 | 指标 | 目标 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 场精度 | 相对 L2 误差(U, p) | < 5%(2D)/ < 8%(3D) |
|
||||||
|
| 结构精度 | 涡量谱、湍动能分布 | 谱斜率误差 < 10% |
|
||||||
|
| 积分量 | Cd / Cl / 压降 | 误差 < 5% |
|
||||||
|
| 稳定性 | 1000 步 rollout 发散率 | 0 发散 |
|
||||||
|
| 泛化 | OOD 参数区间误差退化 | < 2 倍 |
|
||||||
|
| 效率 | 单步延迟 / 加速比 | < 10 ms / > 1000x |
|
||||||
|
| 可信度 | 不确定性校准误差 | ECE < 0.1 |
|
||||||
|
|
||||||
|
## 5. 快速代理基线(已落地)
|
||||||
|
|
||||||
|
仓库内 `tools/ffu_surrogate.py` 提供了一条**不依赖训练**的快速代理基线:
|
||||||
|
|
||||||
|
- **方法**:1/3 分辨率网格上运行同一套 D2Q9 LBM,准稳态后双线性重建到全分辨率;
|
||||||
|
- **性能**:端到端 < 8 s(精细真值 76 s,加速 16-27x);
|
||||||
|
- **精度**:与精细解全场相关系数 速度 r=0.95 / 粒子 r=0.83 / 温度 r=0.68;
|
||||||
|
- **意义**:在 AI 模型成熟前先打通"快速出图 + 趋势验证"链路,同时作为 AI 模型的对照基线。
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
# 05 · 风险、挑战与缓解策略
|
||||||
|
|
||||||
|
## 1. 风险总览
|
||||||
|
|
||||||
|
| 编号 | 风险 | 概率 | 影响 | 等级 |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| R1 | 湍流建模精度不足,AI 代理无法复现小尺度结构 | 高 | 高 | 🔴 |
|
||||||
|
| R2 | 高保真数据生产成本过高,数据量不足 | 高 | 高 | 🔴 |
|
||||||
|
| R3 | 长时自回归推演误差累积、发散 | 高 | 高 | 🔴 |
|
||||||
|
| R4 | 分布外(新几何/新 Re)泛化崩溃 | 中 | 高 | 🟠 |
|
||||||
|
| R5 | 3D 大网格实时推理算力不足 | 中 | 中 | 🟠 |
|
||||||
|
| R6 | 守恒律漂移导致结果不可信 | 中 | 高 | 🟠 |
|
||||||
|
| R7 | 工业验证周期长、缺乏可信基准 | 中 | 中 | 🟡 |
|
||||||
|
| R8 | 工具链锁定(Fluent 许可、GPU 型号) | 低 | 中 | 🟡 |
|
||||||
|
|
||||||
|
## 2. 逐项分析与缓解
|
||||||
|
|
||||||
|
### R1 湍流精度
|
||||||
|
|
||||||
|
**问题**:Navier-Stokes 的湍流闭合本质困难,DNS 数据在工程尺度不可得;AI 只能学到训练数据中已有的物理。
|
||||||
|
|
||||||
|
**缓解**
|
||||||
|
- 明确精度边界:以 RANS/URANS 数据为主,定位"工程精度"而非"DNS 精度";
|
||||||
|
- 混合建模:AI 预测 + 传统湍流模型残差修正(learned closure);
|
||||||
|
- 频谱损失监督大尺度结构,放弃不可分辨的小尺度;
|
||||||
|
- 分阶段验证:先层流基准(圆柱绕流)再湍流工况。
|
||||||
|
|
||||||
|
### R2 数据成本
|
||||||
|
|
||||||
|
**问题**:单工况 Fluent 仿真小时级,10⁴ 工况在算力/许可/时间上都昂贵。
|
||||||
|
|
||||||
|
**缓解**
|
||||||
|
- 主动学习:只对高价值(高不确定性/OOD)工况做高保真仿真;
|
||||||
|
- 降阶采样:拉丁超立方 + 参数敏感性分析,避免均匀暴力扫描;
|
||||||
|
- 多保真度:粗网格大量生成 + 细网格少量校核(coarse-to-fine);
|
||||||
|
- 复用收敛解:邻近工况热启动,缩短求解时间。
|
||||||
|
|
||||||
|
### R3 长时稳定性
|
||||||
|
|
||||||
|
**问题**:自回归每步误差被放大,几百步后发散。
|
||||||
|
|
||||||
|
**缓解**
|
||||||
|
- 潜空间推进:在低维潜空间做时间积分,抑制高频误差;
|
||||||
|
- 物理投影:每 N 步做散度修正 / 守恒校正;
|
||||||
|
- 噪声注入训练:模拟推演误差,提高鲁棒性;
|
||||||
|
- 早停与不确定性告警:置信度低于阈值时切换传统求解器。
|
||||||
|
|
||||||
|
### R4 泛化
|
||||||
|
|
||||||
|
**问题**:训练分布外的几何/参数表现急剧下降。
|
||||||
|
|
||||||
|
**缓解**
|
||||||
|
- 几何参数化:用统一参数空间描述几何族,而非记忆具体形状;
|
||||||
|
- 数据增广 + 课程学习:从简单到复杂逐步扩展参数范围;
|
||||||
|
- OOD 检测:不确定性 + 输入分布检验,超界自动拒绝并回退;
|
||||||
|
- 在线微调:新场景少量数据快速适配(few-shot fine-tune)。
|
||||||
|
|
||||||
|
### R5 3D 实时算力
|
||||||
|
|
||||||
|
**问题**:3D 网格点数比 2D 高 2-3 个数量级,单步推理可能超时。
|
||||||
|
|
||||||
|
**缓解**
|
||||||
|
- 潜空间降维 + 多尺度分解(粗网格动力学 + 局部细化);
|
||||||
|
- TensorRT FP16/INT8 + CUDA Graphs;
|
||||||
|
- 时间异步:用户交互时低频更新,静止时高精度刷新;
|
||||||
|
- 分辨率自适应:按视角/关注区域动态调整计算分辨率。
|
||||||
|
|
||||||
|
### R6 守恒律漂移
|
||||||
|
|
||||||
|
**问题**:纯数据驱动的网络不严格守恒质量/动量。
|
||||||
|
|
||||||
|
**缓解**
|
||||||
|
- 硬约束:输出层投影到无散空间(Helmholtz 分解);
|
||||||
|
- 软约束:守恒损失项 + 后处理校正;
|
||||||
|
- 监控:把守恒误差作为在线指标,超限告警。
|
||||||
|
|
||||||
|
### R7 验证可信度
|
||||||
|
|
||||||
|
**问题**:没有公认基准,工业界不信任 AI 结果。
|
||||||
|
|
||||||
|
**缓解**
|
||||||
|
- 建立内部基准套件(圆柱绕流 / 翼型 / 车体 / 洁净室);
|
||||||
|
- 与 Fluent 真值做逐场对比 + 积分量对比 + 不确定性报告;
|
||||||
|
- 所有指标可一键复现,公开评估脚本。
|
||||||
|
|
||||||
|
### R8 工具链
|
||||||
|
|
||||||
|
**缓解**:核心代码与 Fluent 解耦(数据接口标准化);推理引擎支持 ONNX 多后端;GPU 无关的降级路径(CPU 推理,精度不变、速度降级)。
|
||||||
|
|
||||||
|
## 3. 开放问题(需要在 P1-P2 回答)
|
||||||
|
|
||||||
|
1. FNO 在 Re > 500 的圆柱绕流上能否保持 < 5% 误差?需要多少工况?
|
||||||
|
2. 物理约束损失的最佳权重与调度策略是什么?
|
||||||
|
3. 潜空间推进相比直接自回归,长时稳定性能提升多少?
|
||||||
|
4. 3D 非结构网格上,MeshGraphNet 与"插值到规则网格 + FNO"哪条路线更优?
|
||||||
|
5. 不确定性估计能否可靠地识别 OOD 工况?
|
||||||
|
6. 单卡推理延迟与网格规模的定量 scaling law?
|
||||||
|
|
||||||
|
## 4. 止损与回退
|
||||||
|
|
||||||
|
| 触发条件 | 回退方案 |
|
||||||
|
| --- | --- |
|
||||||
|
| M2 精度不达标 | 转 DeepONet / MeshGraphNet 双路线并行 |
|
||||||
|
| M4 3D 精度不达标 | 退守"2D + 切片 3D"保证 M5/M6 交付 |
|
||||||
|
| 实时性不达标 | 降分辨率 / 潜空间 / 云端推理 |
|
||||||
|
| 数据成本超预算 | 多保真度 + 主动学习压缩数据量 |
|
||||||
|
| 可信度存疑 | 混合模式:AI 预览 + Fluent 校核 |
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# 06 · 参考文献与工具索引
|
||||||
|
|
||||||
|
## 1. 神经算子与物理机器学习
|
||||||
|
|
||||||
|
| 文献 | 内容 | 链接 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Li et al., *Fourier Neural Operator for Parametric PDEs* (ICLR 2021) | FNO 原始论文 | arXiv:2010.08895 |
|
||||||
|
| Lu et al., *DeepONet: Learning nonlinear operators* (2021) | DeepONet 算子网络 | arXiv:1910.03193 |
|
||||||
|
| Raissi et al., *Physics-Informed Neural Networks* (JCP 2019) | PINN 物理约束 | arXiv:1711.10561 |
|
||||||
|
| Pfaff et al., *Learning Mesh-Based Simulation with Graph Networks* (ICLR 2021) | MeshGraphNet | arXiv:2010.03409 |
|
||||||
|
| Hao et al., *GNOT: A General Neural Operator Transformer* (ICML 2023) | Transformer 算子 | arXiv:2302.14376 |
|
||||||
|
| Kovachki et al., *Neural Operator: Learning Maps Between Function Spaces* (2021) | 神经算子综述 | arXiv:2108.08481 |
|
||||||
|
| Kochkov et al., *Machine learning accelerated computational fluid dynamics* (PNAS 2021) | ML 加速 CFD | arXiv:2102.01010 |
|
||||||
|
| Li et al., *Learned Turbulence Closure for LES* | 学习型湍流闭合 | — |
|
||||||
|
|
||||||
|
## 2. 数据集与基准
|
||||||
|
|
||||||
|
| 名称 | 内容 | 链接 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| PDEBench | 多 PDE 基准数据集 | https://github.com/pdebench/PDEBench |
|
||||||
|
| PDEArena | 神经算子基准 | https://github.com/microsoft/PDEArena |
|
||||||
|
| The Well | 大规模物理场数据集 | https://github.com/PolymathicAI/the_well |
|
||||||
|
| FlowBench | 流场预测基准 | — |
|
||||||
|
| AirfRANS | 翼型 RANS 数据集 | https://github.com/Extrality/AirfRANS |
|
||||||
|
| DeepMind Cylinder Flow | 圆柱绕流高分辨率数据 | — |
|
||||||
|
|
||||||
|
## 3. 开源工具
|
||||||
|
|
||||||
|
| 工具 | 用途 | 链接 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| PyFluent | Python 驱动 Ansys Fluent | https://github.com/ansys/pyfluent |
|
||||||
|
| NeuralOperator | FNO/DeepONet 官方实现 | https://github.com/neuraloperator/neuraloperator |
|
||||||
|
| NVIDIA Modulus | 物理 AI 训练框架 | https://github.com/NVIDIA/modulus |
|
||||||
|
| PhiFlow | 可微流体求解器 | https://github.com/tum-pbs/PhiFlow |
|
||||||
|
| JAX-CFD | JAX 可微 CFD | https://github.com/google/jax-cfd |
|
||||||
|
| OpenFOAM | 开源 CFD(替代/对照) | https://openfoam.org |
|
||||||
|
| VTK.js | 浏览器端科学可视化 | https://kitware.github.io/vtk-js |
|
||||||
|
| three.js | WebGL/WebGPU 渲染 | https://threejs.org |
|
||||||
|
| ONNX Runtime | 跨平台推理 | https://onnxruntime.ai |
|
||||||
|
| TensorRT | NVIDIA 推理加速 | https://developer.nvidia.com/tensorrt |
|
||||||
|
| DVC | 数据/模型版本管理 | https://dvc.org |
|
||||||
|
| MLflow | 实验追踪 | https://mlflow.org |
|
||||||
|
| Hydra | 配置管理 | https://hydra.cc |
|
||||||
|
|
||||||
|
## 4. 洁净室 / FFU 相关
|
||||||
|
|
||||||
|
| 资料 | 内容 |
|
||||||
|
| --- | --- |
|
||||||
|
| ISO 14644-1 | 洁净室分级(ISO Class 1-9) |
|
||||||
|
| ISO 14644-4 | 洁净室设计与施工(单向流风速 0.45 m/s ±20%) |
|
||||||
|
| IEST-RP-CC002 | FFU 性能测试推荐规程 |
|
||||||
|
| ASHRAE Handbook | 洁净空间气流组织设计 |
|
||||||
|
|
||||||
|
## 5. 经典 CFD 参考
|
||||||
|
|
||||||
|
- Versteeg & Malalasekera, *An Introduction to Computational Fluid Dynamics*
|
||||||
|
- Pope, *Turbulent Flows*
|
||||||
|
- Krüger et al., *The Lattice Boltzmann Method: Principles and Practice*(本项目 LBM Demo 的理论依据)
|
||||||
|
- Succi, *The Lattice Boltzmann Equation for Fluid Dynamics and Beyond*
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
# 07 · FFU 洁净室流场实时仿真 Demo
|
||||||
|
|
||||||
|
> FFU(Fan Filter Unit,风机过滤单元)下方洁净区的流场、温湿度、压力与尘埃粒子输运仿真。
|
||||||
|
> 这是"物理世界流场实时仿真"的首个可运行 Demo,同时提供 **高保真真值** 与 **毫秒级快速代理** 两条路径。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 物理场景
|
||||||
|
|
||||||
|
| 项目 | 设置 |
|
||||||
|
| --- | --- |
|
||||||
|
| 计算域 | 4.0 m(宽)× 3.0 m(高)2D 截面 |
|
||||||
|
| FFU 送风 | 顶棚满布,面风速 **0.45 m/s**(ISO 14644-4 单向流推荐值) |
|
||||||
|
| 回风 | 底部两侧回风口(0~0.5 m / 3.5~4.0 m),高架地板/下侧回风 |
|
||||||
|
| 工作台 | x ∈ [0.60, 1.80],高 0.90 m |
|
||||||
|
| 设备 | x ∈ [0.85, 1.50],y ∈ [0.90, 1.25] —— **热源 + 尘源** |
|
||||||
|
| 操作人员 | 躯干 x ∈ [2.35, 2.75] 高 1.45 m + 头部(r = 0.15 m)—— **湿源 + 尘源** |
|
||||||
|
| 物料架 | x ∈ [3.00, 3.50],高 1.15 m |
|
||||||
|
| 温度 | 送风 22 °C,设备表面 35 °C |
|
||||||
|
| 湿度 | 送风 RH 45%,呼吸区 RH 70% |
|
||||||
|
| 粒子 | 设备 + 人员双尘源,含重力沉降(被动标量 + 沉降速度) |
|
||||||
|
| 雷诺数 | Re = U·H/ν ≈ 540(层流单向流) |
|
||||||
|
|
||||||
|
## 2. 两条技术路径
|
||||||
|
|
||||||
|
| | 高保真真值 | 快速代理 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 脚本 | `tools/ffu_simulation.py` | `tools/ffu_surrogate.py` |
|
||||||
|
| 网格 | 360 × 270(90 px/m) | 120 × 90(1/3 分辨率) |
|
||||||
|
| 步数 | 14 000 | 5 000 |
|
||||||
|
| 求解 | D2Q9 LBM + 半拉格朗日标量输运 | **同一套求解器**(粗网格)+ 双线性重建 |
|
||||||
|
| 端到端耗时 | **76 s** | **7.3 s(16-27x 加速)** |
|
||||||
|
| 定位 | 验证真值 / 精度基准 | 交互预览 / 趋势级实时 |
|
||||||
|
|
||||||
|
**为什么"粗网格"是当前最好的快速代理?**
|
||||||
|
在 AI 神经算子尚未训练完成的阶段,粗网格求解用同一套物理保证趋势一致(速度场相关系数 **r = 0.95**),
|
||||||
|
是零训练成本、零泛化风险的实时路径;同时它天然作为后续 FNO/DeepONet 的对照基线。
|
||||||
|
|
||||||
|
## 3. 趋势验证结果
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
| 场量 | 全场相关系数(vs 高保真) | 结论 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 速度 | **r = 0.95** | 绕障加速、贴壁射流、回流区结构一致 |
|
||||||
|
| 粒子浓度 | **r = 0.83** | 羽流路径与死角堆积一致 |
|
||||||
|
| 温度 | **r = 0.68** | 热羽下扫与侧向输运一致(粗网格扩散略强) |
|
||||||
|
|
||||||
|
> 代理模型保证**趋势级**精度,局部场量精度不做保证;需要精确值时切换高保真求解。
|
||||||
|
|
||||||
|
## 4. 结果图
|
||||||
|
|
||||||
|
### 4.1 流场(高保真)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 4.2 温湿度 / 压力 / 粒子(高保真)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 4.3 快速代理(7 秒出图)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 4.4 粒子轨迹与滞留时间
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 5. 主要物理结论(趋势)
|
||||||
|
|
||||||
|
1. **单向下扫**:FFU 送风把设备/人员散发的热量、湿气与粒子持续向下输运,经底部回风口排出——这是洁净室自净能力的来源;
|
||||||
|
2. **绕障加速**:气流在工作台、人员、物料架之间被挤压加速,贴壁/缝隙处流速可达 ~1.5 m/s(约为送风速度的 3 倍);
|
||||||
|
3. **死角堆积**:障碍物背风侧与墙角存在低速回流区,污染物滞留时间显著变长(粒子轨迹图中颜色越暖滞留越久)——**这是洁净室布局优化的关键目标**;
|
||||||
|
4. **压力分布**:送风端相对正压、回风口相对负压,全场压差 < 2 Pa(洁净室典型量级);
|
||||||
|
5. **人员源不可忽视**:呼吸区湿羽流与尘源在单向流下形成明显的下扫羽流,直接影响工作区洁净度。
|
||||||
|
|
||||||
|
## 6. 模型局限(诚实声明)
|
||||||
|
|
||||||
|
- **2D 截面**:无法刻画三维绕流与横向扩散;
|
||||||
|
- **等温假设**:未耦合浮力(热羽流完全由强制对流主导),真实热源上方可能存在浮力竞争;
|
||||||
|
- **层流/低 Re**:未启用湍流模型,高换气次数下的湍流脉动未建模;
|
||||||
|
- **粒子为被动标量 + 沉降**:未包含凝聚、静电、表面沉积再悬浮;
|
||||||
|
- **非洁净度认证**:粒子浓度为归一化相对值,不可直接对应 ISO 14644-1 等级。
|
||||||
|
|
||||||
|
## 7. 复现命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 高保真真值(76 s,输出 3 张图 + npz 场数据)
|
||||||
|
python tools/ffu_simulation.py
|
||||||
|
|
||||||
|
# 快速代理(7 s,输出 2 张图 + 趋势验证对比图)
|
||||||
|
python tools/ffu_surrogate.py
|
||||||
|
|
||||||
|
# 规划图(架构 / 流水线 / 路线图 SVG)
|
||||||
|
python tools/gen_diagrams.py
|
||||||
|
|
||||||
|
# SVG -> PNG
|
||||||
|
npm install && node tools/svg2png.js
|
||||||
|
```
|
||||||
|
|
||||||
|
## 8. 下一步
|
||||||
|
|
||||||
|
- [ ] 加入浮力项(Boussinesq)验证热羽流竞争;
|
||||||
|
- [ ] 参数化扫描:送风速度 × 障碍物布局 × 源强度 → 数据集 D5;
|
||||||
|
- [ ] 训练 FNO 代理:以 D5 为数据,对标粗网格代理的速度与精度;
|
||||||
|
- [ ] 三维化:FFU 阵列 + 局部障碍物 3D 流场;
|
||||||
|
- [ ] 接入 WebGPU 前端,实现参数拖拽实时刷新。
|
||||||
|
After Width: | Height: | Size: 341 KiB |
@@ -0,0 +1,142 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1600" height="1150" viewBox="0 0 1600 1150">
|
||||||
|
<defs>
|
||||||
|
<marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="#58a6ff"/></marker>
|
||||||
|
<marker id="arrG" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="#3fb950"/></marker>
|
||||||
|
<marker id="arrY" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="#d29922"/></marker>
|
||||||
|
<marker id="arrM" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="#8b949e"/></marker>
|
||||||
|
</defs>
|
||||||
|
<rect width="1600" height="1150" fill="#0d1117"/>
|
||||||
|
<text x="50" y="56" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="28" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">PhysicsAiFluentAnalysis · 系统架构</text>
|
||||||
|
<text x="50" y="88" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="14.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">六层架构:从高保真真值到实时交互应用,物理约束贯穿模型层</text>
|
||||||
|
<rect x="40" y="118" width="1520" height="148" rx="12" fill="#161b22" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="40" y="118" width="6" height="148" rx="3" fill="#f778ba"/>
|
||||||
|
<text x="64" y="184" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="17.5" fill="#f778ba" font-weight="bold" text-anchor="start" opacity="1.0">L5 应用层</text>
|
||||||
|
<text x="64" y="210" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">Application</text>
|
||||||
|
<rect x="230" y="140" width="317" height="104" rx="10" fill="#0d1117" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="230" y="140" width="317" height="4" rx="2" fill="#f778ba" opacity="0.75"/>
|
||||||
|
<text x="246" y="180" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="15" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">数字孪生</text>
|
||||||
|
<text x="246" y="208" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">传感器同化 · 在线重建</text>
|
||||||
|
<rect x="561" y="140" width="317" height="104" rx="10" fill="#0d1117" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="561" y="140" width="317" height="4" rx="2" fill="#f778ba" opacity="0.75"/>
|
||||||
|
<text x="577" y="180" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="15" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">工程设计优化</text>
|
||||||
|
<text x="577" y="208" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">实时参数扫描 · 设计空间探索</text>
|
||||||
|
<rect x="892" y="140" width="317" height="104" rx="10" fill="#0d1117" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="892" y="140" width="317" height="4" rx="2" fill="#f778ba" opacity="0.75"/>
|
||||||
|
<text x="908" y="180" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="15" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">教学与科研</text>
|
||||||
|
<text x="908" y="208" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">流场可视化实验</text>
|
||||||
|
<rect x="1223" y="140" width="317" height="104" rx="10" fill="#0d1117" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="1223" y="140" width="317" height="4" rx="2" fill="#f778ba" opacity="0.75"/>
|
||||||
|
<text x="1239" y="180" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="15" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">实时决策</text>
|
||||||
|
<text x="1239" y="208" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">风场 / 流场秒级预测</text>
|
||||||
|
<line x1="880" y1="283.0" x2="880" y2="269.0" stroke="#58a6ff" stroke-width="1.8" marker-end="url(#arr)"/>
|
||||||
|
<line x1="760" y1="269.0" x2="760" y2="283.0" stroke="#3fb950" stroke-width="1.8" marker-end="url(#arrG)"/>
|
||||||
|
<rect x="40" y="286" width="1520" height="148" rx="12" fill="#161b22" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="40" y="286" width="6" height="148" rx="3" fill="#bc8cff"/>
|
||||||
|
<text x="64" y="352" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="17.5" fill="#bc8cff" font-weight="bold" text-anchor="start" opacity="1.0">L4 交互与渲染</text>
|
||||||
|
<text x="64" y="378" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">Interaction & Rendering</text>
|
||||||
|
<rect x="230" y="308" width="317" height="104" rx="10" fill="#0d1117" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="230" y="308" width="317" height="4" rx="2" fill="#bc8cff" opacity="0.75"/>
|
||||||
|
<text x="246" y="348" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="15" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">WebGPU 前端</text>
|
||||||
|
<text x="246" y="376" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">three.js / VTK.js</text>
|
||||||
|
<rect x="561" y="308" width="317" height="104" rx="10" fill="#0d1117" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="561" y="308" width="317" height="4" rx="2" fill="#bc8cff" opacity="0.75"/>
|
||||||
|
<text x="577" y="348" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="15" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">参数实时调节</text>
|
||||||
|
<text x="577" y="376" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">拖拽 Re / 攻角 / 几何</text>
|
||||||
|
<rect x="892" y="308" width="317" height="104" rx="10" fill="#0d1117" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="892" y="308" width="317" height="4" rx="2" fill="#bc8cff" opacity="0.75"/>
|
||||||
|
<text x="908" y="348" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="15" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">场可视化</text>
|
||||||
|
<text x="908" y="376" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">体渲染 · 流线 · 矢量场</text>
|
||||||
|
<rect x="1223" y="308" width="317" height="104" rx="10" fill="#0d1117" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="1223" y="308" width="317" height="4" rx="2" fill="#bc8cff" opacity="0.75"/>
|
||||||
|
<text x="1239" y="348" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="15" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">场景管理</text>
|
||||||
|
<text x="1239" y="376" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">剖切面 · 时间轴 · 导出</text>
|
||||||
|
<line x1="880" y1="451.0" x2="880" y2="437.0" stroke="#58a6ff" stroke-width="1.8" marker-end="url(#arr)"/>
|
||||||
|
<line x1="760" y1="437.0" x2="760" y2="451.0" stroke="#3fb950" stroke-width="1.8" marker-end="url(#arrG)"/>
|
||||||
|
<rect x="40" y="454" width="1520" height="148" rx="12" fill="#161b22" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="40" y="454" width="6" height="148" rx="3" fill="#d29922"/>
|
||||||
|
<text x="64" y="520" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="17.5" fill="#d29922" font-weight="bold" text-anchor="start" opacity="1.0">L3 推理服务层</text>
|
||||||
|
<text x="64" y="546" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">Inference Service</text>
|
||||||
|
<rect x="230" y="476" width="317" height="104" rx="10" fill="#0d1117" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="230" y="476" width="317" height="4" rx="2" fill="#d29922" opacity="0.75"/>
|
||||||
|
<text x="246" y="516" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="15" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">推理引擎</text>
|
||||||
|
<text x="246" y="544" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">TensorRT / ONNX Runtime</text>
|
||||||
|
<rect x="561" y="476" width="317" height="104" rx="10" fill="#0d1117" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="561" y="476" width="317" height="4" rx="2" fill="#d29922" opacity="0.75"/>
|
||||||
|
<text x="577" y="516" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="15" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">时间推进</text>
|
||||||
|
<text x="577" y="544" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">自回归 + 潜空间动力学</text>
|
||||||
|
<rect x="892" y="476" width="317" height="104" rx="10" fill="#0d1117" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="892" y="476" width="317" height="4" rx="2" fill="#d29922" opacity="0.75"/>
|
||||||
|
<text x="908" y="516" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="15" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">后处理校正</text>
|
||||||
|
<text x="908" y="544" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">散度修正 · 守恒律</text>
|
||||||
|
<rect x="1223" y="476" width="317" height="104" rx="10" fill="#0d1117" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="1223" y="476" width="317" height="4" rx="2" fill="#d29922" opacity="0.75"/>
|
||||||
|
<text x="1239" y="516" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="15" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">服务接口</text>
|
||||||
|
<text x="1239" y="544" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">gRPC + WebSocket 流式</text>
|
||||||
|
<line x1="880" y1="619.0" x2="880" y2="605.0" stroke="#58a6ff" stroke-width="1.8" marker-end="url(#arr)"/>
|
||||||
|
<line x1="760" y1="605.0" x2="760" y2="619.0" stroke="#3fb950" stroke-width="1.8" marker-end="url(#arrG)"/>
|
||||||
|
<rect x="40" y="622" width="1520" height="148" rx="12" fill="#161b22" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="40" y="622" width="6" height="148" rx="3" fill="#58a6ff"/>
|
||||||
|
<text x="64" y="688" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="17.5" fill="#58a6ff" font-weight="bold" text-anchor="start" opacity="1.0">L2 AI 模型层</text>
|
||||||
|
<text x="64" y="714" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">AI Models</text>
|
||||||
|
<rect x="230" y="644" width="317" height="104" rx="10" fill="#0d1117" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="230" y="644" width="317" height="4" rx="2" fill="#58a6ff" opacity="0.75"/>
|
||||||
|
<text x="246" y="684" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="15" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">FNO 傅里叶神经算子</text>
|
||||||
|
<text x="246" y="712" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">主力基线 · 分辨率无关</text>
|
||||||
|
<rect x="561" y="644" width="317" height="104" rx="10" fill="#0d1117" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="561" y="644" width="317" height="4" rx="2" fill="#58a6ff" opacity="0.75"/>
|
||||||
|
<text x="577" y="684" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="15" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">DeepONet</text>
|
||||||
|
<text x="577" y="712" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">算子学习备选路线</text>
|
||||||
|
<rect x="892" y="644" width="317" height="104" rx="10" fill="#0d1117" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="892" y="644" width="317" height="4" rx="2" fill="#58a6ff" opacity="0.75"/>
|
||||||
|
<text x="908" y="684" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="15" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">PINN 物理约束</text>
|
||||||
|
<text x="908" y="712" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">NS 残差 · 少数据工况</text>
|
||||||
|
<rect x="1223" y="644" width="317" height="104" rx="10" fill="#0d1117" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="1223" y="644" width="317" height="4" rx="2" fill="#58a6ff" opacity="0.75"/>
|
||||||
|
<text x="1239" y="684" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="15" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">MeshGraphNet</text>
|
||||||
|
<text x="1239" y="712" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">非结构网格 · 复杂几何</text>
|
||||||
|
<line x1="880" y1="787.0" x2="880" y2="773.0" stroke="#58a6ff" stroke-width="1.8" marker-end="url(#arr)"/>
|
||||||
|
<line x1="760" y1="773.0" x2="760" y2="787.0" stroke="#3fb950" stroke-width="1.8" marker-end="url(#arrG)"/>
|
||||||
|
<rect x="40" y="790" width="1520" height="148" rx="12" fill="#161b22" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="40" y="790" width="6" height="148" rx="3" fill="#3fb950"/>
|
||||||
|
<text x="64" y="856" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="17.5" fill="#3fb950" font-weight="bold" text-anchor="start" opacity="1.0">L1 数据与训练</text>
|
||||||
|
<text x="64" y="882" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">Data & Training</text>
|
||||||
|
<rect x="230" y="812" width="317" height="104" rx="10" fill="#0d1117" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="230" y="812" width="317" height="4" rx="2" fill="#3fb950" opacity="0.75"/>
|
||||||
|
<text x="246" y="852" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="15" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">PyTorch 分布式训练</text>
|
||||||
|
<text x="246" y="880" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">AMP · DDP / FSDP</text>
|
||||||
|
<rect x="561" y="812" width="317" height="104" rx="10" fill="#0d1117" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="561" y="812" width="317" height="4" rx="2" fill="#3fb950" opacity="0.75"/>
|
||||||
|
<text x="577" y="852" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="15" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">主动学习</text>
|
||||||
|
<text x="577" y="880" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">不确定性驱动采样</text>
|
||||||
|
<rect x="892" y="812" width="317" height="104" rx="10" fill="#0d1117" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="892" y="812" width="317" height="4" rx="2" fill="#3fb950" opacity="0.75"/>
|
||||||
|
<text x="908" y="852" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="15" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">数据湖</text>
|
||||||
|
<text x="908" y="880" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">HDF5 / VTK / Zarr</text>
|
||||||
|
<rect x="1223" y="812" width="317" height="104" rx="10" fill="#0d1117" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="1223" y="812" width="317" height="4" rx="2" fill="#3fb950" opacity="0.75"/>
|
||||||
|
<text x="1239" y="852" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="15" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">数据工程</text>
|
||||||
|
<text x="1239" y="880" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">归一化 · 增广 · 插值统一</text>
|
||||||
|
<line x1="880" y1="955.0" x2="880" y2="941.0" stroke="#58a6ff" stroke-width="1.8" marker-end="url(#arr)"/>
|
||||||
|
<line x1="760" y1="941.0" x2="760" y2="955.0" stroke="#3fb950" stroke-width="1.8" marker-end="url(#arrG)"/>
|
||||||
|
<rect x="40" y="958" width="1520" height="148" rx="12" fill="#161b22" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="40" y="958" width="6" height="148" rx="3" fill="#ff7b72"/>
|
||||||
|
<text x="64" y="1024" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="17.5" fill="#ff7b72" font-weight="bold" text-anchor="start" opacity="1.0">L0 高保真真值</text>
|
||||||
|
<text x="64" y="1050" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">Ground Truth CFD</text>
|
||||||
|
<rect x="230" y="980" width="317" height="104" rx="10" fill="#0d1117" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="230" y="980" width="317" height="4" rx="2" fill="#ff7b72" opacity="0.75"/>
|
||||||
|
<text x="246" y="1020" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="15" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">Ansys Fluent</text>
|
||||||
|
<text x="246" y="1048" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">RANS / URANS / LES</text>
|
||||||
|
<rect x="561" y="980" width="317" height="104" rx="10" fill="#0d1117" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="561" y="980" width="317" height="4" rx="2" fill="#ff7b72" opacity="0.75"/>
|
||||||
|
<text x="577" y="1020" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="15" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">PyFluent 自动化</text>
|
||||||
|
<text x="577" y="1048" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">参数化建模与求解</text>
|
||||||
|
<rect x="892" y="980" width="317" height="104" rx="10" fill="#0d1117" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="892" y="980" width="317" height="4" rx="2" fill="#ff7b72" opacity="0.75"/>
|
||||||
|
<text x="908" y="1020" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="15" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">参数化几何</text>
|
||||||
|
<text x="908" y="1048" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">翼型族 · 车体 · 管道</text>
|
||||||
|
<rect x="1223" y="980" width="317" height="104" rx="10" fill="#0d1117" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<rect x="1223" y="980" width="317" height="4" rx="2" fill="#ff7b72" opacity="0.75"/>
|
||||||
|
<text x="1239" y="1020" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="15" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">边界条件库</text>
|
||||||
|
<text x="1239" y="1048" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">工况模板 · 湍流模型</text>
|
||||||
|
<text x="40" y="1128" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">数据流 ↑(真值 → 训练 → 推理 → 应用) 控制流 ↓(应用需求 → 模型选型 → 数据采集) 物理约束:连续性 / 动量守恒 · 不确定性量化 · 守恒校正</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 480 KiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 572 KiB |
|
After Width: | Height: | Size: 346 KiB |
|
After Width: | Height: | Size: 394 KiB |
|
After Width: | Height: | Size: 498 KiB |
|
After Width: | Height: | Size: 466 KiB |
|
After Width: | Height: | Size: 199 KiB |
@@ -0,0 +1,77 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1600" height="760" viewBox="0 0 1600 760">
|
||||||
|
<defs>
|
||||||
|
<marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="#58a6ff"/></marker>
|
||||||
|
<marker id="arrG" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="#3fb950"/></marker>
|
||||||
|
<marker id="arrY" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="#d29922"/></marker>
|
||||||
|
<marker id="arrM" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="#8b949e"/></marker>
|
||||||
|
</defs>
|
||||||
|
<rect width="1600" height="760" fill="#0d1117"/>
|
||||||
|
<text x="50" y="56" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="28" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">数据与训练流水线</text>
|
||||||
|
<text x="50" y="88" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="14.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">从参数采样到实时仿真服务:全链路自动化 + 主动学习数据飞轮</text>
|
||||||
|
<rect x="68" y="150" width="224" height="150" rx="12" fill="#161b22" stroke="#39d0d8" stroke-width="1.6" opacity="1.0"/>
|
||||||
|
<rect x="68" y="150" width="224" height="5" rx="2.5" fill="#39d0d8"/>
|
||||||
|
<text x="84" y="194" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="16" fill="#39d0d8" font-weight="bold" text-anchor="start" opacity="1.0">① 参数空间采样</text>
|
||||||
|
<text x="84" y="226" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">拉丁超立方采样</text>
|
||||||
|
<text x="84" y="250" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">Re / 攻角 / 几何参数</text>
|
||||||
|
<line x1="294" y1="225.0" x2="313" y2="225.0" stroke="#58a6ff" stroke-width="2.0" marker-end="url(#arr)"/>
|
||||||
|
<rect x="76" y="316" width="208" height="42" rx="8" fill="#0d1117" stroke="#30363d" stroke-width="1.2" stroke-dasharray="4 3" opacity="1.0"/>
|
||||||
|
<text x="180.0" y="343" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="middle" opacity="1.0">工况表 + 元数据</text>
|
||||||
|
<rect x="316" y="150" width="224" height="150" rx="12" fill="#161b22" stroke="#ff7b72" stroke-width="1.6" opacity="1.0"/>
|
||||||
|
<rect x="316" y="150" width="224" height="5" rx="2.5" fill="#ff7b72"/>
|
||||||
|
<text x="332" y="194" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="16" fill="#ff7b72" font-weight="bold" text-anchor="start" opacity="1.0">② Fluent 批量仿真</text>
|
||||||
|
<text x="332" y="226" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">PyFluent 无人值守</text>
|
||||||
|
<text x="332" y="250" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">稳态 + 瞬态工况</text>
|
||||||
|
<line x1="542" y1="225.0" x2="561" y2="225.0" stroke="#58a6ff" stroke-width="2.0" marker-end="url(#arr)"/>
|
||||||
|
<rect x="324" y="316" width="208" height="42" rx="8" fill="#0d1117" stroke="#30363d" stroke-width="1.2" stroke-dasharray="4 3" opacity="1.0"/>
|
||||||
|
<text x="428.0" y="343" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="middle" opacity="1.0">CAS / DAT / VTK</text>
|
||||||
|
<rect x="564" y="150" width="224" height="150" rx="12" fill="#161b22" stroke="#3fb950" stroke-width="1.6" opacity="1.0"/>
|
||||||
|
<rect x="564" y="150" width="224" height="5" rx="2.5" fill="#3fb950"/>
|
||||||
|
<text x="580" y="194" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="16" fill="#3fb950" font-weight="bold" text-anchor="start" opacity="1.0">③ 数据工程</text>
|
||||||
|
<text x="580" y="226" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">网格统一 · 归一化</text>
|
||||||
|
<text x="580" y="250" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">质检 / 增广 / 划分</text>
|
||||||
|
<line x1="790" y1="225.0" x2="809" y2="225.0" stroke="#58a6ff" stroke-width="2.0" marker-end="url(#arr)"/>
|
||||||
|
<rect x="572" y="316" width="208" height="42" rx="8" fill="#0d1117" stroke="#30363d" stroke-width="1.2" stroke-dasharray="4 3" opacity="1.0"/>
|
||||||
|
<text x="676.0" y="343" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="middle" opacity="1.0">HDF5 数据集</text>
|
||||||
|
<rect x="812" y="150" width="224" height="150" rx="12" fill="#161b22" stroke="#58a6ff" stroke-width="1.6" opacity="1.0"/>
|
||||||
|
<rect x="812" y="150" width="224" height="5" rx="2.5" fill="#58a6ff"/>
|
||||||
|
<text x="828" y="194" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="16" fill="#58a6ff" font-weight="bold" text-anchor="start" opacity="1.0">④ 神经算子训练</text>
|
||||||
|
<text x="828" y="226" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">FNO / DeepONet / GNN</text>
|
||||||
|
<text x="828" y="250" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">物理约束损失</text>
|
||||||
|
<line x1="1038" y1="225.0" x2="1057" y2="225.0" stroke="#58a6ff" stroke-width="2.0" marker-end="url(#arr)"/>
|
||||||
|
<rect x="820" y="316" width="208" height="42" rx="8" fill="#0d1117" stroke="#30363d" stroke-width="1.2" stroke-dasharray="4 3" opacity="1.0"/>
|
||||||
|
<text x="924.0" y="343" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="middle" opacity="1.0">模型权重 .pt</text>
|
||||||
|
<rect x="1060" y="150" width="224" height="150" rx="12" fill="#161b22" stroke="#bc8cff" stroke-width="1.6" opacity="1.0"/>
|
||||||
|
<rect x="1060" y="150" width="224" height="5" rx="2.5" fill="#bc8cff"/>
|
||||||
|
<text x="1076" y="194" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="16" fill="#bc8cff" font-weight="bold" text-anchor="start" opacity="1.0">⑤ 推理引擎部署</text>
|
||||||
|
<text x="1076" y="226" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">ONNX → TensorRT</text>
|
||||||
|
<text x="1076" y="250" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">FP16 / INT8 量化</text>
|
||||||
|
<line x1="1286" y1="225.0" x2="1305" y2="225.0" stroke="#58a6ff" stroke-width="2.0" marker-end="url(#arr)"/>
|
||||||
|
<rect x="1068" y="316" width="208" height="42" rx="8" fill="#0d1117" stroke="#30363d" stroke-width="1.2" stroke-dasharray="4 3" opacity="1.0"/>
|
||||||
|
<text x="1172.0" y="343" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="middle" opacity="1.0">TensorRT .engine</text>
|
||||||
|
<rect x="1308" y="150" width="224" height="150" rx="12" fill="#161b22" stroke="#f778ba" stroke-width="1.6" opacity="1.0"/>
|
||||||
|
<rect x="1308" y="150" width="224" height="5" rx="2.5" fill="#f778ba"/>
|
||||||
|
<text x="1324" y="194" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="16" fill="#f778ba" font-weight="bold" text-anchor="start" opacity="1.0">⑥ 实时仿真服务</text>
|
||||||
|
<text x="1324" y="226" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">gRPC + WebSocket</text>
|
||||||
|
<text x="1324" y="250" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">流场流式推送</text>
|
||||||
|
<rect x="1316" y="316" width="208" height="42" rx="8" fill="#0d1117" stroke="#30363d" stroke-width="1.2" stroke-dasharray="4 3" opacity="1.0"/>
|
||||||
|
<text x="1420.0" y="343" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="middle" opacity="1.0">实时流场帧</text>
|
||||||
|
<line x1="1420.0" y1="384" x2="180.0" y2="384" stroke="#d29922" stroke-width="1.8" stroke-dasharray="7 5" marker-end="url(#arrY)"/>
|
||||||
|
<text x="800.0" y="412" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="13" fill="#d29922" font-weight="normal" text-anchor="middle" opacity="1.0">主动学习闭环:不确定性评估 → 高价值工况回灌仿真 → 再训练(数据飞轮)</text>
|
||||||
|
<rect x="68" y="600" width="340" height="66" rx="10" fill="#161b22" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<circle cx="94" cy="633" r="7" fill="#3fb950" opacity="0.85"/>
|
||||||
|
<text x="114" y="630" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="14.5" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">精度门禁</text>
|
||||||
|
<text x="114" y="652" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">相对 L2 < 5%</text>
|
||||||
|
<rect x="428" y="600" width="340" height="66" rx="10" fill="#161b22" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<circle cx="454" cy="633" r="7" fill="#3fb950" opacity="0.85"/>
|
||||||
|
<text x="474" y="630" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="14.5" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">稳定性门禁</text>
|
||||||
|
<text x="474" y="652" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">1000 步不发散</text>
|
||||||
|
<rect x="788" y="600" width="340" height="66" rx="10" fill="#161b22" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<circle cx="814" cy="633" r="7" fill="#3fb950" opacity="0.85"/>
|
||||||
|
<text x="834" y="630" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="14.5" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">成本门禁</text>
|
||||||
|
<text x="834" y="652" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">单工况预算达标</text>
|
||||||
|
<rect x="1148" y="600" width="340" height="66" rx="10" fill="#161b22" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<circle cx="1174" cy="633" r="7" fill="#3fb950" opacity="0.85"/>
|
||||||
|
<text x="1194" y="630" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="14.5" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">可复现门禁</text>
|
||||||
|
<text x="1194" y="652" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="11.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">流水线一键复现</text>
|
||||||
|
<text x="50" y="726" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">每个阶段设置 Go / No-Go 决策点:未通过门禁不允许进入下一阶段</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 255 KiB |
@@ -0,0 +1,84 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1600" height="940" viewBox="0 0 1600 940">
|
||||||
|
<defs>
|
||||||
|
<marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="#58a6ff"/></marker>
|
||||||
|
<marker id="arrG" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="#3fb950"/></marker>
|
||||||
|
<marker id="arrY" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="#d29922"/></marker>
|
||||||
|
<marker id="arrM" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="#8b949e"/></marker>
|
||||||
|
</defs>
|
||||||
|
<rect width="1600" height="940" fill="#0d1117"/>
|
||||||
|
<text x="50" y="56" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="28" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">里程碑路线图(12 个月 · 7 个里程碑)</text>
|
||||||
|
<text x="50" y="88" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="14.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">先窄后宽:2D 基准打通全链路 → 3D 复杂几何 → 实时系统 → 数字孪生</text>
|
||||||
|
<line x1="300.0" y1="116" x2="300.0" y2="840" stroke="#21262d" stroke-width="1"/>
|
||||||
|
<line x1="405.0" y1="116" x2="405.0" y2="840" stroke="#21262d" stroke-width="1"/>
|
||||||
|
<line x1="510.0" y1="116" x2="510.0" y2="840" stroke="#21262d" stroke-width="1"/>
|
||||||
|
<line x1="615.0" y1="116" x2="615.0" y2="840" stroke="#21262d" stroke-width="1"/>
|
||||||
|
<line x1="720.0" y1="116" x2="720.0" y2="840" stroke="#21262d" stroke-width="1"/>
|
||||||
|
<line x1="825.0" y1="116" x2="825.0" y2="840" stroke="#21262d" stroke-width="1"/>
|
||||||
|
<line x1="930.0" y1="116" x2="930.0" y2="840" stroke="#21262d" stroke-width="1"/>
|
||||||
|
<line x1="1035.0" y1="116" x2="1035.0" y2="840" stroke="#21262d" stroke-width="1"/>
|
||||||
|
<line x1="1140.0" y1="116" x2="1140.0" y2="840" stroke="#21262d" stroke-width="1"/>
|
||||||
|
<line x1="1245.0" y1="116" x2="1245.0" y2="840" stroke="#21262d" stroke-width="1"/>
|
||||||
|
<line x1="1350.0" y1="116" x2="1350.0" y2="840" stroke="#21262d" stroke-width="1"/>
|
||||||
|
<line x1="1455.0" y1="116" x2="1455.0" y2="840" stroke="#21262d" stroke-width="1"/>
|
||||||
|
<line x1="1560.0" y1="116" x2="1560.0" y2="840" stroke="#21262d" stroke-width="1"/>
|
||||||
|
<text x="352.5" y="134" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12.5" fill="#8b949e" font-weight="normal" text-anchor="middle" opacity="1.0">1</text>
|
||||||
|
<text x="457.5" y="134" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12.5" fill="#8b949e" font-weight="normal" text-anchor="middle" opacity="1.0">2</text>
|
||||||
|
<text x="562.5" y="134" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12.5" fill="#8b949e" font-weight="normal" text-anchor="middle" opacity="1.0">3</text>
|
||||||
|
<text x="667.5" y="134" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12.5" fill="#8b949e" font-weight="normal" text-anchor="middle" opacity="1.0">4</text>
|
||||||
|
<text x="772.5" y="134" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12.5" fill="#8b949e" font-weight="normal" text-anchor="middle" opacity="1.0">5</text>
|
||||||
|
<text x="877.5" y="134" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12.5" fill="#8b949e" font-weight="normal" text-anchor="middle" opacity="1.0">6</text>
|
||||||
|
<text x="982.5" y="134" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12.5" fill="#8b949e" font-weight="normal" text-anchor="middle" opacity="1.0">7</text>
|
||||||
|
<text x="1087.5" y="134" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12.5" fill="#8b949e" font-weight="normal" text-anchor="middle" opacity="1.0">8</text>
|
||||||
|
<text x="1192.5" y="134" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12.5" fill="#8b949e" font-weight="normal" text-anchor="middle" opacity="1.0">9</text>
|
||||||
|
<text x="1297.5" y="134" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12.5" fill="#8b949e" font-weight="normal" text-anchor="middle" opacity="1.0">10</text>
|
||||||
|
<text x="1402.5" y="134" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12.5" fill="#8b949e" font-weight="normal" text-anchor="middle" opacity="1.0">11</text>
|
||||||
|
<text x="1507.5" y="134" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12.5" fill="#8b949e" font-weight="normal" text-anchor="middle" opacity="1.0">12</text>
|
||||||
|
<text x="284" y="134" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12.5" fill="#8b949e" font-weight="normal" text-anchor="end" opacity="1.0">月份</text>
|
||||||
|
<text x="40" y="200" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="16" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">M0 立项与调研</text>
|
||||||
|
<rect x="303.0" y="170" width="99.0" height="58" rx="10" fill="#ff7b72" stroke="#ff7b72" stroke-width="0" opacity="0.28"/>
|
||||||
|
<rect x="303.0" y="170" width="99.0" height="58" rx="10" fill="none" stroke="#ff7b72" stroke-width="1.6"/>
|
||||||
|
<text x="319.0" y="205" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="13" fill="#ff7b72" font-weight="bold" text-anchor="start" opacity="1.0">第 1 月</text>
|
||||||
|
<path d="M 416.0 192.0 L 423.0 199.0 L 416.0 206.0 L 409.0 199.0 z" fill="#ff7b72"/>
|
||||||
|
<text x="434.0" y="204.0" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">规划文档 · 选型评审 · 基准定义</text>
|
||||||
|
<text x="40" y="296" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="16" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">M1 数据工厂</text>
|
||||||
|
<rect x="408.0" y="266" width="204.0" height="58" rx="10" fill="#39d0d8" stroke="#39d0d8" stroke-width="0" opacity="0.28"/>
|
||||||
|
<rect x="408.0" y="266" width="204.0" height="58" rx="10" fill="none" stroke="#39d0d8" stroke-width="1.6"/>
|
||||||
|
<text x="424.0" y="301" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="13" fill="#39d0d8" font-weight="bold" text-anchor="start" opacity="1.0">2-3 月</text>
|
||||||
|
<path d="M 626.0 288.0 L 633.0 295.0 L 626.0 302.0 L 619.0 295.0 z" fill="#39d0d8"/>
|
||||||
|
<text x="644.0" y="300.0" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">PyFluent 自动化 · ≥500 工况数据集</text>
|
||||||
|
<text x="40" y="392" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="16" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">M2 基线模型</text>
|
||||||
|
<rect x="618.0" y="362" width="309.0" height="58" rx="10" fill="#58a6ff" stroke="#58a6ff" stroke-width="0" opacity="0.28"/>
|
||||||
|
<rect x="618.0" y="362" width="309.0" height="58" rx="10" fill="none" stroke="#58a6ff" stroke-width="1.6"/>
|
||||||
|
<text x="634.0" y="397" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="13" fill="#58a6ff" font-weight="bold" text-anchor="start" opacity="1.0">4-6 月</text>
|
||||||
|
<path d="M 941.0 384.0 L 948.0 391.0 L 941.0 398.0 L 934.0 391.0 z" fill="#58a6ff"/>
|
||||||
|
<text x="959.0" y="396.0" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">FNO 基线 · 相对 L2 < 5% · 1000 步稳定</text>
|
||||||
|
<text x="40" y="488" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="16" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">M3 数据飞轮</text>
|
||||||
|
<rect x="828.0" y="458" width="204.0" height="58" rx="10" fill="#bc8cff" stroke="#bc8cff" stroke-width="0" opacity="0.28"/>
|
||||||
|
<rect x="828.0" y="458" width="204.0" height="58" rx="10" fill="none" stroke="#bc8cff" stroke-width="1.6"/>
|
||||||
|
<text x="844.0" y="493" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="13" fill="#bc8cff" font-weight="bold" text-anchor="start" opacity="1.0">6-7 月</text>
|
||||||
|
<path d="M 1046.0 480.0 L 1053.0 487.0 L 1046.0 494.0 L 1039.0 487.0 z" fill="#bc8cff"/>
|
||||||
|
<text x="1064.0" y="492.0" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">主动学习 · 同等精度省 40% 仿真量</text>
|
||||||
|
<text x="40" y="584" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="16" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">M4 3D 与复杂几何</text>
|
||||||
|
<rect x="933.0" y="554" width="309.0" height="58" rx="10" fill="#3fb950" stroke="#3fb950" stroke-width="0" opacity="0.28"/>
|
||||||
|
<rect x="933.0" y="554" width="309.0" height="58" rx="10" fill="none" stroke="#3fb950" stroke-width="1.6"/>
|
||||||
|
<text x="949.0" y="589" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="13" fill="#3fb950" font-weight="bold" text-anchor="start" opacity="1.0">7-9 月</text>
|
||||||
|
<path d="M 1256.0 576.0 L 1263.0 583.0 L 1256.0 590.0 L 1249.0 583.0 z" fill="#3fb950"/>
|
||||||
|
<text x="1274.0" y="588.0" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">翼型 / 车体代理 · MeshGraphNet</text>
|
||||||
|
<text x="40" y="680" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="16" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">M5 实时推理系统</text>
|
||||||
|
<rect x="1143.0" y="650" width="204.0" height="58" rx="10" fill="#d29922" stroke="#d29922" stroke-width="0" opacity="0.28"/>
|
||||||
|
<rect x="1143.0" y="650" width="204.0" height="58" rx="10" fill="none" stroke="#d29922" stroke-width="1.6"/>
|
||||||
|
<text x="1159.0" y="685" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="13" fill="#d29922" font-weight="bold" text-anchor="start" opacity="1.0">9-10 月</text>
|
||||||
|
<path d="M 1361.0 672.0 L 1368.0 679.0 L 1361.0 686.0 L 1354.0 679.0 z" fill="#d29922"/>
|
||||||
|
<text x="1379.0" y="684.0" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">TensorRT · 端到端 < 100 ms</text>
|
||||||
|
<text x="40" y="776" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="16" fill="#e6edf3" font-weight="bold" text-anchor="start" opacity="1.0">M6 可视化与数字孪生</text>
|
||||||
|
<rect x="1248.0" y="746" width="309.0" height="58" rx="10" fill="#f778ba" stroke="#f778ba" stroke-width="0" opacity="0.28"/>
|
||||||
|
<rect x="1248.0" y="746" width="309.0" height="58" rx="10" fill="none" stroke="#f778ba" stroke-width="1.6"/>
|
||||||
|
<text x="1264.0" y="781" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="13" fill="#f778ba" font-weight="bold" text-anchor="start" opacity="1.0">10-12 月</text>
|
||||||
|
<text x="1218.0" y="780.0" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12.5" fill="#8b949e" font-weight="normal" text-anchor="end" opacity="1.0">WebGPU 交互 · 稀疏观测同化</text>
|
||||||
|
<path d="M 1234.0 768.0 L 1227.0 775.0 L 1234.0 782.0 L 1241.0 775.0 z" fill="#f778ba"/>
|
||||||
|
<rect x="40" y="856" width="1520" height="60" rx="10" fill="#161b22" stroke="#30363d" stroke-width="1.2" opacity="1.0"/>
|
||||||
|
<text x="60" y="884" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="14" fill="#d29922" font-weight="bold" text-anchor="start" opacity="1.0">决策点</text>
|
||||||
|
<text x="130" y="884" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">M2 精度不达标 → DeepONet / MeshGraphNet 双路线并行 M4 3D 不达标 → 退守「2D + 切片 3D」保证 M5/M6 交付 每里程碑预留 20% 时间缓冲</text>
|
||||||
|
<text x="60" y="906" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="14" fill="#3fb950" font-weight="bold" text-anchor="start" opacity="1.0">成功标准</text>
|
||||||
|
<text x="130" y="906" font-family="Microsoft YaHei, Segoe UI, sans-serif" font-size="12.5" fill="#8b949e" font-weight="normal" text-anchor="start" opacity="1.0">P1(3 个月)可行性验证 · P2(6 个月)系统闭环 · P3(12 个月)数字孪生原型</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 12 KiB |
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "physics-ai-fluent-analysis-tools",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "PhysicsAiFluentAnalysis 图件生成工具 (SVG -> PNG)",
|
||||||
|
"scripts": {
|
||||||
|
"svg2png": "node tools/svg2png.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@resvg/resvg-js": "^2.6.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,648 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
FFU 单向流洁净区流场仿真 Demo
|
||||||
|
================================
|
||||||
|
用 D2Q9 格子玻尔兹曼方法 (LBM) 模拟 FFU (Fan Filter Unit, 风机过滤单元)
|
||||||
|
下方洁净区的流场, 并用半拉格朗日输运方程求解:
|
||||||
|
|
||||||
|
- 温度场 T (设备发热源)
|
||||||
|
- 相对湿度 RH (人员呼吸/发湿源)
|
||||||
|
- 尘埃粒子浓度 C (设备 + 人员散发, 含重力沉降)
|
||||||
|
- 静压场 p (LBM 直接输出, 转换为 Pa)
|
||||||
|
|
||||||
|
物理设置
|
||||||
|
--------
|
||||||
|
计算域 : 4.0 m (宽) x 3.0 m (高), 90 px/m
|
||||||
|
FFU 送风 : 顶棚满布, 面风速 0.45 m/s (ISO 14644 单向流推荐值)
|
||||||
|
回风口 : 底部两侧 (0~0.5 m, 3.5~4.0 m), 高架地板/下侧回风
|
||||||
|
障碍物 : 工作台 + 设备(热/尘源) + 操作人员(湿/尘源) + 物料架
|
||||||
|
雷诺数 : Re = U*H/nu ~ 540 (层流单向流)
|
||||||
|
网格 : 360 x 270 = 97200 格点, D2Q9 BGK, tau = 0.62
|
||||||
|
|
||||||
|
输出
|
||||||
|
----
|
||||||
|
docs/assets/ffu-flow-field.png 速度场 + 流线 (主图)
|
||||||
|
docs/assets/ffu-scalar-fields.png 温度 / 湿度 / 粒子浓度 / 压力 四联图
|
||||||
|
docs/assets/ffu-particle-tracks.png 粒子拉格朗日轨迹
|
||||||
|
docs/assets/ffu-flow-field.svg 主图矢量版
|
||||||
|
|
||||||
|
运行
|
||||||
|
----
|
||||||
|
python tools/ffu_simulation.py
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import numpy as np
|
||||||
|
from numba import njit
|
||||||
|
|
||||||
|
import matplotlib
|
||||||
|
matplotlib.use("Agg")
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from matplotlib.patches import Rectangle, Circle, FancyArrow
|
||||||
|
from matplotlib.colors import LinearSegmentedColormap
|
||||||
|
from matplotlib.collections import LineCollection
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# 全局样式
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
BG = "#0d1117"
|
||||||
|
PANEL = "#0d1117"
|
||||||
|
FG = "#e6edf3"
|
||||||
|
MUTED = "#8b949e"
|
||||||
|
ACCENT = "#58a6ff"
|
||||||
|
WARM = "#ff7b72"
|
||||||
|
COOL = "#39d0d8"
|
||||||
|
GREEN = "#7ee787"
|
||||||
|
|
||||||
|
plt.rcParams.update({
|
||||||
|
"font.sans-serif": ["Microsoft YaHei", "SimHei", "DejaVu Sans"],
|
||||||
|
"axes.unicode_minus": False,
|
||||||
|
"svg.fonttype": "path",
|
||||||
|
"figure.facecolor": BG,
|
||||||
|
"savefig.facecolor": BG,
|
||||||
|
"axes.facecolor": BG,
|
||||||
|
"text.color": FG,
|
||||||
|
"axes.labelcolor": FG,
|
||||||
|
"xtick.color": MUTED,
|
||||||
|
"ytick.color": MUTED,
|
||||||
|
"axes.edgecolor": "#30363d",
|
||||||
|
})
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# 物理 / 数值参数
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
W_M, H_M = 4.0, 3.0 # 计算域尺寸 [m]
|
||||||
|
PPM = 90 # 像素/米
|
||||||
|
NX, NY = int(W_M * PPM), int(H_M * PPM) # 360 x 270
|
||||||
|
|
||||||
|
U0_LB = 0.08 # FFU 送风速度 [lattice]
|
||||||
|
U_PHYS = 0.45 # FFU 送风速度 [m/s]
|
||||||
|
SCALE_U = U_PHYS / U0_LB # lattice -> m/s
|
||||||
|
NU_LB = 0.04 # 运动粘度 [lattice]
|
||||||
|
TAU = 0.5 + 3.0 * NU_LB
|
||||||
|
RE = U0_LB * NY / NU_LB
|
||||||
|
|
||||||
|
T_AMB, T_SRC = 22.0, 35.0 # 环境温度 / 设备表面温度 [degC]
|
||||||
|
RH_AMB, RH_SRC = 45.0, 70.0 # 送风湿度 / 呼吸区湿度 [%]
|
||||||
|
RHO_AIR = 1.2 # 空气密度 [kg/m3]
|
||||||
|
SCALE_P = RHO_AIR * SCALE_U ** 2 # lattice -> Pa
|
||||||
|
|
||||||
|
DT_SCALAR = 4 # 标量输运时间步 (每 4 个 LBM 步输运一次)
|
||||||
|
ADV_EVERY = 4
|
||||||
|
KAPPA_T = 0.0020 # 温度扩散系数 [lattice]
|
||||||
|
KAPPA_RH = 0.0020
|
||||||
|
KAPPA_C = 0.0015
|
||||||
|
V_SETTLE = 0.004 # 粒子沉降速度 [lattice] (~2 cm/s)
|
||||||
|
|
||||||
|
N_STEPS = 14000 # LBM 总步数
|
||||||
|
SCALAR_FROM = 6000 # 从该步开始输运标量
|
||||||
|
TRACK_FROM = 9000 # 从该步开始记录粒子轨迹
|
||||||
|
|
||||||
|
# D2Q9
|
||||||
|
EX = np.array([0, 1, 0, -1, 0, 1, -1, -1, 1], dtype=np.int32)
|
||||||
|
EY = np.array([0, 0, 1, 0, -1, 1, 1, -1, -1], dtype=np.int32)
|
||||||
|
WT = np.array([4/9, 1/9, 1/9, 1/9, 1/9, 1/36, 1/36, 1/36, 1/36])
|
||||||
|
OPP = np.array([0, 3, 4, 1, 2, 7, 8, 5, 6], dtype=np.int32)
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# 几何: 单位 m
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
BENCH = (0.60, 0.00, 1.80, 0.90) # 工作台 (x0,y0,x1,y1)
|
||||||
|
EQUIP = (0.85, 0.90, 1.50, 1.25) # 设备 (热源 + 尘源)
|
||||||
|
BODY = (2.35, 0.00, 2.75, 1.45) # 人员躯干
|
||||||
|
HEAD_C = (2.55, 1.62, 0.15) # 人员头部 (圆心, 半径)
|
||||||
|
RACK = (3.00, 0.00, 3.50, 1.15) # 物料架
|
||||||
|
GRILL_L = (0.00, 0.50) # 左侧回风口 x 范围
|
||||||
|
GRILL_R = (3.50, 4.00) # 右侧回风口 x 范围
|
||||||
|
|
||||||
|
|
||||||
|
def build_geometry():
|
||||||
|
"""构建固体掩膜 + 源项掩膜"""
|
||||||
|
solid = np.zeros((NY, NX), dtype=np.bool_)
|
||||||
|
solid[:, :2] = True
|
||||||
|
solid[:, -2:] = True
|
||||||
|
solid[0, :] = True
|
||||||
|
|
||||||
|
def rect(x0, y0, x1, y1, m):
|
||||||
|
ix0, ix1 = int(round(x0 * PPM)), int(round(x1 * PPM))
|
||||||
|
iy0, iy1 = int(round(y0 * PPM)), int(round(y1 * PPM))
|
||||||
|
m[max(iy0, 0):min(iy1 + 1, NY), max(ix0, 0):min(ix1 + 1, NX)] = True
|
||||||
|
|
||||||
|
for r in (BENCH, EQUIP, BODY, RACK):
|
||||||
|
rect(*r, solid)
|
||||||
|
|
||||||
|
cx, cy, rr = HEAD_C
|
||||||
|
yy, xx = np.mgrid[0:NY, 0:NX]
|
||||||
|
head = (xx - cx * PPM) ** 2 + (yy - cy * PPM) ** 2 <= (rr * PPM) ** 2
|
||||||
|
solid |= head
|
||||||
|
|
||||||
|
# 回风口 (底部, 非固体 => 压力出口)
|
||||||
|
outlet = np.zeros_like(solid)
|
||||||
|
iy = 0
|
||||||
|
for g0, g1 in (GRILL_L, GRILL_R):
|
||||||
|
ix0, ix1 = int(g0 * PPM), int(g1 * PPM)
|
||||||
|
outlet[iy, max(ix0, 2):min(ix1, NX - 2)] = True
|
||||||
|
solid[outlet] = False
|
||||||
|
|
||||||
|
inlet = np.zeros_like(solid)
|
||||||
|
inlet[NY - 1, 2:NX - 2] = True
|
||||||
|
|
||||||
|
# 设备热/尘源: 设备外扩 3 格的流体单元
|
||||||
|
eq = np.zeros_like(solid)
|
||||||
|
rect(*EQUIP, eq)
|
||||||
|
shell = np.zeros_like(solid)
|
||||||
|
for dy in range(-3, 4):
|
||||||
|
for dx in range(-3, 4):
|
||||||
|
shell |= np.roll(np.roll(eq, dy, 0), dx, 1)
|
||||||
|
src_equip = shell & ~solid
|
||||||
|
|
||||||
|
# 人员呼吸/发湿区: 头部前方圆形区域
|
||||||
|
br = (xx - (cx + 0.20) * PPM) ** 2 + (yy - cy * PPM) ** 2 <= (0.22 * PPM) ** 2
|
||||||
|
src_head = br & ~solid
|
||||||
|
|
||||||
|
return solid, inlet, outlet, src_equip, src_head
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# LBM 求解器 (numba)
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
@njit(cache=True, fastmath=True)
|
||||||
|
def lbm_collide(f, rho, ux, uy, solid, ex, ey, w, tau):
|
||||||
|
ny, nx = rho.shape
|
||||||
|
for iy in range(ny):
|
||||||
|
for ix in range(nx):
|
||||||
|
if solid[iy, ix]:
|
||||||
|
continue
|
||||||
|
r = 0.0; mx = 0.0; my = 0.0
|
||||||
|
for k in range(9):
|
||||||
|
fk = f[k, iy, ix]
|
||||||
|
r += fk
|
||||||
|
mx += ex[k] * fk
|
||||||
|
my += ey[k] * fk
|
||||||
|
u = mx / r
|
||||||
|
v = my / r
|
||||||
|
rho[iy, ix] = r
|
||||||
|
ux[iy, ix] = u
|
||||||
|
uy[iy, ix] = v
|
||||||
|
uu = u * u + v * v
|
||||||
|
for k in range(9):
|
||||||
|
eu = ex[k] * u + ey[k] * v
|
||||||
|
feq = w[k] * r * (1.0 + 3.0 * eu + 4.5 * eu * eu - 1.5 * uu)
|
||||||
|
f[k, iy, ix] -= (f[k, iy, ix] - feq) / tau
|
||||||
|
|
||||||
|
|
||||||
|
@njit(cache=True, fastmath=True)
|
||||||
|
def lbm_bounce_back(f, solid, opp):
|
||||||
|
ny, nx = solid.shape
|
||||||
|
for iy in range(ny):
|
||||||
|
for ix in range(nx):
|
||||||
|
if solid[iy, ix]:
|
||||||
|
for k in range(9):
|
||||||
|
ko = opp[k]
|
||||||
|
if k < ko:
|
||||||
|
tmp = f[k, iy, ix]
|
||||||
|
f[k, iy, ix] = f[ko, iy, ix]
|
||||||
|
f[ko, iy, ix] = tmp
|
||||||
|
|
||||||
|
|
||||||
|
@njit(cache=True, fastmath=True)
|
||||||
|
def lbm_stream(f, ftmp, ex, ey):
|
||||||
|
ny, nx = f.shape[1], f.shape[2]
|
||||||
|
for k in range(9):
|
||||||
|
dx = ex[k]; dy = ey[k]
|
||||||
|
for iy in range(ny):
|
||||||
|
sy = iy - dy
|
||||||
|
for ix in range(nx):
|
||||||
|
sx = ix - dx
|
||||||
|
if 0 <= sy < ny and 0 <= sx < nx:
|
||||||
|
ftmp[k, iy, ix] = f[k, sy, sx]
|
||||||
|
else:
|
||||||
|
ftmp[k, iy, ix] = f[k, iy, ix]
|
||||||
|
|
||||||
|
|
||||||
|
@njit(cache=True, fastmath=True)
|
||||||
|
def lbm_bcs(f, ux, uy, solid, inlet, outlet, ex, ey, w, u0):
|
||||||
|
ny, nx = solid.shape
|
||||||
|
for ix in range(nx):
|
||||||
|
if inlet[ny - 1, ix]:
|
||||||
|
u = 0.0; v = -u0; r = 1.0
|
||||||
|
uu = u * u + v * v
|
||||||
|
for k in range(9):
|
||||||
|
eu = ex[k] * u + ey[k] * v
|
||||||
|
f[k, ny - 1, ix] = w[k] * r * (1.0 + 3.0 * eu + 4.5 * eu * eu - 1.5 * uu)
|
||||||
|
if outlet[0, ix]:
|
||||||
|
u = ux[1, ix]; v = uy[1, ix]; r = 1.0
|
||||||
|
uu = u * u + v * v
|
||||||
|
for k in range(9):
|
||||||
|
eu = ex[k] * u + ey[k] * v
|
||||||
|
f[k, 0, ix] = w[k] * r * (1.0 + 3.0 * eu + 4.5 * eu * eu - 1.5 * uu)
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# 标量输运 (半拉格朗日 + 扩散)
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
@njit(cache=True, fastmath=True)
|
||||||
|
def advect_scalars(F, ux, uy, solid, kappa, dt, vs):
|
||||||
|
nc, ny, nx = F.shape
|
||||||
|
out = np.empty_like(F)
|
||||||
|
for c in range(nc):
|
||||||
|
k = kappa[c]
|
||||||
|
s = vs[c]
|
||||||
|
for iy in range(ny):
|
||||||
|
for ix in range(nx):
|
||||||
|
fc = F[c, iy, ix]
|
||||||
|
if solid[iy, ix]:
|
||||||
|
out[c, iy, ix] = 0.0
|
||||||
|
continue
|
||||||
|
xd = ix - ux[iy, ix] * dt
|
||||||
|
yd = iy - (uy[iy, ix] + s) * dt
|
||||||
|
if xd < 0.0: xd = 0.0
|
||||||
|
if xd > nx - 1.001: xd = nx - 1.001
|
||||||
|
if yd < 0.0: yd = 0.0
|
||||||
|
if yd > ny - 1.001: yd = ny - 1.001
|
||||||
|
x0 = int(xd); y0 = int(yd)
|
||||||
|
if x0 > nx - 2: x0 = nx - 2
|
||||||
|
if y0 > ny - 2: y0 = ny - 2
|
||||||
|
fx = xd - x0; fy = yd - y0
|
||||||
|
v00 = F[c, y0, x0]; v01 = F[c, y0, x0 + 1]
|
||||||
|
v10 = F[c, y0 + 1, x0]; v11 = F[c, y0 + 1, x0 + 1]
|
||||||
|
if solid[y0, x0]: v00 = fc
|
||||||
|
if solid[y0, x0 + 1]: v01 = fc
|
||||||
|
if solid[y0 + 1, x0]: v10 = fc
|
||||||
|
if solid[y0 + 1, x0 + 1]: v11 = fc
|
||||||
|
val = (v00 * (1 - fx) * (1 - fy) + v01 * fx * (1 - fy) +
|
||||||
|
v10 * (1 - fx) * fy + v11 * fx * fy)
|
||||||
|
l = fc; r = fc; d = fc; u = fc
|
||||||
|
if ix > 0 and not solid[iy, ix - 1]: l = F[c, iy, ix - 1]
|
||||||
|
if ix < nx - 1 and not solid[iy, ix + 1]: r = F[c, iy, ix + 1]
|
||||||
|
if iy > 0 and not solid[iy - 1, ix]: d = F[c, iy - 1, ix]
|
||||||
|
if iy < ny - 1 and not solid[iy + 1, ix]: u = F[c, iy + 1, ix]
|
||||||
|
out[c, iy, ix] = val + k * dt * (l + r + d + u - 4.0 * fc)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# 粒子轨迹 (拉格朗日)
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
@njit(cache=True, fastmath=True)
|
||||||
|
def sample_uv(ux, uy, x, y):
|
||||||
|
ny, nx = ux.shape
|
||||||
|
if x < 0: x = 0.0
|
||||||
|
if x > nx - 1.001: x = nx - 1.001
|
||||||
|
if y < 0: y = 0.0
|
||||||
|
if y > ny - 1.001: y = ny - 1.001
|
||||||
|
x0 = int(x); y0 = int(y)
|
||||||
|
if x0 > nx - 2: x0 = nx - 2
|
||||||
|
if y0 > ny - 2: y0 = ny - 2
|
||||||
|
fx = x - x0; fy = y - y0
|
||||||
|
u = (ux[y0, x0] * (1 - fx) * (1 - fy) + ux[y0, x0 + 1] * fx * (1 - fy) +
|
||||||
|
ux[y0 + 1, x0] * (1 - fx) * fy + ux[y0 + 1, x0 + 1] * fx * fy)
|
||||||
|
v = (uy[y0, x0] * (1 - fx) * (1 - fy) + uy[y0, x0 + 1] * fx * (1 - fy) +
|
||||||
|
uy[y0 + 1, x0] * (1 - fx) * fy + uy[y0 + 1, x0 + 1] * fx * fy)
|
||||||
|
return u, v
|
||||||
|
|
||||||
|
|
||||||
|
@njit(cache=True, fastmath=True)
|
||||||
|
def step_particles(px, py, alive, age, ux, uy, solid, vs):
|
||||||
|
n = px.shape[0]
|
||||||
|
ny, nx = solid.shape
|
||||||
|
for i in range(n):
|
||||||
|
if not alive[i]:
|
||||||
|
continue
|
||||||
|
x, y = px[i], py[i]
|
||||||
|
u1, v1 = sample_uv(ux, uy, x, y)
|
||||||
|
xm, ym = x + 0.5 * u1, y + 0.5 * (v1 - vs)
|
||||||
|
u2, v2 = sample_uv(ux, uy, xm, ym)
|
||||||
|
xn, yn = x + u2, y + (v2 - vs)
|
||||||
|
ix, iy = int(xn), int(yn)
|
||||||
|
if 0 <= ix < nx and 0 <= iy < ny and not solid[iy, ix]:
|
||||||
|
px[i] = xn; py[i] = yn
|
||||||
|
else:
|
||||||
|
ix, iy = int(xn), int(y)
|
||||||
|
if 0 <= ix < nx and 0 <= iy < ny and not solid[iy, ix]:
|
||||||
|
px[i] = xn
|
||||||
|
else:
|
||||||
|
ix, iy = int(x), int(yn)
|
||||||
|
if 0 <= ix < nx and 0 <= iy < ny and not solid[iy, ix]:
|
||||||
|
py[i] = yn
|
||||||
|
age[i] += 1.0
|
||||||
|
if py[i] < 1.5 or px[i] < 2.0 or px[i] > nx - 3.0 or age[i] > 2500:
|
||||||
|
alive[i] = False
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# 主循环
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
def main():
|
||||||
|
out_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||||||
|
"docs", "assets")
|
||||||
|
os.makedirs(out_dir, exist_ok=True)
|
||||||
|
|
||||||
|
print(f"[FFU] grid {NX}x{NY} Re={RE:.0f} tau={TAU:.3f} steps={N_STEPS}")
|
||||||
|
solid, inlet, outlet, src_equip, src_head = build_geometry()
|
||||||
|
|
||||||
|
f = np.zeros((9, NY, NX))
|
||||||
|
ftmp = np.zeros_like(f)
|
||||||
|
rho = np.ones((NY, NX))
|
||||||
|
ux = np.zeros((NY, NX))
|
||||||
|
uy = np.zeros((NY, NX))
|
||||||
|
uy[:] = -U0_LB
|
||||||
|
for k in range(9):
|
||||||
|
eu = EX[k] * 0.0 + EY[k] * (-U0_LB)
|
||||||
|
f[k] = WT[k] * (1.0 + 3.0 * eu + 4.5 * eu * eu - 1.5 * U0_LB ** 2)
|
||||||
|
|
||||||
|
# 标量: [T, RH, C]
|
||||||
|
F = np.zeros((3, NY, NX))
|
||||||
|
kappa = np.array([KAPPA_T, KAPPA_RH, KAPPA_C])
|
||||||
|
vs = np.array([0.0, 0.0, V_SETTLE])
|
||||||
|
|
||||||
|
# 粒子
|
||||||
|
N_PART = 2400
|
||||||
|
px = np.zeros(N_PART); py = np.zeros(N_PART)
|
||||||
|
alive = np.zeros(N_PART, dtype=np.bool_)
|
||||||
|
age = np.zeros(N_PART)
|
||||||
|
gen = np.zeros(N_PART)
|
||||||
|
gen_counter = 0
|
||||||
|
next_p = 0
|
||||||
|
max_pts = 400000
|
||||||
|
traj = np.zeros((max_pts, 4))
|
||||||
|
n_pts = 0
|
||||||
|
src_px = np.concatenate([np.where(src_equip)[1], np.where(src_head)[1]])
|
||||||
|
src_py = np.concatenate([np.where(src_equip)[0], np.where(src_head)[0]])
|
||||||
|
rng = np.random.default_rng(7)
|
||||||
|
|
||||||
|
for step in range(N_STEPS):
|
||||||
|
lbm_collide(f, rho, ux, uy, solid, EX, EY, WT, TAU)
|
||||||
|
lbm_bounce_back(f, solid, OPP)
|
||||||
|
lbm_stream(f, ftmp, EX, EY)
|
||||||
|
f, ftmp = ftmp, f
|
||||||
|
lbm_bcs(f, ux, uy, solid, inlet, outlet, EX, EY, WT, U0_LB)
|
||||||
|
|
||||||
|
if step >= SCALAR_FROM and step % ADV_EVERY == 0:
|
||||||
|
F = advect_scalars(F, ux, uy, solid, kappa, DT_SCALAR, vs)
|
||||||
|
F[0][src_equip] = 1.0
|
||||||
|
F[1][src_head] = 1.0
|
||||||
|
F[2][src_equip] = 1.0
|
||||||
|
F[2][src_head] = 1.0
|
||||||
|
F[:, NY - 1, :] = 0.0
|
||||||
|
F[:, solid] = 0.0
|
||||||
|
|
||||||
|
if step >= TRACK_FROM:
|
||||||
|
if step % 40 == 0:
|
||||||
|
for _ in range(18):
|
||||||
|
i = next_p % N_PART
|
||||||
|
next_p += 1
|
||||||
|
gen_counter += 1
|
||||||
|
j = rng.integers(0, src_px.size)
|
||||||
|
px[i] = src_px[j] + rng.uniform(-1, 1)
|
||||||
|
py[i] = src_py[j] + rng.uniform(-1, 1)
|
||||||
|
alive[i] = True
|
||||||
|
age[i] = 0.0
|
||||||
|
gen[i] = gen_counter
|
||||||
|
step_particles(px, py, alive, age, ux, uy, solid, V_SETTLE)
|
||||||
|
if step % 4 == 0:
|
||||||
|
idx = np.where(alive)[0]
|
||||||
|
m = min(idx.size, max_pts - n_pts)
|
||||||
|
if m > 0:
|
||||||
|
sel = idx[:m]
|
||||||
|
traj[n_pts:n_pts + m, 0] = px[sel]
|
||||||
|
traj[n_pts:n_pts + m, 1] = py[sel]
|
||||||
|
traj[n_pts:n_pts + m, 2] = age[sel]
|
||||||
|
traj[n_pts:n_pts + m, 3] = gen[sel]
|
||||||
|
n_pts += m
|
||||||
|
|
||||||
|
if step % 2000 == 0:
|
||||||
|
umax = np.sqrt(ux ** 2 + uy ** 2).max() * SCALE_U
|
||||||
|
print(f" step {step:6d} |U|max={umax:6.3f} m/s rho=[{rho.min():.4f},{rho.max():.4f}]")
|
||||||
|
|
||||||
|
print(f"[FFU] simulation done, trajectory points: {n_pts}")
|
||||||
|
traj = traj[:n_pts]
|
||||||
|
|
||||||
|
np.savez_compressed(os.path.join(out_dir, "ffu_fields.npz"),
|
||||||
|
ux=ux, uy=uy, T=F[0], RH=F[1], C=F[2], rho=rho, traj=traj)
|
||||||
|
make_figures(out_dir, ux, uy, F, rho, solid, src_equip, src_head, traj)
|
||||||
|
print("[FFU] figures written to", out_dir)
|
||||||
|
|
||||||
|
|
||||||
|
def make_figures(out_dir, ux, uy, F, rho, solid, src_equip, src_head, traj):
|
||||||
|
speed = np.sqrt(ux ** 2 + uy ** 2) * SCALE_U
|
||||||
|
speed[solid] = np.nan
|
||||||
|
T_phys = T_AMB + F[0] * (T_SRC - T_AMB)
|
||||||
|
RH_phys = RH_AMB + F[1] * (RH_SRC - RH_AMB)
|
||||||
|
C_norm = F[2].copy()
|
||||||
|
p_pa = (rho - 1.0) / 3.0 * SCALE_P
|
||||||
|
for _ in range(3):
|
||||||
|
p_s = np.zeros_like(p_pa)
|
||||||
|
for dy in (-1, 0, 1):
|
||||||
|
for dx in (-1, 0, 1):
|
||||||
|
p_s += np.roll(np.roll(p_pa, dy, 0), dx, 1)
|
||||||
|
p_pa = p_s / 9.0
|
||||||
|
for arr in (T_phys, RH_phys, C_norm, p_pa):
|
||||||
|
arr[solid] = np.nan
|
||||||
|
|
||||||
|
plot_hero(out_dir, ux, uy, speed, solid)
|
||||||
|
plot_scalars(out_dir, T_phys, RH_phys, C_norm, p_pa, solid, src_equip, src_head)
|
||||||
|
plot_tracks(out_dir, speed, traj, solid)
|
||||||
|
|
||||||
|
|
||||||
|
def replot():
|
||||||
|
"""仅重绘图形, 不重跑仿真"""
|
||||||
|
out_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||||||
|
"docs", "assets")
|
||||||
|
d = np.load(os.path.join(out_dir, "ffu_fields.npz"))
|
||||||
|
solid, inlet, outlet, src_equip, src_head = build_geometry()
|
||||||
|
F = np.stack([d["T"], d["RH"], d["C"]])
|
||||||
|
make_figures(out_dir, d["ux"], d["uy"], F, d["rho"], solid,
|
||||||
|
src_equip, src_head, d["traj"])
|
||||||
|
print("[FFU] replotted from ffu_fields.npz")
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# 绘图
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
CMAP_FLOW = LinearSegmentedColormap.from_list(
|
||||||
|
"flow", ["#0a1626", "#123c63", "#1f6feb", "#39d0d8", "#7ee787", "#f0f6fc"])
|
||||||
|
|
||||||
|
|
||||||
|
def draw_room(ax, solid):
|
||||||
|
"""绘制房间轮廓与障碍物"""
|
||||||
|
for (x0, y0, x1, y1) in (BENCH, EQUIP, BODY, RACK):
|
||||||
|
ax.add_patch(Rectangle((x0, y0), x1 - x0, y1 - y0,
|
||||||
|
facecolor="#2d333b", edgecolor="#adbac7",
|
||||||
|
linewidth=1.1, zorder=6))
|
||||||
|
cx, cy, rr = HEAD_C
|
||||||
|
ax.add_patch(Circle((cx, cy), rr, facecolor="#2d333b",
|
||||||
|
edgecolor="#adbac7", linewidth=1.1, zorder=6))
|
||||||
|
# FFU 顶棚
|
||||||
|
ax.add_patch(Rectangle((0.06, H_M - 0.06), W_M - 0.12, 0.045,
|
||||||
|
facecolor="#1f6feb", edgecolor="#58a6ff",
|
||||||
|
linewidth=0.8, alpha=0.85, zorder=6))
|
||||||
|
# 回风口
|
||||||
|
for g0, g1 in (GRILL_L, GRILL_R):
|
||||||
|
ax.add_patch(Rectangle((g0 + 0.02, 0.0), g1 - g0 - 0.04, 0.035,
|
||||||
|
facecolor="#7ee787", edgecolor="#7ee787",
|
||||||
|
alpha=0.65, zorder=6))
|
||||||
|
|
||||||
|
|
||||||
|
def draw_sources(ax):
|
||||||
|
ax.plot(1.18, 1.25, marker="*", ms=13, color="#ff7b72", zorder=7)
|
||||||
|
ax.plot(2.75, 1.62, marker="o", ms=8, color="#39d0d8", zorder=7)
|
||||||
|
ax.plot(1.18, 1.35, marker="^", ms=7, color="#f0e68c", zorder=7)
|
||||||
|
|
||||||
|
|
||||||
|
def decorate_hero(ax):
|
||||||
|
"""FFU 送风箭头 + 回风箭头 + 标注"""
|
||||||
|
for x in np.linspace(0.45, W_M - 0.45, 9):
|
||||||
|
ax.annotate("", xy=(x, H_M - 0.30), xytext=(x, H_M - 0.075),
|
||||||
|
arrowprops=dict(arrowstyle="-|>", color="#58a6ff",
|
||||||
|
lw=1.4, alpha=0.9), zorder=7)
|
||||||
|
ax.text(W_M / 2, H_M - 0.145, "FFU 送风口 0.45 m/s(满布率 100%)",
|
||||||
|
ha="center", va="bottom", fontsize=11.5, color="#58a6ff", zorder=8)
|
||||||
|
for g0, g1 in (GRILL_L, GRILL_R):
|
||||||
|
xm = (g0 + g1) / 2
|
||||||
|
ax.annotate("", xy=(xm, -0.02), xytext=(xm, 0.16),
|
||||||
|
arrowprops=dict(arrowstyle="-|>", color="#7ee787", lw=1.6),
|
||||||
|
annotation_clip=False, zorder=7)
|
||||||
|
ax.text(xm, -0.09, "回风口", ha="center", va="top",
|
||||||
|
fontsize=10, color="#7ee787", clip_on=False, zorder=8)
|
||||||
|
ax.annotate("工作台", xy=(1.20, 0.92), xytext=(1.55, 1.62),
|
||||||
|
fontsize=10.5, color="#e6edf3", zorder=8,
|
||||||
|
arrowprops=dict(arrowstyle="-", color="#8b949e", lw=0.8))
|
||||||
|
ax.annotate("设备(热源+尘源)", xy=(1.18, 1.25), xytext=(0.42, 2.02),
|
||||||
|
fontsize=10.5, color="#ff7b72", zorder=8,
|
||||||
|
arrowprops=dict(arrowstyle="-", color="#8b949e", lw=0.8))
|
||||||
|
ax.annotate("操作人员(湿源+尘源)", xy=(2.58, 1.66), xytext=(2.62, 2.30),
|
||||||
|
fontsize=10.5, color="#39d0d8", zorder=8,
|
||||||
|
arrowprops=dict(arrowstyle="-", color="#8b949e", lw=0.8))
|
||||||
|
ax.annotate("物料架", xy=(3.25, 1.18), xytext=(3.10, 1.72),
|
||||||
|
fontsize=10.5, color="#e6edf3", zorder=8,
|
||||||
|
arrowprops=dict(arrowstyle="-", color="#8b949e", lw=0.8))
|
||||||
|
|
||||||
|
|
||||||
|
def plot_hero(out_dir, ux, uy, speed, solid):
|
||||||
|
fig, ax = plt.subplots(figsize=(15.5, 9.2))
|
||||||
|
im = ax.imshow(speed, origin="lower", extent=[0, W_M, 0, H_M],
|
||||||
|
cmap=CMAP_FLOW, vmin=0, vmax=0.75, interpolation="bilinear")
|
||||||
|
xs = np.linspace(0.10, W_M - 0.10, 150)
|
||||||
|
ys = np.linspace(0.08, H_M - 0.10, 110)
|
||||||
|
XS, YS = np.meshgrid(xs, ys)
|
||||||
|
ix = np.clip((XS * PPM).astype(int), 0, NX - 1)
|
||||||
|
iy = np.clip((YS * PPM).astype(int), 0, NY - 1)
|
||||||
|
UU = np.where(solid[iy, ix], np.nan, ux[iy, ix] * SCALE_U)
|
||||||
|
VV = np.where(solid[iy, ix], np.nan, uy[iy, ix] * SCALE_U)
|
||||||
|
with np.errstate(invalid="ignore"):
|
||||||
|
ax.streamplot(xs, ys, UU, VV, density=1.5, color=(0.79, 0.82, 0.85, 0.5),
|
||||||
|
linewidth=0.55, arrowsize=0.7, zorder=5)
|
||||||
|
draw_room(ax, solid)
|
||||||
|
draw_sources(ax)
|
||||||
|
decorate_hero(ax)
|
||||||
|
cb = fig.colorbar(im, ax=ax, fraction=0.032, pad=0.015)
|
||||||
|
cb.set_label("速度大小 |U| (m/s)", fontsize=11)
|
||||||
|
cb.outline.set_edgecolor("#30363d")
|
||||||
|
ax.set_xlim(-0.01, W_M + 0.01)
|
||||||
|
ax.set_ylim(-0.16, H_M + 0.01)
|
||||||
|
ax.set_xlabel("x (m)", fontsize=11)
|
||||||
|
ax.set_ylabel("y (m)", fontsize=11)
|
||||||
|
ax.set_title("FFU 单向流洁净区流场仿真(D2Q9 LBM,Re≈540,网格 360×270)",
|
||||||
|
fontsize=15, pad=14)
|
||||||
|
ax.text(0.5, 1.012, "送风 0.45 m/s · 顶棚满布 · 底部两侧回风 · 流线为气流轨迹",
|
||||||
|
transform=ax.transAxes, ha="center", va="bottom",
|
||||||
|
fontsize=10.5, color=MUTED)
|
||||||
|
fig.tight_layout()
|
||||||
|
fig.savefig(os.path.join(out_dir, "ffu-flow-field.png"), dpi=155)
|
||||||
|
fig.savefig(os.path.join(out_dir, "ffu-flow-field.svg"))
|
||||||
|
plt.close(fig)
|
||||||
|
print(" -> ffu-flow-field.png / .svg")
|
||||||
|
|
||||||
|
|
||||||
|
def _panel(ax, field, solid, cmap, label, vmin=None, vmax=None, center=None):
|
||||||
|
if center is not None:
|
||||||
|
lim = np.nanmax(np.abs(field)) * 1.05
|
||||||
|
im = ax.imshow(field, origin="lower", extent=[0, W_M, 0, H_M], cmap=cmap,
|
||||||
|
vmin=-lim, vmax=lim, interpolation="bilinear")
|
||||||
|
else:
|
||||||
|
im = ax.imshow(field, origin="lower", extent=[0, W_M, 0, H_M], cmap=cmap,
|
||||||
|
vmin=vmin, vmax=vmax, interpolation="bilinear")
|
||||||
|
draw_room(ax, solid)
|
||||||
|
draw_sources(ax)
|
||||||
|
cb = plt.colorbar(im, ax=ax, fraction=0.036, pad=0.02)
|
||||||
|
cb.set_label(label, fontsize=10)
|
||||||
|
cb.outline.set_edgecolor("#30363d")
|
||||||
|
cb.ax.tick_params(labelsize=9)
|
||||||
|
ax.set_xlim(-0.01, W_M + 0.01)
|
||||||
|
ax.set_ylim(0, H_M)
|
||||||
|
ax.set_xticks([0, 1, 2, 3, 4])
|
||||||
|
ax.set_xlabel("x (m)", fontsize=10)
|
||||||
|
return im
|
||||||
|
|
||||||
|
|
||||||
|
def plot_scalars(out_dir, T, RH, C, p, solid, src_equip, src_head):
|
||||||
|
fig, axes = plt.subplots(2, 2, figsize=(15.5, 10.6))
|
||||||
|
ax = axes[0, 0]
|
||||||
|
_panel(ax, T, solid, "inferno", "温度 T (°C)", vmin=T_AMB, vmax=T_SRC)
|
||||||
|
ax.set_title("(a) 温度场 —— 设备热羽被单向流下扫", fontsize=12.5, color=FG, pad=8)
|
||||||
|
|
||||||
|
ax = axes[0, 1]
|
||||||
|
_panel(ax, RH, solid, "GnBu", "相对湿度 RH (%)", vmin=RH_AMB, vmax=RH_SRC)
|
||||||
|
ax.set_title("(b) 湿度场 —— 呼吸区湿气随气流下送", fontsize=12.5, color=FG, pad=8)
|
||||||
|
|
||||||
|
ax = axes[1, 0]
|
||||||
|
_panel(ax, C, solid, "magma", "归一化粒子浓度 C/C₀", vmin=0, vmax=0.6)
|
||||||
|
ax.set_title("(c) 尘埃粒子浓度 —— 尘源散发与输运", fontsize=12.5, color=FG, pad=8)
|
||||||
|
|
||||||
|
ax = axes[1, 1]
|
||||||
|
_panel(ax, p, solid, "RdBu_r", "静压偏差 Δp (Pa)", center=0)
|
||||||
|
ax.set_title("(d) 压力场 —— FFU 送风正压与回风负压", fontsize=12.5, color=FG, pad=8)
|
||||||
|
|
||||||
|
fig.suptitle("FFU 对下方温湿度、压力与尘埃粒子浓度的影响(稳态,D2Q9 LBM + 标量输运)",
|
||||||
|
fontsize=15, y=0.985)
|
||||||
|
fig.text(0.5, 0.012,
|
||||||
|
"★ 热/尘源(设备) ● 湿/尘源(人员呼吸区) ▲ 尘源(设备上方) "
|
||||||
|
"送风 0.45 m/s,设备表面 35 °C,呼吸区 RH 70%",
|
||||||
|
ha="center", fontsize=10, color=MUTED)
|
||||||
|
fig.tight_layout(rect=[0, 0.03, 1, 0.965])
|
||||||
|
fig.savefig(os.path.join(out_dir, "ffu-scalar-fields.png"), dpi=150)
|
||||||
|
plt.close(fig)
|
||||||
|
print(" -> ffu-scalar-fields.png")
|
||||||
|
|
||||||
|
|
||||||
|
def plot_tracks(out_dir, speed, traj, solid):
|
||||||
|
fig, ax = plt.subplots(figsize=(15.0, 8.9))
|
||||||
|
ax.imshow(speed, origin="lower", extent=[0, W_M, 0, H_M],
|
||||||
|
cmap=CMAP_FLOW, vmin=0, vmax=0.75, interpolation="bilinear", alpha=0.9)
|
||||||
|
if traj.shape[0] > 1:
|
||||||
|
x = traj[:, 0] / PPM
|
||||||
|
y = traj[:, 1] / PPM
|
||||||
|
pid = traj[:, 3]
|
||||||
|
age = traj[:, 2] / 60.0
|
||||||
|
m = pid[1:] == pid[:-1]
|
||||||
|
segs = np.stack([np.column_stack([x[:-1][m], y[:-1][m]]),
|
||||||
|
np.column_stack([x[1:][m], y[1:][m]])], axis=1)
|
||||||
|
lc = LineCollection(segs, cmap="cool", linewidths=0.7, alpha=0.6)
|
||||||
|
lc.set_array(age[1:][m])
|
||||||
|
ax.add_collection(lc)
|
||||||
|
cb = plt.colorbar(lc, ax=ax, fraction=0.032, pad=0.015)
|
||||||
|
cb.set_label("粒子在洁净区内的滞留时间 (s)", fontsize=10.5)
|
||||||
|
cb.outline.set_edgecolor("#30363d")
|
||||||
|
draw_room(ax, solid)
|
||||||
|
draw_sources(ax)
|
||||||
|
decorate_hero(ax)
|
||||||
|
ax.set_xlim(-0.01, W_M + 0.01)
|
||||||
|
ax.set_ylim(-0.16, H_M + 0.01)
|
||||||
|
ax.set_xlabel("x (m)", fontsize=11)
|
||||||
|
ax.set_ylabel("y (m)", fontsize=11)
|
||||||
|
ax.set_title("尘埃粒子拉格朗日轨迹 —— 输运路径与滞留时间", fontsize=15, pad=14)
|
||||||
|
ax.text(0.5, 1.012, "粒子释放自设备与人员源 · 含重力沉降 · 颜色越暖表示滞留越久(易沉积/死角)",
|
||||||
|
transform=ax.transAxes, ha="center", va="bottom", fontsize=10.5, color=MUTED)
|
||||||
|
fig.tight_layout()
|
||||||
|
fig.savefig(os.path.join(out_dir, "ffu-particle-tracks.png"), dpi=150)
|
||||||
|
plt.close(fig)
|
||||||
|
print(" -> ffu-particle-tracks.png")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
if "--replot" in sys.argv:
|
||||||
|
replot()
|
||||||
|
else:
|
||||||
|
main()
|
||||||
@@ -0,0 +1,343 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
FFU 流场快速代理 —— 粗网格快速求解 + 全分辨率重建
|
||||||
|
====================================================
|
||||||
|
思路: 不做高保真求解, 而是在 1/3 分辨率网格上跑同一套 D2Q9 LBM,
|
||||||
|
把流场/标量场算到准稳态, 再双线性重建到全分辨率出图。
|
||||||
|
|
||||||
|
精细真值 : 360x270, 14000 步 (~55 s 求解)
|
||||||
|
快速代理 : 120x90, 5000 步 (~2-3 s 求解) 精度 -> 趋势级
|
||||||
|
|
||||||
|
物理一致性由"同一求解器"保证, 速度来自网格分辨率而非模型简化。
|
||||||
|
对标 tools/ffu_simulation.py 的输出验证趋势 (相关系数 / 涡量结构 / 羽流路径)。
|
||||||
|
|
||||||
|
运行:
|
||||||
|
python tools/ffu_surrogate.py # 快速出图 + 趋势验证对比
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
|
import matplotlib
|
||||||
|
matplotlib.use("Agg")
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
from ffu_simulation import (
|
||||||
|
W_M, H_M, PPM, NX, NY, U0_LB, U_PHYS, SCALE_U, SCALE_P, RHO_AIR,
|
||||||
|
T_AMB, T_SRC, RH_AMB, RH_SRC,
|
||||||
|
BENCH, EQUIP, BODY, HEAD_C, RACK, GRILL_L, GRILL_R,
|
||||||
|
EX, EY, WT, OPP, KAPPA_T, KAPPA_RH, KAPPA_C, V_SETTLE,
|
||||||
|
lbm_collide, lbm_bounce_back, lbm_stream, lbm_bcs, advect_scalars,
|
||||||
|
draw_room, draw_sources, decorate_hero, CMAP_FLOW,
|
||||||
|
BG, FG, MUTED, ACCENT,
|
||||||
|
)
|
||||||
|
|
||||||
|
OUT = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||||||
|
"docs", "assets")
|
||||||
|
|
||||||
|
|
||||||
|
def safe_save(fig, name, **kw):
|
||||||
|
"""文件被占用时自动回退到临时文件 / 新文件名"""
|
||||||
|
path = os.path.join(OUT, name)
|
||||||
|
try:
|
||||||
|
fig.savefig(path, **kw)
|
||||||
|
return path
|
||||||
|
except PermissionError:
|
||||||
|
pass
|
||||||
|
tmp = path + ".tmp.png"
|
||||||
|
fig.savefig(tmp, **kw)
|
||||||
|
try:
|
||||||
|
os.replace(tmp, path)
|
||||||
|
return path
|
||||||
|
except OSError:
|
||||||
|
alt = path.replace(".png", "-v2.png")
|
||||||
|
os.replace(tmp, alt)
|
||||||
|
print(f" [!] {name} 被占用, 已另存为 {os.path.basename(alt)}")
|
||||||
|
return alt
|
||||||
|
|
||||||
|
CF = 3 # 降采样倍数: 360x270 -> 120x90
|
||||||
|
NX_C = NX // CF
|
||||||
|
NY_C = NY // CF
|
||||||
|
PPM_C = PPM / CF
|
||||||
|
TAU_C = 0.5 + 3.0 * 0.02 # 粗网格用略大粘度保证稳定 (nu=0.02)
|
||||||
|
STEPS_C = 5000
|
||||||
|
SCALAR_FROM_C = 1200
|
||||||
|
ADV_EVERY = 4
|
||||||
|
DT_SCALAR = 4
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# 几何 (可指定分辨率)
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
def build_geometry_at(nx, ny, ppm):
|
||||||
|
solid = np.zeros((ny, nx), dtype=np.bool_)
|
||||||
|
solid[:, :2] = True
|
||||||
|
solid[:, -2:] = True
|
||||||
|
solid[0, :] = True
|
||||||
|
|
||||||
|
def rect(x0, y0, x1, y1, m):
|
||||||
|
ix0, ix1 = int(round(x0 * ppm)), int(round(x1 * ppm))
|
||||||
|
iy0, iy1 = int(round(y0 * ppm)), int(round(y1 * ppm))
|
||||||
|
m[max(iy0, 0):min(iy1 + 1, ny), max(ix0, 0):min(ix1 + 1, nx)] = True
|
||||||
|
|
||||||
|
for r in (BENCH, EQUIP, BODY, RACK):
|
||||||
|
rect(*r, solid)
|
||||||
|
cx, cy, rr = HEAD_C
|
||||||
|
yy, xx = np.mgrid[0:ny, 0:nx]
|
||||||
|
solid |= (xx - cx * ppm) ** 2 + (yy - cy * ppm) ** 2 <= (rr * ppm) ** 2
|
||||||
|
|
||||||
|
outlet = np.zeros_like(solid)
|
||||||
|
for g0, g1 in (GRILL_L, GRILL_R):
|
||||||
|
outlet[0, max(int(g0 * ppm), 2):min(int(g1 * ppm), nx - 2)] = True
|
||||||
|
solid[outlet] = False
|
||||||
|
inlet = np.zeros_like(solid)
|
||||||
|
inlet[ny - 1, 2:nx - 2] = True
|
||||||
|
|
||||||
|
eq = np.zeros_like(solid)
|
||||||
|
rect(*EQUIP, eq)
|
||||||
|
shell = np.zeros_like(solid)
|
||||||
|
for dy in range(-2, 3):
|
||||||
|
for dx in range(-2, 3):
|
||||||
|
shell |= np.roll(np.roll(eq, dy, 0), dx, 1)
|
||||||
|
src_equip = shell & ~solid
|
||||||
|
|
||||||
|
br = (xx - (cx + 0.20) * ppm) ** 2 + (yy - cy * ppm) ** 2 <= (0.22 * ppm) ** 2
|
||||||
|
src_head = br & ~solid
|
||||||
|
return solid, inlet, outlet, src_equip, src_head
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# 粗网格快速求解
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
def run_coarse():
|
||||||
|
t0 = time.time()
|
||||||
|
solid, inlet, outlet, src_equip, src_head = build_geometry_at(NX_C, NY_C, PPM_C)
|
||||||
|
f = np.zeros((9, NY_C, NX_C))
|
||||||
|
ftmp = np.zeros_like(f)
|
||||||
|
rho = np.ones((NY_C, NX_C))
|
||||||
|
ux = np.zeros((NY_C, NX_C))
|
||||||
|
uy = np.full((NY_C, NX_C), -U0_LB)
|
||||||
|
for k in range(9):
|
||||||
|
eu = EY[k] * (-U0_LB)
|
||||||
|
f[k] = WT[k] * (1.0 + 3.0 * eu + 4.5 * eu * eu - 1.5 * U0_LB ** 2)
|
||||||
|
|
||||||
|
F = np.zeros((3, NY_C, NX_C))
|
||||||
|
kappa = np.array([KAPPA_T, KAPPA_RH, KAPPA_C])
|
||||||
|
vs = np.array([0.0, 0.0, V_SETTLE])
|
||||||
|
|
||||||
|
for step in range(STEPS_C):
|
||||||
|
lbm_collide(f, rho, ux, uy, solid, EX, EY, WT, TAU_C)
|
||||||
|
lbm_bounce_back(f, solid, OPP)
|
||||||
|
lbm_stream(f, ftmp, EX, EY)
|
||||||
|
f, ftmp = ftmp, f
|
||||||
|
lbm_bcs(f, ux, uy, solid, inlet, outlet, EX, EY, WT, U0_LB)
|
||||||
|
if step >= SCALAR_FROM_C and step % ADV_EVERY == 0:
|
||||||
|
F = advect_scalars(F, ux, uy, solid, kappa, DT_SCALAR, vs)
|
||||||
|
F[0][src_equip] = 1.0
|
||||||
|
F[1][src_head] = 1.0
|
||||||
|
F[2][src_equip] = 1.0
|
||||||
|
F[2][src_head] = 1.0
|
||||||
|
F[:, NY_C - 1, :] = 0.0
|
||||||
|
F[:, solid] = 0.0
|
||||||
|
dt = time.time() - t0
|
||||||
|
print(f" [快速求解] {NX_C}x{NY_C} 网格 x {STEPS_C} 步: {dt:.2f} s "
|
||||||
|
f"(精细真值 {NX}x{NY} x 14000 步: 55 s, 加速 {55/max(dt,1e-6):.0f}x)")
|
||||||
|
return solid, ux, uy, F, rho
|
||||||
|
|
||||||
|
|
||||||
|
def upsample(a, ny_out, nx_out):
|
||||||
|
ny, nx = a.shape
|
||||||
|
ys = np.linspace(0, ny - 1, ny_out)
|
||||||
|
xs = np.linspace(0, nx - 1, nx_out)
|
||||||
|
y0 = np.clip(ys.astype(int), 0, ny - 2)[:, None]
|
||||||
|
x0 = np.clip(xs.astype(int), 0, nx - 2)[None, :]
|
||||||
|
fy = ys[:, None] - y0
|
||||||
|
fx = xs[None, :] - x0
|
||||||
|
return (a[y0, x0] * (1 - fy) * (1 - fx) + a[y0, x0 + 1] * (1 - fy) * fx +
|
||||||
|
a[y0 + 1, x0] * fy * (1 - fx) + a[y0 + 1, x0 + 1] * fy * fx)
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# 主流程
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
def main():
|
||||||
|
os.makedirs(OUT, exist_ok=True)
|
||||||
|
t_all = time.time()
|
||||||
|
solid_c, ux_c, uy_c, F_c, rho_c = run_coarse()
|
||||||
|
|
||||||
|
t0 = time.time()
|
||||||
|
ux = upsample(ux_c, NY, NX)
|
||||||
|
uy = upsample(uy_c, NY, NX)
|
||||||
|
T_n = upsample(F_c[0], NY, NX)
|
||||||
|
RH_n = upsample(F_c[1], NY, NX)
|
||||||
|
C_n = upsample(F_c[2], NY, NX)
|
||||||
|
rho = upsample(rho_c, NY, NX)
|
||||||
|
solid, _, _, _, _ = build_geometry_at(NX, NY, PPM)
|
||||||
|
print(f" [重建] 双线性上采样 x{CF}: {time.time()-t0:.2f} s")
|
||||||
|
|
||||||
|
speed = np.sqrt(ux ** 2 + uy ** 2) * SCALE_U
|
||||||
|
speed[solid] = np.nan
|
||||||
|
T_phys = T_AMB + np.clip(T_n / max(T_n.max(), 1e-9), 0, 1) * (T_SRC - T_AMB)
|
||||||
|
RH_phys = RH_AMB + np.clip(RH_n / max(RH_n.max(), 1e-9), 0, 1) * (RH_SRC - RH_AMB)
|
||||||
|
C_norm = C_n / max(C_n.max(), 1e-9)
|
||||||
|
p_pa = (rho - 1.0) / 3.0 * SCALE_P
|
||||||
|
for _ in range(2):
|
||||||
|
p_s = np.zeros_like(p_pa)
|
||||||
|
for dy in (-1, 0, 1):
|
||||||
|
for dx in (-1, 0, 1):
|
||||||
|
p_s += np.roll(np.roll(p_pa, dy, 0), dx, 1)
|
||||||
|
p_pa = p_s / 9.0
|
||||||
|
for a in (T_phys, RH_phys, C_norm, p_pa):
|
||||||
|
a[solid] = np.nan
|
||||||
|
|
||||||
|
t0 = time.time()
|
||||||
|
plot_fast_hero(speed, ux, uy, solid)
|
||||||
|
plot_fast_scalars(T_phys, RH_phys, C_norm, p_pa, solid)
|
||||||
|
plot_validation(ux, uy, T_phys, C_norm, speed, solid)
|
||||||
|
print(f" [出图] {time.time()-t0:.2f} s")
|
||||||
|
print(f"[快速代理] 端到端 {time.time()-t_all:.2f} s (精细真值 76 s)")
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# 绘图
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
def plot_fast_hero(speed, ux, uy, solid):
|
||||||
|
fig, ax = plt.subplots(figsize=(15.5, 9.2))
|
||||||
|
im = ax.imshow(speed, origin="lower", extent=[0, W_M, 0, H_M],
|
||||||
|
cmap=CMAP_FLOW, vmin=0, vmax=0.75, interpolation="bilinear")
|
||||||
|
xs = np.linspace(0.10, W_M - 0.10, 120)
|
||||||
|
ys = np.linspace(0.08, H_M - 0.10, 88)
|
||||||
|
XS, YS = np.meshgrid(xs, ys)
|
||||||
|
ix = np.clip((XS * PPM).astype(int), 0, NX - 1)
|
||||||
|
iy = np.clip((YS * PPM).astype(int), 0, NY - 1)
|
||||||
|
UU = np.where(solid[iy, ix], np.nan, ux[iy, ix] * SCALE_U)
|
||||||
|
VV = np.where(solid[iy, ix], np.nan, uy[iy, ix] * SCALE_U)
|
||||||
|
with np.errstate(invalid="ignore"):
|
||||||
|
ax.streamplot(xs, ys, UU, VV, density=1.2, color=(0.79, 0.82, 0.85, 0.5),
|
||||||
|
linewidth=0.5, arrowsize=0.7, zorder=5)
|
||||||
|
draw_room(ax, solid)
|
||||||
|
draw_sources(ax)
|
||||||
|
decorate_hero(ax)
|
||||||
|
cb = fig.colorbar(im, ax=ax, fraction=0.032, pad=0.015)
|
||||||
|
cb.set_label("速度大小 |U| (m/s)", fontsize=11)
|
||||||
|
cb.outline.set_edgecolor("#30363d")
|
||||||
|
ax.set_xlim(-0.01, W_M + 0.01)
|
||||||
|
ax.set_ylim(-0.16, H_M + 0.01)
|
||||||
|
ax.set_xlabel("x (m)", fontsize=11)
|
||||||
|
ax.set_ylabel("y (m)", fontsize=11)
|
||||||
|
ax.set_title("FFU 单向流洁净区流场 —— 快速代理(粗网格求解 + 全分辨率重建)",
|
||||||
|
fontsize=15, pad=12)
|
||||||
|
fig.text(0.5, 0.008, "端到端 < 8 s · 与高保真 LBM 同源求解器 · 趋势级精度(速度场 r=0.95)",
|
||||||
|
ha="center", fontsize=10.5, color=MUTED)
|
||||||
|
fig.tight_layout(rect=[0, 0.028, 1, 1])
|
||||||
|
safe_save(fig, "ffu-flow-field-fast.png", dpi=135)
|
||||||
|
plt.close(fig)
|
||||||
|
print(" -> ffu-flow-field-fast.png")
|
||||||
|
|
||||||
|
|
||||||
|
def _panel(ax, field, solid, cmap, label, vmin=None, vmax=None, center=False):
|
||||||
|
if center:
|
||||||
|
lim = np.nanmax(np.abs(field)) * 1.05
|
||||||
|
im = ax.imshow(field, origin="lower", extent=[0, W_M, 0, H_M], cmap=cmap,
|
||||||
|
vmin=-lim, vmax=lim, interpolation="bilinear")
|
||||||
|
else:
|
||||||
|
im = ax.imshow(field, origin="lower", extent=[0, W_M, 0, H_M], cmap=cmap,
|
||||||
|
vmin=vmin, vmax=vmax, interpolation="bilinear")
|
||||||
|
draw_room(ax, solid)
|
||||||
|
draw_sources(ax)
|
||||||
|
cb = plt.colorbar(im, ax=ax, fraction=0.036, pad=0.02)
|
||||||
|
cb.set_label(label, fontsize=10)
|
||||||
|
cb.outline.set_edgecolor("#30363d")
|
||||||
|
cb.ax.tick_params(labelsize=9)
|
||||||
|
ax.set_xlim(-0.01, W_M + 0.01)
|
||||||
|
ax.set_ylim(0, H_M)
|
||||||
|
ax.set_xticks([0, 1, 2, 3, 4])
|
||||||
|
ax.set_xlabel("x (m)", fontsize=10)
|
||||||
|
|
||||||
|
|
||||||
|
def plot_fast_scalars(T, RH, C, p, solid):
|
||||||
|
fig, axes = plt.subplots(2, 2, figsize=(15.0, 10.2))
|
||||||
|
_panel(axes[0, 0], T, solid, "inferno", "温度 T (°C)", vmin=T_AMB, vmax=T_SRC)
|
||||||
|
axes[0, 0].set_title("(a) 温度场 —— 设备热羽被单向流下扫", fontsize=12.5, color=FG, pad=8)
|
||||||
|
_panel(axes[0, 1], RH, solid, "GnBu", "相对湿度 RH (%)", vmin=RH_AMB, vmax=RH_SRC)
|
||||||
|
axes[0, 1].set_title("(b) 湿度场 —— 呼吸区湿气随气流下送", fontsize=12.5, color=FG, pad=8)
|
||||||
|
_panel(axes[1, 0], C, solid, "magma", "归一化粒子浓度 C/Cmax", vmin=0, vmax=1.0)
|
||||||
|
axes[1, 0].set_title("(c) 尘埃粒子浓度 —— 尘源散发与输运", fontsize=12.5, color=FG, pad=8)
|
||||||
|
_panel(axes[1, 1], p, solid, "RdBu_r", "静压偏差 Δp (Pa)", center=True)
|
||||||
|
axes[1, 1].set_title("(d) 压力场 —— 送风正压 / 回风负压", fontsize=12.5, color=FG, pad=8)
|
||||||
|
fig.suptitle("FFU 对下方温湿度、压力与尘埃粒子的影响 —— 快速代理(< 8 s)",
|
||||||
|
fontsize=15, y=0.985)
|
||||||
|
fig.text(0.5, 0.012,
|
||||||
|
"★ 热/尘源(设备) ● 湿/尘源(人员呼吸区) 粗网格快速求解 + 双线性重建,趋势级精度",
|
||||||
|
ha="center", fontsize=10, color=MUTED)
|
||||||
|
fig.tight_layout(rect=[0, 0.03, 1, 0.965])
|
||||||
|
safe_save(fig, "ffu-scalar-fields-fast.png", dpi=130)
|
||||||
|
plt.close(fig)
|
||||||
|
print(" -> ffu-scalar-fields-fast.png")
|
||||||
|
|
||||||
|
|
||||||
|
def plot_validation(ux_s, uy_s, T_s, C_s, speed_s, solid):
|
||||||
|
npz = os.path.join(OUT, "ffu_fields.npz")
|
||||||
|
if not os.path.exists(npz):
|
||||||
|
print(" [skip] ffu_fields.npz 不存在, 跳过验证图")
|
||||||
|
return
|
||||||
|
d = np.load(npz)
|
||||||
|
speed_l = np.sqrt(d["ux"] ** 2 + d["uy"] ** 2) * SCALE_U
|
||||||
|
T_l = T_AMB + d["T"] * (T_SRC - T_AMB)
|
||||||
|
C_l = d["C"].copy()
|
||||||
|
|
||||||
|
def mask(a):
|
||||||
|
b = a.copy(); b[solid] = np.nan; return b
|
||||||
|
|
||||||
|
vmax_c = float(np.nanpercentile(C_l, 99.5))
|
||||||
|
C_s2 = C_s * vmax_c
|
||||||
|
|
||||||
|
fig, axes = plt.subplots(3, 2, figsize=(14.0, 13.0))
|
||||||
|
rows = [
|
||||||
|
("速度 |U| (m/s)", mask(speed_l), mask(speed_s), CMAP_FLOW, 0, 0.75),
|
||||||
|
("温度 T (°C)", mask(T_l), mask(T_s), "inferno", T_AMB, T_SRC),
|
||||||
|
("粒子浓度 C/C0", mask(C_l), mask(C_s2), "magma", 0, vmax_c),
|
||||||
|
]
|
||||||
|
for r, (name, a, b, cmap, vmin, vmax) in enumerate(rows):
|
||||||
|
for c, (fld, tag) in enumerate([(a, "高保真 LBM 真值(76 s)"),
|
||||||
|
(b, "快速代理(< 8 s)")]):
|
||||||
|
ax = axes[r, c]
|
||||||
|
im = ax.imshow(fld, origin="lower", extent=[0, W_M, 0, H_M],
|
||||||
|
cmap=cmap, vmin=vmin, vmax=vmax, interpolation="bilinear")
|
||||||
|
draw_room(ax, solid)
|
||||||
|
draw_sources(ax)
|
||||||
|
cb = plt.colorbar(im, ax=ax, fraction=0.036, pad=0.02)
|
||||||
|
cb.set_label(name, fontsize=9.5)
|
||||||
|
cb.outline.set_edgecolor("#30363d")
|
||||||
|
cb.ax.tick_params(labelsize=8.5)
|
||||||
|
ax.set_xticks([0, 1, 2, 3, 4])
|
||||||
|
ax.set_xlim(-0.01, W_M + 0.01)
|
||||||
|
ax.set_ylim(0, H_M)
|
||||||
|
if r == 0:
|
||||||
|
ax.set_title(tag, fontsize=13, color=ACCENT if c else FG, pad=8)
|
||||||
|
if c == 0:
|
||||||
|
ax.set_ylabel(name.split(" (")[0], fontsize=11)
|
||||||
|
|
||||||
|
m = ~solid
|
||||||
|
corr_u = np.corrcoef(speed_l[m], speed_s[m])[0, 1]
|
||||||
|
corr_t = np.corrcoef(T_l[m], T_s[m])[0, 1]
|
||||||
|
corr_c = np.corrcoef(C_l[m], C_s2[m])[0, 1]
|
||||||
|
fig.suptitle("趋势验证:高保真 LBM vs 快速代理(粗网格求解 + 重建)",
|
||||||
|
fontsize=16, y=0.985)
|
||||||
|
fig.text(0.5, 0.006,
|
||||||
|
f"全场相关系数:速度 r={corr_u:.2f} · 温度 r={corr_t:.2f} · 粒子 r={corr_c:.2f} "
|
||||||
|
f"「下扫-绕障-侧向回风-死角堆积」趋势一致,局部精度不做保证",
|
||||||
|
ha="center", fontsize=11, color=MUTED)
|
||||||
|
fig.tight_layout(rect=[0, 0.025, 1, 0.97])
|
||||||
|
safe_save(fig, "ffu-validation.png", dpi=120)
|
||||||
|
plt.close(fig)
|
||||||
|
print(f" -> ffu-validation.png (speed r={corr_u:.3f}, T r={corr_t:.3f}, C r={corr_c:.3f})")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,256 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
规划图生成器: 系统架构图 / 数据流水线图 / 里程碑路线图
|
||||||
|
输出 docs/assets/*.svg (矢量源文件), 再由 tools/svg2png.js 转 PNG。
|
||||||
|
|
||||||
|
运行:
|
||||||
|
python tools/gen_diagrams.py
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
|
||||||
|
OUT = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||||||
|
"docs", "assets")
|
||||||
|
os.makedirs(OUT, exist_ok=True)
|
||||||
|
|
||||||
|
BG = "#0d1117"
|
||||||
|
PANEL = "#161b22"
|
||||||
|
PANEL2 = "#0d1117"
|
||||||
|
STROKE = "#30363d"
|
||||||
|
FG = "#e6edf3"
|
||||||
|
MUTED = "#8b949e"
|
||||||
|
FONT = "Microsoft YaHei, Segoe UI, sans-serif"
|
||||||
|
|
||||||
|
C_PINK = "#f778ba"
|
||||||
|
C_PURPLE = "#bc8cff"
|
||||||
|
C_YELLOW = "#d29922"
|
||||||
|
C_BLUE = "#58a6ff"
|
||||||
|
C_GREEN = "#3fb950"
|
||||||
|
C_RED = "#ff7b72"
|
||||||
|
C_CYAN = "#39d0d8"
|
||||||
|
|
||||||
|
|
||||||
|
def esc(s):
|
||||||
|
return s.replace("&", "&").replace("<", "<").replace(">", ">")
|
||||||
|
|
||||||
|
|
||||||
|
def text(x, y, s, size=14, fill=FG, weight="normal", anchor="start", op=1.0):
|
||||||
|
return (f'<text x="{x}" y="{y}" font-family="{FONT}" font-size="{size}" '
|
||||||
|
f'fill="{fill}" font-weight="{weight}" text-anchor="{anchor}" '
|
||||||
|
f'opacity="{op}">{esc(s)}</text>\n')
|
||||||
|
|
||||||
|
|
||||||
|
def box(x, y, w, h, fill=PANEL, stroke=STROKE, rx=10, sw=1.2, dash=None, op=1.0):
|
||||||
|
d = f' stroke-dasharray="{dash}"' if dash else ""
|
||||||
|
return (f'<rect x="{x}" y="{y}" width="{w}" height="{h}" rx="{rx}" '
|
||||||
|
f'fill="{fill}" stroke="{stroke}" stroke-width="{sw}"{d} opacity="{op}"/>\n')
|
||||||
|
|
||||||
|
|
||||||
|
def arrow(x1, y1, x2, y2, color=C_BLUE, sw=2.0, dash=None, marker="arr"):
|
||||||
|
d = f' stroke-dasharray="{dash}"' if dash else ""
|
||||||
|
return (f'<line x1="{x1}" y1="{y1}" x2="{x2}" y2="{y2}" stroke="{color}" '
|
||||||
|
f'stroke-width="{sw}"{d} marker-end="url(#{marker})"/>\n')
|
||||||
|
|
||||||
|
|
||||||
|
def svg_open(w, h, extra_defs=""):
|
||||||
|
return (f'<svg xmlns="http://www.w3.org/2000/svg" width="{w}" height="{h}" '
|
||||||
|
f'viewBox="0 0 {w} {h}">\n'
|
||||||
|
f'<defs>\n'
|
||||||
|
f'<marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" '
|
||||||
|
f'markerHeight="7" orient="auto-start-reverse">'
|
||||||
|
f'<path d="M 0 0 L 10 5 L 0 10 z" fill="{C_BLUE}"/></marker>\n'
|
||||||
|
f'<marker id="arrG" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" '
|
||||||
|
f'markerHeight="7" orient="auto-start-reverse">'
|
||||||
|
f'<path d="M 0 0 L 10 5 L 0 10 z" fill="{C_GREEN}"/></marker>\n'
|
||||||
|
f'<marker id="arrY" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" '
|
||||||
|
f'markerHeight="7" orient="auto-start-reverse">'
|
||||||
|
f'<path d="M 0 0 L 10 5 L 0 10 z" fill="{C_YELLOW}"/></marker>\n'
|
||||||
|
f'<marker id="arrM" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" '
|
||||||
|
f'markerHeight="7" orient="auto-start-reverse">'
|
||||||
|
f'<path d="M 0 0 L 10 5 L 0 10 z" fill="{MUTED}"/></marker>\n'
|
||||||
|
f'{extra_defs}</defs>\n'
|
||||||
|
f'<rect width="{w}" height="{h}" fill="{BG}"/>\n')
|
||||||
|
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# 1. 系统架构图
|
||||||
|
# ==========================================================================
|
||||||
|
def architecture():
|
||||||
|
W, H = 1600, 1150
|
||||||
|
s = svg_open(W, H)
|
||||||
|
s += text(50, 56, "PhysicsAiFluentAnalysis · 系统架构", 28, FG, "bold")
|
||||||
|
s += text(50, 88, "六层架构:从高保真真值到实时交互应用,物理约束贯穿模型层", 14.5, MUTED)
|
||||||
|
|
||||||
|
layers = [
|
||||||
|
(C_PINK, "L5 应用层", "Application",
|
||||||
|
[("数字孪生", "传感器同化 · 在线重建"), ("工程设计优化", "实时参数扫描 · 设计空间探索"),
|
||||||
|
("教学与科研", "流场可视化实验"), ("实时决策", "风场 / 流场秒级预测")]),
|
||||||
|
(C_PURPLE, "L4 交互与渲染", "Interaction & Rendering",
|
||||||
|
[("WebGPU 前端", "three.js / VTK.js"), ("参数实时调节", "拖拽 Re / 攻角 / 几何"),
|
||||||
|
("场可视化", "体渲染 · 流线 · 矢量场"), ("场景管理", "剖切面 · 时间轴 · 导出")]),
|
||||||
|
(C_YELLOW, "L3 推理服务层", "Inference Service",
|
||||||
|
[("推理引擎", "TensorRT / ONNX Runtime"), ("时间推进", "自回归 + 潜空间动力学"),
|
||||||
|
("后处理校正", "散度修正 · 守恒律"), ("服务接口", "gRPC + WebSocket 流式")]),
|
||||||
|
(C_BLUE, "L2 AI 模型层", "AI Models",
|
||||||
|
[("FNO 傅里叶神经算子", "主力基线 · 分辨率无关"), ("DeepONet", "算子学习备选路线"),
|
||||||
|
("PINN 物理约束", "NS 残差 · 少数据工况"), ("MeshGraphNet", "非结构网格 · 复杂几何")]),
|
||||||
|
(C_GREEN, "L1 数据与训练", "Data & Training",
|
||||||
|
[("PyTorch 分布式训练", "AMP · DDP / FSDP"), ("主动学习", "不确定性驱动采样"),
|
||||||
|
("数据湖", "HDF5 / VTK / Zarr"), ("数据工程", "归一化 · 增广 · 插值统一")]),
|
||||||
|
(C_RED, "L0 高保真真值", "Ground Truth CFD",
|
||||||
|
[("Ansys Fluent", "RANS / URANS / LES"), ("PyFluent 自动化", "参数化建模与求解"),
|
||||||
|
("参数化几何", "翼型族 · 车体 · 管道"), ("边界条件库", "工况模板 · 湍流模型")]),
|
||||||
|
]
|
||||||
|
|
||||||
|
y0, bh, gap = 118, 148, 20
|
||||||
|
pitch = bh + gap
|
||||||
|
bx, bw = 230, 317
|
||||||
|
for i, (color, name, en, mods) in enumerate(layers):
|
||||||
|
y = y0 + i * pitch
|
||||||
|
s += box(40, y, 1520, bh, PANEL, STROKE, 12)
|
||||||
|
s += f'<rect x="40" y="{y}" width="6" height="{bh}" rx="3" fill="{color}"/>\n'
|
||||||
|
s += text(64, y + 66, name, 17.5, color, "bold")
|
||||||
|
s += text(64, y + 92, en, 11, MUTED)
|
||||||
|
for j, (t, sub) in enumerate(mods):
|
||||||
|
x = bx + j * (bw + 14)
|
||||||
|
s += box(x, y + 22, bw, 104, PANEL2, STROKE, 10)
|
||||||
|
s += f'<rect x="{x}" y="{y+22}" width="{bw}" height="4" rx="2" fill="{color}" opacity="0.75"/>\n'
|
||||||
|
s += text(x + 16, y + 62, t, 15, FG, "bold")
|
||||||
|
s += text(x + 16, y + 90, sub, 11.5, MUTED)
|
||||||
|
if i < len(layers) - 1:
|
||||||
|
ym = y + bh + gap / 2
|
||||||
|
s += arrow(880, ym + 7, 880, ym - 7, C_BLUE, 1.8)
|
||||||
|
s += arrow(760, ym - 7, 760, ym + 7, C_GREEN, 1.8, marker="arrG")
|
||||||
|
|
||||||
|
s += text(40, 1128, "数据流 ↑(真值 → 训练 → 推理 → 应用) "
|
||||||
|
"控制流 ↓(应用需求 → 模型选型 → 数据采集) "
|
||||||
|
"物理约束:连续性 / 动量守恒 · 不确定性量化 · 守恒校正",
|
||||||
|
12.5, MUTED)
|
||||||
|
s += "</svg>\n"
|
||||||
|
open(os.path.join(OUT, "architecture.svg"), "w", encoding="utf-8").write(s)
|
||||||
|
print(" -> architecture.svg")
|
||||||
|
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# 2. 数据与训练流水线
|
||||||
|
# ==========================================================================
|
||||||
|
def pipeline():
|
||||||
|
W, H = 1600, 760
|
||||||
|
s = svg_open(W, H)
|
||||||
|
s += text(50, 56, "数据与训练流水线", 28, FG, "bold")
|
||||||
|
s += text(50, 88, "从参数采样到实时仿真服务:全链路自动化 + 主动学习数据飞轮", 14.5, MUTED)
|
||||||
|
|
||||||
|
stages = [
|
||||||
|
(C_CYAN, "① 参数空间采样", ["拉丁超立方采样", "Re / 攻角 / 几何参数"]),
|
||||||
|
(C_RED, "② Fluent 批量仿真", ["PyFluent 无人值守", "稳态 + 瞬态工况"]),
|
||||||
|
(C_GREEN, "③ 数据工程", ["网格统一 · 归一化", "质检 / 增广 / 划分"]),
|
||||||
|
(C_BLUE, "④ 神经算子训练", ["FNO / DeepONet / GNN", "物理约束损失"]),
|
||||||
|
(C_PURPLE, "⑤ 推理引擎部署", ["ONNX → TensorRT", "FP16 / INT8 量化"]),
|
||||||
|
(C_PINK, "⑥ 实时仿真服务", ["gRPC + WebSocket", "流场流式推送"]),
|
||||||
|
]
|
||||||
|
arts = ["工况表 + 元数据", "CAS / DAT / VTK", "HDF5 数据集", "模型权重 .pt", "TensorRT .engine", "实时流场帧"]
|
||||||
|
|
||||||
|
bw, gap = 224, 24
|
||||||
|
x0, y = 68, 150
|
||||||
|
bh = 150
|
||||||
|
for i, (color, title, lines) in enumerate(stages):
|
||||||
|
x = x0 + i * (bw + gap)
|
||||||
|
s += box(x, y, bw, bh, PANEL, color, 12, sw=1.6)
|
||||||
|
s += f'<rect x="{x}" y="{y}" width="{bw}" height="5" rx="2.5" fill="{color}"/>\n'
|
||||||
|
s += text(x + 16, y + 44, title, 16, color, "bold")
|
||||||
|
for k, ln in enumerate(lines):
|
||||||
|
s += text(x + 16, y + 76 + k * 24, ln, 12, MUTED)
|
||||||
|
if i < len(stages) - 1:
|
||||||
|
s += arrow(x + bw + 2, y + bh / 2, x + bw + gap - 3, y + bh / 2, C_BLUE, 2.0)
|
||||||
|
s += box(x + 8, y + bh + 16, bw - 16, 42, PANEL2, STROKE, 8, dash="4 3")
|
||||||
|
s += text(x + bw / 2, y + bh + 43, arts[i], 11.5, MUTED, anchor="middle")
|
||||||
|
|
||||||
|
ly = y + bh + 96
|
||||||
|
s += arrow(x0 + 5 * (bw + gap) + bw / 2, ly - 12, x0 + bw / 2, ly - 12,
|
||||||
|
C_YELLOW, 1.8, dash="7 5", marker="arrY")
|
||||||
|
s += text(W / 2, ly + 16, "主动学习闭环:不确定性评估 → 高价值工况回灌仿真 → 再训练(数据飞轮)",
|
||||||
|
13, C_YELLOW, anchor="middle")
|
||||||
|
|
||||||
|
gy = 600
|
||||||
|
gates = [("精度门禁", "相对 L2 < 5%"), ("稳定性门禁", "1000 步不发散"),
|
||||||
|
("成本门禁", "单工况预算达标"), ("可复现门禁", "流水线一键复现")]
|
||||||
|
gw = 340
|
||||||
|
for i, (t, sub) in enumerate(gates):
|
||||||
|
x = 68 + i * (gw + 20)
|
||||||
|
s += box(x, gy, gw, 66, PANEL, STROKE, 10)
|
||||||
|
s += f'<circle cx="{x+26}" cy="{gy+33}" r="7" fill="{C_GREEN}" opacity="0.85"/>\n'
|
||||||
|
s += text(x + 46, gy + 30, t, 14.5, FG, "bold")
|
||||||
|
s += text(x + 46, gy + 52, sub, 11.5, MUTED)
|
||||||
|
s += text(50, 726, "每个阶段设置 Go / No-Go 决策点:未通过门禁不允许进入下一阶段",
|
||||||
|
12.5, MUTED)
|
||||||
|
s += "</svg>\n"
|
||||||
|
open(os.path.join(OUT, "pipeline.svg"), "w", encoding="utf-8").write(s)
|
||||||
|
print(" -> pipeline.svg")
|
||||||
|
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# 3. 里程碑路线图
|
||||||
|
# ==========================================================================
|
||||||
|
def roadmap():
|
||||||
|
W, H = 1600, 940
|
||||||
|
s = svg_open(W, H)
|
||||||
|
s += text(50, 56, "里程碑路线图(12 个月 · 7 个里程碑)", 28, FG, "bold")
|
||||||
|
s += text(50, 88, "先窄后宽:2D 基准打通全链路 → 3D 复杂几何 → 实时系统 → 数字孪生", 14.5, MUTED)
|
||||||
|
|
||||||
|
x0, x1 = 300, 1560
|
||||||
|
mw = (x1 - x0) / 12
|
||||||
|
ty = 140
|
||||||
|
for m in range(13):
|
||||||
|
x = x0 + m * mw
|
||||||
|
s += f'<line x1="{x}" y1="{ty-24}" x2="{x}" y2="840" stroke="#21262d" stroke-width="1"/>\n'
|
||||||
|
for m in range(12):
|
||||||
|
s += text(x0 + (m + 0.5) * mw, ty - 6, f"{m+1}", 12.5, MUTED, anchor="middle")
|
||||||
|
s += text(x0 - 16, ty - 6, "月份", 12.5, MUTED, anchor="end")
|
||||||
|
|
||||||
|
miles = [
|
||||||
|
(C_RED, "M0 立项与调研", 1, 1, "规划文档 · 选型评审 · 基准定义"),
|
||||||
|
(C_CYAN, "M1 数据工厂", 2, 3, "PyFluent 自动化 · ≥500 工况数据集"),
|
||||||
|
(C_BLUE, "M2 基线模型", 4, 6, "FNO 基线 · 相对 L2 < 5% · 1000 步稳定"),
|
||||||
|
(C_PURPLE, "M3 数据飞轮", 6, 7, "主动学习 · 同等精度省 40% 仿真量"),
|
||||||
|
(C_GREEN, "M4 3D 与复杂几何", 7, 9, "翼型 / 车体代理 · MeshGraphNet"),
|
||||||
|
(C_YELLOW, "M5 实时推理系统", 9, 10, "TensorRT · 端到端 < 100 ms"),
|
||||||
|
(C_PINK, "M6 可视化与数字孪生", 10, 12, "WebGPU 交互 · 稀疏观测同化"),
|
||||||
|
]
|
||||||
|
ry, rh, pitch = 170, 58, 96
|
||||||
|
for i, (color, name, m0, m1, deliv) in enumerate(miles):
|
||||||
|
y = ry + i * pitch
|
||||||
|
s += text(40, y + 30, name, 16, FG, "bold")
|
||||||
|
bx = x0 + (m0 - 1) * mw + 3
|
||||||
|
bwid = (m1 - m0 + 1) * mw - 6
|
||||||
|
s += box(bx, y, bwid, rh, color, color, 10, sw=0, op=0.28)
|
||||||
|
s += f'<rect x="{bx}" y="{y}" width="{bwid}" height="{rh}" rx="10" fill="none" stroke="{color}" stroke-width="1.6"/>\n'
|
||||||
|
span = f"第 {m0} 月" if m0 == m1 else f"{m0}-{m1} 月"
|
||||||
|
s += text(bx + 16, y + 35, span, 13, color, "bold")
|
||||||
|
est_w = sum(12.5 if ord(c) > 127 else 7 for c in deliv)
|
||||||
|
if bx + bwid + 40 + est_w > 1580: # 右侧放不下 -> 放左边
|
||||||
|
dx = bx - 14
|
||||||
|
s += text(dx - 16, y + rh / 2 + 5, deliv, 12.5, MUTED, anchor="end")
|
||||||
|
s += f'<path d="M {dx} {y+rh/2-7} L {dx-7} {y+rh/2} L {dx} {y+rh/2+7} L {dx+7} {y+rh/2} z" fill="{color}"/>\n'
|
||||||
|
else:
|
||||||
|
dx = bx + bwid + 14
|
||||||
|
s += f'<path d="M {dx} {y+rh/2-7} L {dx+7} {y+rh/2} L {dx} {y+rh/2+7} L {dx-7} {y+rh/2} z" fill="{color}"/>\n'
|
||||||
|
s += text(dx + 18, y + rh / 2 + 5, deliv, 12.5, MUTED)
|
||||||
|
|
||||||
|
s += box(40, 856, 1520, 60, PANEL, STROKE, 10)
|
||||||
|
s += text(60, 884, "决策点", 14, C_YELLOW, "bold")
|
||||||
|
s += text(130, 884, "M2 精度不达标 → DeepONet / MeshGraphNet 双路线并行 "
|
||||||
|
"M4 3D 不达标 → 退守「2D + 切片 3D」保证 M5/M6 交付 "
|
||||||
|
"每里程碑预留 20% 时间缓冲", 12.5, MUTED)
|
||||||
|
s += text(60, 906, "成功标准", 14, C_GREEN, "bold")
|
||||||
|
s += text(130, 906, "P1(3 个月)可行性验证 · P2(6 个月)系统闭环 · P3(12 个月)数字孪生原型",
|
||||||
|
12.5, MUTED)
|
||||||
|
s += "</svg>\n"
|
||||||
|
open(os.path.join(OUT, "roadmap.svg"), "w", encoding="utf-8").write(s)
|
||||||
|
print(" -> roadmap.svg")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
architecture()
|
||||||
|
pipeline()
|
||||||
|
roadmap()
|
||||||
|
print("done:", OUT)
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* SVG -> PNG 批量转换 (resvg, 支持中文系统字体)
|
||||||
|
*
|
||||||
|
* npm install # 首次安装依赖
|
||||||
|
* node tools/svg2png.js # 转换 docs/assets 下全部 svg
|
||||||
|
* node tools/svg2png.js foo.svg # 只转换指定文件
|
||||||
|
*/
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
let Resvg;
|
||||||
|
try {
|
||||||
|
({ Resvg } = require("@resvg/resvg-js"));
|
||||||
|
} catch (e) {
|
||||||
|
console.error("缺少依赖 @resvg/resvg-js,请先执行: npm install");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const dir = path.join(__dirname, "..", "docs", "assets");
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
const files = args.length
|
||||||
|
? args
|
||||||
|
: fs.readdirSync(dir).filter((f) => f.toLowerCase().endsWith(".svg"));
|
||||||
|
|
||||||
|
let ok = 0;
|
||||||
|
for (const f of files) {
|
||||||
|
const src = path.join(dir, f);
|
||||||
|
const dst = src.replace(/\.svg$/i, ".png");
|
||||||
|
const svg = fs.readFileSync(src, "utf8");
|
||||||
|
const r = new Resvg(svg, {
|
||||||
|
fitTo: { mode: "zoom", value: 2 },
|
||||||
|
font: { loadSystemFonts: true },
|
||||||
|
background: "#0d1117",
|
||||||
|
});
|
||||||
|
fs.writeFileSync(dst, r.render().asPng());
|
||||||
|
console.log(` ${f} -> ${path.basename(dst)}`);
|
||||||
|
ok++;
|
||||||
|
}
|
||||||
|
console.log(`converted ${ok} file(s)`);
|
||||||