XIAN-FEM-2026June/OpticsFEM_开发与使用手册.md

606 lines
22 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# OpticsFEM 二维光学有限元程序 — 开发与使用手册
| 项目 | 说明 |
|------|------|
| 文档版本 | v1.0 |
| 编制日期 | 2026-06-03 |
| 适用代码 | `opticsfem-master/` |
| 适用平台 | Windows x64Visual Studio 202618/ MSVC |
| 关联文档 | `OpticsFEM_LASPCEM_接口规范.md` |
---
## 目录
1. [概述](#1-概述)
2. [从源代码到可执行文件](#2-从源代码到可执行文件)
3. [软件架构与模块划分](#3-软件架构与模块划分)
4. [程序执行流程](#4-程序执行流程)
5. [数据流与对象模型](#5-数据流与对象模型)
6. [对外 API 规范](#6-对外-api-规范)
7. [输入与输出文件规范](#7-输入与输出文件规范)
8. [构建与部署](#8-构建与部署)
9. [算例运行指南](#9-算例运行指南)
10. [代码阅读路径](#10-代码阅读路径)
11. [求解器依赖说明](#11-求解器依赖说明)
12. [常见问题与排查](#12-常见问题与排查)
---
## 1. 概述
### 1.1 程序定位
OpticsFEM 是一套基于 C++ 实现的**二维矢量有限元FEM光学电磁仿真内核**,采用 Nedelec 边元离散麦克斯韦方程,支持以下三类问题:
| FemType | 问题类型 | 核心类 |
|---------|----------|--------|
| 0 | 本征模式EigenMode | `OpticsFEM_2D_EigenMode` |
| 1 | 本征频率EigenFreq | `OpticsFEM_2D_EigenFreq` |
| 2 | 散射Scatter按波长 | `OpticsFEM_2D_Scatter` |
| 3 | 散射Scatter按频率 | `OpticsFEM_2D_Scatter` |
程序通过 **JSON 字符串** 描述物理模型、材料参数、边界条件及求解配置;通过 **`.dat` 文本文件** 描述三角网格。计算结果以文本文件形式输出至指定目录。
### 1.2 入口程序与可执行文件的关系
`test/Test_Main.cpp` 并非完整程序,而是**可执行文件的入口模块**。其职责限于:
1. 从本地文件系统读取 JSON 配置文件;
2. 构造 `OpticsFEMData` 结构体;
3. 调用统一 API `OpticsFEM_API::OpticsFEM_All()` 触发完整求解流程。
可执行文件 `OpticsFEM.exe` 由 CMake 将 `Test_Main.cpp` 与项目中全部业务源文件(约 70 余个 `.cpp` 单元)**编译并链接**为单一二进制产物。操作系统启动进程时,自 `main()` 函数开始执行;后续全部计算逻辑由 `FEM_Interface.cpp` 及其调用的各功能模块完成。
### 1.3 设计原则
| 原则 | 说明 |
|------|------|
| 单一 API 入口 | 外部调用方仅需调用 `OpticsFEM_All()`,无需感知内部分支 |
| JSON 为配置语言 | 物理、材料、边界、求解参数均通过 JSON 字段传递 |
| 前后端解耦 | `Test_Main.cpp` 可替换为 GUI、脚本或 DLL 宿主,接口不变 |
| 模块化组装 | 网格、物理、材料、组装、求解、后处理分层实现 |
---
## 2. 从源代码到可执行文件
### 2.1 构建系统
项目采用 **CMake** 管理构建,配置文件为 `opticsfem-master/CMakeLists.txt`。目标产物定义如下:
```cmake
add_executable(OpticsFEM
"test/Test_Main.cpp"
${PARSER} ${COMMON} ${GAUSS} ${BF}
${MESH} ${MATERIAL} ${PHY}
${SOLVER} ${POST} ${KERNEL}
${TEST} ${INTERFACE}
"post/Post_GetEb.cpp"
)
target_compile_definitions(OpticsFEM PRIVATE EIGENSOLVER_STATIC)
set(CMAKE_CXX_STANDARD 17)
```
### 2.2 编译链接流程
```
┌──────────────────────────────────────────────────────────────────┐
│ 源文件层 │
│ Test_Main.cpp, FEM_Interface.cpp, Mesh_Interface.cpp, ... │
└────────────────────────────┬─────────────────────────────────────┘
│ cmake .. -G "Visual Studio 18 2026" -A x64
┌──────────────────────────────────────────────────────────────────┐
│ 工程生成层 │
│ build/OpticsFEM.vcxproj, build/Project.slnx │
└────────────────────────────┬─────────────────────────────────────┘
│ cmake --build . --config Release
┌──────────────────────────────────────────────────────────────────┐
│ 编译层Translation
│ 每个 .cpp → 对应 .obj 目标文件 │
└────────────────────────────┬─────────────────────────────────────┘
│ 链接Linking
┌──────────────────────────────────────────────────────────────────┐
│ 产物层 │
│ build/Release/OpticsFEM.exe │
└──────────────────────────────────────────────────────────────────┘
```
**编译Compile**:将各翻译单元(`.cpp`)独立转换为目标文件(`.obj`)。`#include` 头文件仅提供声明,不参与链接。
**链接Link**:合并全部 `.obj`,解析跨文件符号引用,以 `Test_Main.cpp` 中的 `main()` 作为进程入口,生成最终 `.exe`
### 2.3 第三方依赖
| 依赖 | 位置 | 用途 |
|------|------|------|
| Eigen | `opticsfem-master/Eigen/` | 稠密/稀疏矩阵运算 |
| nlohmann/json | `opticsfem-master/nlohmann/` | JSON 解析 |
| muparser 派生库 | `opticsfem-master/parser/` | BELE 等边界表达式求值 |
### 2.4 关于 Test_Main.cpp 中未使用的变量
`Test_Main.cpp` 中声明的 `Phy_WaveOpticsModel`、`MaterialLib`、`Mesh_2D` 等对象在 `main()` 内**未被直接使用**。实际对象实例化与数据注入在 `FEM_Interface.cpp::OpticsFEM_All()` 中完成。上述声明为历史遗留或调试用途;注释块(第 3958 行)保留了手动分步调用流程的旧版写法,现行标准路径为 `OpticsFEM_All()` 一站式调用。
---
## 3. 软件架构与模块划分
### 3.1 目录结构与职责
```
opticsfem-master/
├── test/
│ ├── Test_Main.cpp # 进程入口;读取 JSON 文件
│ ├── Test_ReadData.cpp # JSON → Phy / Material 解析实现
│ └── Test_OutputData.cpp # 调试输出
├── Interface/
│ ├── FEM_Interface.h # 对外 API 声明
│ └── FEM_Interface.cpp # FemType 路由与全流程调度
├── phy/ # 物理模型:边界条件、激励
├── material/ # 材料库ε、μ、σ、n、k
├── mesh/ # 网格:.dat 读取与拓扑查询
├── kernel/ # FEM 组装:方程与边界积分
├── solver/ # 数值求解:线性方程组 / 特征值
├── post/ # 后处理DOF → 顶点场 → 文件输出
├── function/ # Nedelec 基函数、高斯积分
├── parser/ # 数学表达式解析器
└── Eigen/ # 线性代数库(第三方)
```
### 3.2 模块依赖关系
```mermaid
flowchart TB
subgraph entry [入口层]
TM[Test_Main.cpp]
API[FEM_Interface.cpp]
end
subgraph io [数据读入层]
PHY[phy]
MAT[material]
MSH[mesh]
end
subgraph core [计算核心层]
KRN[kernel]
SLV[solver]
end
subgraph output [输出层]
PST[post]
end
TM --> API
API --> PHY
API --> MAT
API --> MSH
API --> KRN
KRN --> SLV
KRN --> PST
SLV --> PST
PHY --> KRN
MAT --> KRN
MSH --> KRN
MSH --> PST
PHY --> PST
```
### 3.3 核心类层次(二维)
| 类名 | 职责 |
|------|------|
| `Phy_WaveOpticsModel` | 存储边界类型、BELE/PML/EF/SBC 等边界数据 |
| `MaterialLib` | 存储各物理域材料参数 |
| `Mesh_2D` | 存储顶点、边、三角形及域编号 |
| `OpticsFEM_2D_Scatter` | 散射问题组装、求解、后处理调度 |
| `Solver_LdaDom` | 散射线性方程组求解接口 |
| `Post_2D_Scatter` | 散射结果电场插值与输出 |
---
## 4. 程序执行流程
### 4.1 顶层调用序列
`FemType = 2`散射bele 算例)为例,完整调用序列为:
```
main()
└─ OpticsFEM_API::OpticsFEM_All(data)
├─ json::parse(data.data)
├─ Phy_WaveOpticsModel::Test_ReadData(str)
├─ MaterialLib::Test_ReadData(str)
├─ Mesh_2D::GetMesh(meshFile, str)
└─ [FemType == 2 分支]
├─ Solver_LdaDom::GetSolver(str)
├─ OpticsFEM_2D_Scatter::GetMaterial / GetMesh / GetPhy / GetSolver / GetPost
├─ OpticsFEM_2D_Scatter::Assemble()
├─ OpticsFEM_2D_Scatter::Run()
└─ OpticsFEM_2D_Scatter::Post(outFile)
```
### 4.2 Assemble 阶段内部流程
`OpticsFEM_2D_Scatter::Assemble()``kernel/Assemble_kernel.cpp`)执行以下步骤:
1. **确定矩阵类型**:根据 PML、SBC、复材料等条件判定采用实数或复数稀疏矩阵
2. **体积积分**`Assemble_WaveEquation()` — 在各三角形单元上积分麦克斯韦弱形式;
3. **边界积分**(按 JSON 配置条件触发):
- `Assemble_PEC_ELE()` — 理想导体 / 电边界
- `Assemble_PBC()` — 周期边界
- `Assemble_BELE()` — 边界等效源bele 算例)
- `Assemble_PML()` — 完美匹配层
- `Assemble_Port()` — 端口激励
-
4. **约束处理**:对 Dirichlet 型边界自由度施加罚函数或消元。
输出:稀疏矩阵 **A**、右端向量 **b**、预处理矩阵 **P**
### 4.3 Run 阶段
```cpp
void OpticsFEM_2D_Scatter::Run()
{
_mSolver->SetParam(&_mA_complex, &_mB_complex, &_mP_complex);
_mSolver->Run(&_mX);
}
```
`Solver_LdaDom::Run()` 调用 `solveComplexLinearEqu()``solver/interface.cpp`),将 CSR 格式矩阵写入临时文件,并启动外部求解器 `complex/complexsolver.exe` 完成迭代求解。解向量经预处理矩阵变换后存入 `_mX`
### 4.4 Post 阶段
```cpp
void OpticsFEM_2D_Scatter::Post(string file)
{
_mPost->GetMesh(_mMesh);
_mPost->GetSolver(_mSolver);
_mPost->GetPhy(_mPhy);
_mPost->GetResult(&_mX);
_mPost->GetElectric(); // 边 DOF → 顶点 Ex/Ey/Ez
_mPost->OutputData(file); // 写入 OutFile/
}
```
---
## 5. 数据流与对象模型
### 5.1 端到端数据流
```mermaid
flowchart LR
subgraph inputs [外部输入]
J["bele.json\n(JSON 字符串)"]
D["project_3.dat\n(网格文件)"]
end
subgraph parsed [内存对象]
P["Phy_WaveOpticsModel\n边界/激励"]
M["MaterialLib\n材料参数"]
G["Mesh_2D\n网格拓扑"]
end
subgraph linear [离散方程]
A["稀疏矩阵 A"]
B["右端向量 b"]
X["解向量 x"]
end
subgraph files [文件输出]
O["OutFile/Ex, Ey, Ez, normE"]
end
J -->|Test_ReadData| P
J -->|Test_ReadData| M
J -->|MeshFile 字段| D
D -->|GetMesh| G
P --> A
M --> A
G --> A
P --> B
A --> X
B --> X
X --> O
G --> O
```
### 5.2 JSON 字段到内存对象的映射
| JSON 字段组 | 目标对象 | 解析函数 |
|-------------|----------|----------|
| `FemType`, `EletricType`, `BoundaryFlag`, `bele`, `ef`, `pml`, `sbc`, … | `Phy_WaveOpticsModel` | `Phy_WaveOpticsModel::Test_ReadData()` |
| `NbrDomain`, `epsilonrR/I`, `murR/I`, `n`, `k`, `sigma`, … | `MaterialLib` | `MaterialLib::Test_ReadData()` |
| `MeshFile` | 网格文件路径 | `Mesh_2D::GetMesh()` |
| `lambda` / `freq`, `NbrMode`, `searchValue`, … | 求解器参数 | `Solver_*::GetSolver()` |
### 5.3 bele 算例配置摘要
| 配置项 | 值 | 含义 |
|--------|-----|------|
| `FemType` | 2 | 散射问题 |
| `EletricType` | 2 | 求解完整电场 E |
| `lambda` | 1 | 真空波长 |
| `bele.index` | [4] | 边界域 4 施加 BELE |
| `bele.Eby` | `sin(2*pi*x)` | 边界切向电场解析表达式 |
| `ef` | 边界 1 电场激励 | 等效 Dirichlet 激励 |
| `pml` | 4 个吸收层 | PML 参数 |
| `MeshFile` | `project_3.dat` | 网格文件 |
| `OutFile` | `./OutFile` | 输出目录 |
---
## 6. 对外 API 规范
### 6.1 数据结构
```cpp
struct OpticsFEMData {
double test1; // 保留字段
double test2; // 保留字段
char* data; // UTF-8 JSON 字符串(单行或紧凑格式)
};
```
### 6.2 接口函数
```cpp
class OpticsFEM_API {
public:
// 完整求解流程:读配置 → 组装 → 求解 → 后处理
static int OpticsFEM_All(OpticsFEMData data);
// 连通性测试:向 OutFile 写入 "test success"
int OpticsFEM_Test(OpticsFEMData data);
};
```
### 6.3 返回值
| 返回值 | 含义 |
|--------|------|
| 0 | `FemType` 无法识别 |
| 1 | 本征模式完成 |
| 2 | 本征频率完成 |
| 3 | 散射完成 |
### 6.4 集成方式
**方式 A — 独立可执行文件(当前 `Test_Main.cpp` 模式)**
从文件读取 JSON调用 API适用于本地验证与回归测试。
**方式 B — 动态库宿主(注释代码预留)**
`FEM_Interface.h` 底部预留 `extern "C"` 导出声明,可将内核编译为 DLL 供前端或其他语言调用。
---
## 7. 输入与输出文件规范
### 7.1 JSON 配置文件
- **格式**:单行 UTF-8 JSON 或紧凑 JSON
- **读取方式**`Test_Main.cpp` 使用 `getline()` 读取整行;
- **必填公共字段**`FemType`, `NbrBoundary`, `BoundaryFlag`, `NbrDomain`, `MeshFile`, `OutFile`
- **边界扩展字段**(可选,按 `js.contains` 判定):`bele`, `ef`, `pml`, `sbc`, `mag`, `scd`, `pbc`, `mpd`, `epd`, `port`, `beam`
详细字段定义参见 `OpticsFEM_LASPCEM_接口规范.md` 第 2.3 节。
### 7.2 二维网格 `.dat` 文件
`Mesh_2D::GetMesh()``mesh/Mesh_Interface.cpp`)顺序解析,字段顺序为:
```
NbrVertex → Vertex → NbrEdge → Edge → NbrTri → Tri
→ EdgeOfTri → DomainOfTri → NbrEdges → CoonOfEdges
→ DomainOfEdges → normal → CopyOfEdges → ...
```
顶点坐标为二维 `(x, y)`,第三分量在读取时置零。
### 7.3 输出文件
散射问题(`Post_2D_Scatter::OutputData`)在 `OutFile` 目录下生成:
| 文件名 | 内容 |
|--------|------|
| `Ex` | 各顶点 Ex 分量(实部 虚部,逐行) |
| `Ey` | 各顶点 Ey 分量 |
| `Ez` | 各顶点 Ez 分量 |
| `normE` | 各顶点电场模 |
本征问题额外输出 `neff``freq` 文件。
---
## 8. 构建与部署
### 8.1 环境要求
| 组件 | 要求 |
|------|------|
| 操作系统 | Windows 10/11 x64 |
| 编译器 | Visual Studio 202618Community工作负载「使用 C++ 的桌面开发」 |
| 构建工具 | CMake ≥ 3.8VS 自带或独立安装) |
| C++ 标准 | C++17 |
### 8.2 构建命令
```powershell
# 配置 CMake 路径(若未加入系统 PATH
$env:Path += ";C:\Program Files\Microsoft Visual Studio\18\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin"
cd opticsfem-master
mkdir build -Force
cd build
cmake .. -G "Visual Studio 18 2026" -A x64
cmake --build . --config Release
```
产物路径:`build/Release/OpticsFEM.exe`
### 8.3 运行时目录结构
散射算例bele最小部署结构
```
Release/
├── OpticsFEM.exe
├── bele.json # 输入配置(文件名与 Test_Main.cpp 中 fd.open 一致)
├── project_3.dat # 网格(与 JSON 中 MeshFile 一致)
└── complex/
└── complexsolver.exe # 外部线性求解器(运行时必需)
```
本征算例另需:
```
real_solver/real # 或 real_solver 目录下对应可执行文件
complex_solver/complex
```
### 8.4 关于 eigen_solver 静态链接
当前工程已将 `solver/eigen_solver.cpp` 编入可执行文件,并通过宏 `EIGENSOLVER_STATIC` 取消 DLL 导入/导出修饰,**不再依赖** `lib/eigensolver.lib`。该模块服务于本征模式/本征频率问题(`realEigenSolver` / `complexEigenSolver`),运行时仍依赖 `real_solver/``complex_solver/` 目录下的外部可执行文件。
---
## 9. 算例运行指南
### 9.1 标准运行步骤
**步骤 1 — 编译**
执行第 8.2 节构建命令,确认生成 `OpticsFEM.exe`
**步骤 2 — 配置入口 JSON 文件名**
编辑 `test/Test_Main.cpp` 第 28 行:
```cpp
fd.open("bele.json"); // 改为目标算例 JSON 文件名
```
修改后须重新编译。
**步骤 3 — 部署输入文件**
将 JSON 及 `MeshFile` 指定的 `.dat` 文件复制至 `build/Release/` 目录。
**步骤 4 — 部署外部求解器**
`complex/complexsolver.exe` 置于 `Release/complex/` 目录(路径相对于工作目录)。
**步骤 5 — 执行**
```powershell
cd build/Release
.\OpticsFEM.exe
```
**步骤 6 — 验证输出**
检查 `OutFile/` 目录是否生成 `Ex`、`Ey`、`Ez`、`normE`。
### 9.2 切换算例
| 操作 | 说明 |
|------|------|
| 更换 JSON | 修改 `Test_Main.cpp``fd.open()` 文件名,或使用同名复制 |
| 更换网格 | 确保 JSON 中 `MeshFile` 字段与实际 `.dat` 文件名一致 |
| 更换问题类型 | 修改 JSON 中 `FemType`;程序自动路由至对应求解分支 |
测试数据集位于 `测试数据集/` subdirectory 名称即边界/物理类型(如 `bele/`、`scd/`、`pml_xy/`)。
---
## 10. 代码阅读路径
建议按以下顺序阅读,以建立自入口至输出的完整认知:
| 序号 | 文件 | 阅读目标 |
|------|------|----------|
| 1 | `test/Test_Main.cpp` | 进程入口、JSON 文件读取 |
| 2 | `Interface/FEM_Interface.cpp` | FemType 路由、三阶段调度 |
| 3 | `test/Test_ReadData.cpp` | JSON 字段解析逻辑 |
| 4 | `mesh/Mesh_Interface.cpp` | `.dat` 网格格式 |
| 5 | `kernel/Assemble_kernel.cpp` | Assemble / Run / Post 总控 |
| 6 | `kernel/Assemble_Scatter_2D_Boundary.cpp` | BELE / PML / EF 等边界组装 |
| 7 | `kernel/Assemble_Scatter_Equation.cpp` | 体积方程组装 |
| 8 | `solver/Solver_Interface.cpp` | 求解器参数与 Run 实现 |
| 9 | `solver/interface.cpp` | 外部求解器调用封装 |
| 10 | `post/Post_Output.cpp` | 结果文件格式 |
### 调试建议
-`FEM_Interface.cpp``FemType` 分支入口设置断点,确认路由正确;
-`Assemble()` 返回后检查矩阵非零元数量(`Test_OutputMatrix()` 已在散射分支调用);
-`Post()` 返回后检查 `OutFile/` 目录。
---
## 11. 求解器依赖说明
### 11.1 求解器分层
| 层级 | 模块 | 适用问题 | 运行时依赖 |
|------|------|----------|------------|
| 特征值求解 | `eigen_solver.cpp` | FemType 0, 1 | `real_solver/real`, `complex_solver/complex` |
| 线性方程组(实) | `interface.cpp::solveRealLinearEqu` | 实散射 | `real/realsolver.exe` |
| 线性方程组(复) | `interface.cpp::solveComplexLinearEqu` | 复散射bele | `complex/complexsolver.exe` |
### 11.2 外部求解器调用机制
`interface.cpp` 采用**文件交换**方式与外部求解器通信:
1. 将 CSR 格式矩阵、右端项写入当前目录临时文件;
2. 通过 `system()` 调用外部 `.exe`
3. 读取解向量临时文件;
4. 清理临时文件。
因此,外部求解器必须与 `OpticsFEM.exe` **工作目录相对路径**正确对应,且具备可执行权限。
---
## 12. 常见问题与排查
| 现象 | 可能原因 | 处理措施 |
|------|----------|----------|
| `cmake` 无法识别 | CMake 未加入 PATH | 使用 VS 自带 CMake 完整路径,或配置环境变量 |
| `LNK1181: eigensolver.lib` | 旧版 CMakeLists 仍链接外部库 | 确认已启用 `EIGENSOLVER_STATIC` 并编入 `eigen_solver.cpp` |
| 运行无输出、无 OutFile | 缺少 `complexsolver.exe` 或路径错误 | 检查 `complex/` 目录及工作目录 |
| 无法打开 `bele.json` | 工作目录不含 JSON | 在 `Release/` 目录下运行,或复制输入文件 |
| 无法打开 `project_3.dat` | `MeshFile` 路径不匹配 | 确保 `.dat` 与 exe 同目录,或修正 JSON |
| 编译警告 C4819 | Eigen 头文件编码 | 不影响功能,可忽略 |
| `FemType` 返回 0 打印 err | JSON 缺少或错误 `FemType` 字段 | 检查 JSON 完整性 |
---
## 附录 AFemType 路由对照表
| FemType | 求解器类 | 后处理类 | 关键 JSON 参数 |
|---------|----------|----------|----------------|
| 0 | `Solver_EigenMode` | `Post_2D_EigenMode` | `lambda0`, `NbrMode`, `searchValue` |
| 1 | `Solver_EigenFreq` | `Post_2D_EigenFreq` | `lambda0`, `NbrMode`, `searchValue` |
| 2 | `Solver_LdaDom` | `Post_2D_Scatter` | `lambda`, 边界字段 |
| 3 | `Solver_LdaDom` | `Post_2D_Scatter` | `freq`, 边界字段 |
---
## 附录 B文档修订记录
| 版本 | 日期 | 修订内容 |
|------|------|----------|
| v1.0 | 2026-06-03 | 初版:构建流程、架构、数据流、部署与算例指南 |
---
*本文档基于 `opticsfem-master` 当前代码状态编制,若 CMakeLists 或 API 发生变更,请同步更新相应章节。*