22 KiB
OpticsFEM 二维光学有限元程序 — 开发与使用手册
| 项目 | 说明 |
|---|---|
| 文档版本 | v1.0 |
| 编制日期 | 2026-06-03 |
| 适用代码 | opticsfem-master/ |
| 适用平台 | Windows x64,Visual Studio 2026(18)/ MSVC |
| 关联文档 | OpticsFEM_LASPCEM_接口规范.md |
目录
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 并非完整程序,而是可执行文件的入口模块。其职责限于:
- 从本地文件系统读取 JSON 配置文件;
- 构造
OpticsFEMData结构体; - 调用统一 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。目标产物定义如下:
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() 中完成。上述声明为历史遗留或调试用途;注释块(第 39–58 行)保留了手动分步调用流程的旧版写法,现行标准路径为 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 模块依赖关系
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)执行以下步骤:
- 确定矩阵类型:根据 PML、SBC、复材料等条件判定采用实数或复数稀疏矩阵;
- 体积积分:
Assemble_WaveEquation()— 在各三角形单元上积分麦克斯韦弱形式; - 边界积分(按 JSON 配置条件触发):
Assemble_PEC_ELE()— 理想导体 / 电边界Assemble_PBC()— 周期边界Assemble_BELE()— 边界等效源(bele 算例)Assemble_PML()— 完美匹配层Assemble_Port()— 端口激励- 等
- 约束处理:对 Dirichlet 型边界自由度施加罚函数或消元。
输出:稀疏矩阵 A、右端向量 b、预处理矩阵 P。
4.3 Run 阶段
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 阶段
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 端到端数据流
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 数据结构
struct OpticsFEMData {
double test1; // 保留字段
double test2; // 保留字段
char* data; // UTF-8 JSON 字符串(单行或紧凑格式)
};
6.2 接口函数
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 2026(18)Community,工作负载「使用 C++ 的桌面开发」 |
| 构建工具 | CMake ≥ 3.8(VS 自带或独立安装) |
| C++ 标准 | C++17 |
8.2 构建命令
# 配置 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 行:
fd.open("bele.json"); // 改为目标算例 JSON 文件名
修改后须重新编译。
步骤 3 — 部署输入文件
将 JSON 及 MeshFile 指定的 .dat 文件复制至 build/Release/ 目录。
步骤 4 — 部署外部求解器
将 complex/complexsolver.exe 置于 Release/complex/ 目录(路径相对于工作目录)。
步骤 5 — 执行
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 采用文件交换方式与外部求解器通信:
- 将 CSR 格式矩阵、右端项写入当前目录临时文件;
- 通过
system()调用外部.exe; - 读取解向量临时文件;
- 清理临时文件。
因此,外部求解器必须与 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 完整性 |
附录 A:FemType 路由对照表
| 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 发生变更,请同步更新相应章节。