From db3ea489fe5167e18585af8e5b48432b163f1ec2 Mon Sep 17 00:00:00 2001 From: hpnickk-ai Date: Tue, 9 Jun 2026 15:23:53 +0800 Subject: [PATCH] Add 3D eigen frequency (FemType=5) and update docs for scatter+eigen workflows. Support OpticsFEM_3D_EigenFreq assembly with A/B matrix export, add eigen3d.json, and document scat3D/eigen3D build layout alongside FemType=4 scattering. Co-authored-by: Cursor --- .gitignore | 7 + 3D opticsfem-master/.gitignore | 23 +- .../Interface/FEM_Interface.cpp | 21 ++ .../OpticsFEM_完整使用说明与代码介绍.md | 250 +++++++++++++----- 3D opticsfem-master/README.md | 102 +++++-- 3D opticsfem-master/compare/README.md | 8 +- 3D opticsfem-master/eigen3d.json | 33 +++ 3D opticsfem-master/kernel/Assemble_Base.h | 2 + .../kernel/Assemble_EigenFreq_Equation.cpp | 2 +- .../kernel/Assemble_kernel.cpp | 76 +++++- 3D opticsfem-master/test/Test_Main.cpp | 2 +- 11 files changed, 424 insertions(+), 102 deletions(-) create mode 100644 3D opticsfem-master/eigen3d.json diff --git a/.gitignore b/.gitignore index 8a3dea8..07a6a6b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,11 @@ build/ +build1/ +scat3D/ +eigen3D/ +3D opticsfem-master/scat3D/ +3D opticsfem-master/eigen3D/ +3D opticsfem-master/build/ +3D opticsfem-master/build1/ *.exe *.obj *.pdb diff --git a/3D opticsfem-master/.gitignore b/3D opticsfem-master/.gitignore index 4e821a0..26bf6c9 100644 --- a/3D opticsfem-master/.gitignore +++ b/3D opticsfem-master/.gitignore @@ -1,7 +1,24 @@ -################################################################################ -# 此 .gitignore 文件已由 Microsoft(R) Visual Studio 自动创建。 -################################################################################ +# CMake / VS 本地构建目录(勿提交) +/build/ +/build1/ +/scat3D/ +/eigen3D/ +/out/ /.vs /out/build/x64-debug /build/x64-Debug + +# 编译产物 +*.exe +*.obj +*.pdb +*.lib +*.exp +*.ilk +*.tlog +*.lastbuildstate +*.recipe + +# 大型运行输出(本地验证用) +**/OutFile/ diff --git a/3D opticsfem-master/Interface/FEM_Interface.cpp b/3D opticsfem-master/Interface/FEM_Interface.cpp index cd9914e..4022428 100644 --- a/3D opticsfem-master/Interface/FEM_Interface.cpp +++ b/3D opticsfem-master/Interface/FEM_Interface.cpp @@ -139,6 +139,27 @@ int OpticsFEM_API::OpticsFEM_All(OpticsFEMData data) fem.Post(outFile); return 4; } + else if (FemType == 5) + { + Mesh_3D mesh3d; + OpticsFEM_3D_EigenFreq fem; + Solver_EigenFreq solver; + Post_3D_EigenFreq post; + + solver.GetSolver(str); + mesh3d.GetMesh(meshFile, str); + + fem.GetMaterial(&matLab); + fem.GetMesh(&mesh3d); + fem.GetPhy(&phy); + fem.GetSolver(&solver); + fem.GetPost(&post); + + fem.Assemble(); + string outFile = js.at("OutFile"); + fem.Test_OutputMatrix(outFile); + return 5; + } else { std::cout << "err" << std::endl; diff --git a/3D opticsfem-master/OpticsFEM_完整使用说明与代码介绍.md b/3D opticsfem-master/OpticsFEM_完整使用说明与代码介绍.md index cffb582..185b6af 100644 --- a/3D opticsfem-master/OpticsFEM_完整使用说明与代码介绍.md +++ b/3D opticsfem-master/OpticsFEM_完整使用说明与代码介绍.md @@ -2,9 +2,9 @@ | 项目 | 说明 | |------|------| -| 文档版本 | v2.0 | -| 编制日期 | 2026-06-08 | -| 适用代码 | **`3D opticsfem-master/`**(三维版,主算例 FemType=4) | +| 文档版本 | v3.0 | +| 编制日期 | 2026-06-09 | +| 适用代码 | **`3D opticsfem-master/`**(三维版,主算例 FemType=4 散射 + FemType=5 本征频率) | | 适用平台 | Windows x64,**Visual Studio 2026(18)**,MSVC | | 关联文档 | `compare/README.md`(COMSOL 对比)、上级目录 2D 版 `opticsfem-master/` | @@ -31,17 +31,20 @@ **OpticsFEM3D** 是一套基于 C++ 实现的**三维矢量有限元(FEM)光学电磁仿真内核**。程序采用 **一阶 Nedelec 边元** 离散麦克斯韦方程,通过 JSON 配置物理模型、材料与边界条件,通过 `.dat` 文本文件读入四面体网格,计算结果以文本文件输出。 -> **当前主算例**:`FemType = 4`,三维 SBC 散射,配置文件 `sbc3d.json`,网格 `SBCmesh.dat`。 +> **当前三维主算例**: +> - **FemType = 4**:三维 SBC 散射,`sbc3d.json`,网格 `SBCmesh.dat`,完整求解流程。 +> - **FemType = 5**:三维本征频率,`eigen3d.json`,同一网格,当前完成 A/B 矩阵组装与导出。 ### 1.2 支持的问题类型 -| FemType | 问题类型 | 驱动类 | 网格 | 求解器 | 后处理 | -|---------|----------|--------|------|--------|--------| -| 0 | 本征模式(2D 遗留) | `OpticsFEM_2D_EigenMode` | `Mesh_2D` | `Solver_EigenMode` | `Post_2D_EigenMode` | -| 1 | 本征频率(2D 遗留) | `OpticsFEM_2D_EigenFreq` | `Mesh_2D` | `Solver_EigenFreq` | `Post_2D_EigenFreq` | -| 2 | 2D 散射(按波长 λ) | `OpticsFEM_2D_Scatter` | `Mesh_2D` | `Solver_LdaDom` | `Post_2D_Scatter` | -| 3 | 2D 散射(按频率 f) | `OpticsFEM_2D_Scatter` | `Mesh_2D` | `Solver_LdaDom` | `Post_2D_Scatter` | -| **4** | **3D 一阶散射(SBC,按 λ)** | **`OpticsFEM_3D_Scatter`** | **`Mesh_3D`** | **`Solver_LdaDom`** | **`Post_3D_Scatter`** | +| FemType | 问题类型 | 驱动类 | 网格 | 求解器 | 后处理 | 状态 | +|---------|----------|--------|------|--------|--------|------| +| 0 | 本征模式(2D 遗留) | `OpticsFEM_2D_EigenMode` | `Mesh_2D` | `Solver_EigenMode` | `Post_2D_EigenMode` | 遗留 | +| 1 | 本征频率(2D 遗留) | `OpticsFEM_2D_EigenFreq` | `Mesh_2D` | `Solver_EigenFreq` | `Post_2D_EigenFreq` | 遗留 | +| 2 | 2D 散射(按波长 λ) | `OpticsFEM_2D_Scatter` | `Mesh_2D` | `Solver_LdaDom` | `Post_2D_Scatter` | 遗留 | +| 3 | 2D 散射(按频率 f) | `OpticsFEM_2D_Scatter` | `Mesh_2D` | `Solver_LdaDom` | `Post_2D_Scatter` | 遗留 | +| **4** | **3D 一阶散射(SBC,按 λ)** | **`OpticsFEM_3D_Scatter`** | **`Mesh_3D`** | **`Solver_LdaDom`** | **`Post_3D_Scatter`** | **完整** | +| **5** | **3D 一阶本征频率** | **`OpticsFEM_3D_EigenFreq`** | **`Mesh_3D`** | **`Solver_EigenFreq`** | **`Post_3D_EigenFreq`** | **组装+矩阵导出** | ### 1.3 设计原则 @@ -62,7 +65,8 @@ | FemType | 依赖 | 说明 | |---------|------|------| -| **4(3D 散射,推荐)** | `complex/complexsolver.exe` | **CMake 会从 `complex/complexsolver.cpp` 自动编译**(Eigen SparseLU),无需 PETSc | +| **4(3D 散射)** | `complex/complexsolver.exe` | **CMake 自动编译**(Eigen SparseLU),求解 **A·x=b** | +| **5(3D 本征频率)** | 暂无(矩阵导出阶段) | 当前仅 `Assemble` + `Test_OutputMatrix`;完整求解需 `real_solver/`、`complex_solver/` | | 0–3(2D 遗留) | `complex/complexsolver.exe` 等 | 本征问题另需 `real_solver/`、`complex_solver/` | 散射求解器通过**临时 `.dat` 文件**与主程序交换 CSR 格式矩阵,由 `system()` 启动。 @@ -75,8 +79,9 @@ 3D opticsfem-master/ ├── CMakeLists.txt # CMake:OpticsFEM + complexsolver ├── CMakeSettings.json # VS CMake 设置(x64-Release) -├── sbc3d.json # ★ FemType=4 默认算例配置 -├── SBCmesh.dat # ★ 三维四面体网格 +├── sbc3d.json # ★ FemType=4 散射算例配置 +├── eigen3d.json # ★ FemType=5 本征频率算例配置 +├── SBCmesh.dat # ★ 三维四面体网格(散射与本征共用) ├── Interface/ # 对外 API(FemType 路由) ├── test/ # Test_Main.cpp 入口、JSON 解析 ├── complex/ # complexsolver 源码(Eigen 稀疏求解) @@ -84,18 +89,19 @@ ├── phy/ # 物理模型与边界条件 ├── material/ # 材料库 ├── mesh/ # Mesh_2D + Mesh_3D 读写 -├── kernel/ # 2D/3D 组装(含 Assemble_Scatter_3D_Boundary) +├── kernel/ # 2D/3D 组装(散射 SBC + 本征 A/B) ├── solver/ # 数值求解器接口 -├── post/ # Post_3D_Scatter 场重构与输出 +├── post/ # Post_3D_Scatter / Post_3D_EigenFreq ├── function/ # Nedelec 基函数与高斯积分 ├── common/ # 公共常量 ├── parser/ # 表达式解析器 ├── nlohmann/ # JSON 库 ├── Eigen/ # 线性代数库 -└── build/ # CMake 构建产物(本地生成,不上传) +├── scat3D/ # 推荐:FemType=4 本地 CMake 构建(不上传) +└── eigen3D/ # 推荐:FemType=5 本地 CMake 构建(不上传) ``` -**源码统计(不含 build/):** +**源码统计(不含 scat3D/、eigen3D/、build/ 等构建目录):** | 类别 | 数量 | 说明 | |------|------|------| @@ -132,7 +138,7 @@ public: }; ``` -**返回值:** 0 = FemType 无法识别;1 = 本征模式;2 = 本征频率;3 = 2D 散射;**4 = 3D 散射完成**。 +**返回值:** 0 = FemType 无法识别;1 = 2D 本征模式;2 = 2D 本征频率;3 = 2D 散射;**4 = 3D 散射完成**;**5 = 3D 本征频率矩阵导出完成**。 --- @@ -140,7 +146,7 @@ public: | 文件 | 说明 | |------|------| -| `Test_Main.cpp` | 进程 `main()` 入口:从 `bele.json` 读取整行 JSON,封装为 `OpticsFEMData`,调用 `OpticsFEM_All()` | +| `Test_Main.cpp` | 进程 `main()` 入口:从 JSON 文件(默认 `sbc3d.json`)读取整行 JSON,封装为 `OpticsFEMData`,调用 `OpticsFEM_All()`。本征算例改为 `eigen3d.json` 后须重新编译 | | `Test_ReadData.cpp` | 实现 `Phy_WaveOpticsModel::Test_ReadData()` 与 `MaterialLib::Test_ReadData()`,将 JSON 字段映射到内存对象 | | `Test_OutputData.cpp` | 调试用途:将组装后的稀疏矩阵 A/B/P 输出到文本文件 | | `interfaceClass.h` | 遗留的 `FemInput` 结构体,记录预期配置字段(当前 JSON 路径未直接使用) | @@ -204,7 +210,7 @@ public: | `Mesh_Interface.cpp` | `Mesh_2D::GetMesh()`:顺序解析 `.dat` 网格文件 | | `Mesh_Find.cpp` | 域/PBC 索引查找:边/节点所属域、PBC 源/目标映射 | -**Mesh_3D** 已在头文件中声明,但 `OpticsFEM_All()` 当前未接入 3D 求解分支。 +**Mesh_3D** 已接入 `FemType=4`(散射)与 `FemType=5`(本征频率),读入 `SBCmesh.dat` 等三维四面体网格。 --- @@ -350,7 +356,8 @@ FEM_Interface.cpp ──► Phy / Material / Mesh(读入) ├── FemType 0 ──► OpticsFEM_2D_EigenMode ──► Solver_EigenMode ──► Post_2D_EigenMode ├── FemType 1 ──► OpticsFEM_2D_EigenFreq ──► Solver_EigenFreq ──► Post_2D_EigenFreq ├── FemType 2/3 ► OpticsFEM_2D_Scatter ──► Solver_LdaDom ──► Post_2D_Scatter - └── FemType 4 ──► OpticsFEM_3D_Scatter ──► Solver_LdaDom ──► Post_3D_Scatter + ├── FemType 4 ──► OpticsFEM_3D_Scatter ──► Solver_LdaDom ──► Post_3D_Scatter + └── FemType 5 ──► OpticsFEM_3D_EigenFreq ──► Solver_EigenFreq ──► Post_3D_EigenFreq │ └── Mesh_3D ← SBCmesh.dat │ @@ -375,6 +382,22 @@ Test_Main.cpp └─ Post("OutFile") → Ex, Ey, Ez, normE ``` +### 4.2b 完整调用序列(FemType=5,三维本征频率) + +``` +Test_Main.cpp + └─ 读取 eigen3d.json + └─ OpticsFEM_All(data) + └─ [FemType == 5] + ├─ Phy / Material ← JSON(PMC/PEC/PBC 边界) + ├─ Mesh_3D::GetMesh("SBCmesh.dat") + ├─ OpticsFEM_3D_EigenFreq::Assemble() + │ ├─ Assemble_WaveEquation() A=curl-curl, B=ε·质量 + │ └─ Assemble_PEC_PBC() 边界约束矩阵 P + └─ Test_OutputMatrix("OutFile") → Ai/Aj/Av, Bi/Bj/Bv + (Run/Post 待接入:eigs 求 λ,再重构 Ex/Ey/Ez/freq) +``` + ### 4.3 完整调用序列(FemType=2,二维散射,遗留) ``` @@ -403,7 +426,7 @@ main() ### 4.4 数据流 -**FemType = 4(当前主算例):** +**FemType = 4(三维散射):** ``` sbc3d.json ──► Phy(sbc 边界/入射场) @@ -414,6 +437,19 @@ SBCmesh.dat ──► Mesh_3D(四面体 + 三角边界) ──► Post ──► OutFile/Ex, Ey, Ez, normE ``` +**FemType = 5(三维本征频率):** + +``` +eigen3d.json ──► Phy(PMC/PEC/PBC) + ──► MaterialLib +SBCmesh.dat ──► Mesh_3D + ──► Assemble ──► 稀疏矩阵 A(刚度), B(质量) + ──► Test_OutputMatrix ──► OutFile/Ai,Aj,Av,Bi,Bj,Bv + (后续 Run: eigs(A,B) → λ=k₀²;Post → freq, Ex/Ey/Ez) +``` + +**物理对应关系:** 散射体积分 `A_scatter ≈ A_eigen − k₀²·B_eigen`(同一网格与 Nedelec 基函数下)。 + **FemType = 2(2D 遗留):** ``` @@ -441,26 +477,31 @@ project_3.dat ──► Mesh_2D(网格拓扑) ### 5.2 编译步骤 +推荐使用**两个独立构建目录**,避免散射与本征互相覆盖 exe 和 OutFile: + ```powershell # 若 cmake 不在 PATH 中,追加 VS 2026 自带 CMake 路径 $env:Path += ";C:\Program Files\Microsoft Visual Studio\18\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin" cd "3D opticsfem-master" -mkdir build -Force -cd build -cmake .. -G "Visual Studio 18 2026" -A x64 -cmake --build . --config Release +# FemType=4 散射 +cmake -B scat3D -G "Visual Studio 18 2026" -A x64 +cmake --build scat3D --config Release + +# FemType=5 本征频率 +cmake -B eigen3D -G "Visual Studio 18 2026" -A x64 +cmake --build eigen3D --config Release ``` -**产物路径:** `build/Release/OpticsFEM.exe` - **产物路径:** -- `build/Release/OpticsFEM.exe` -- `build/Release/complex/complexsolver.exe`(FemType=4 必需,CMake 自动编译) +- `scat3D/Release/OpticsFEM.exe` + `scat3D/Release/complex/complexsolver.exe` +- `eigen3D/Release/OpticsFEM.exe` -### 5.3 运行前准备(FemType = 4,推荐) +也可使用单一 `build/` 目录(见 `CMakeSettings.json`),但切换算例时需重新编译并注意 OutFile 覆盖。 + +### 5.3 运行前准备(FemType = 4,三维散射) #### 步骤 1:指定 JSON 配置文件 @@ -470,22 +511,20 @@ cmake --build . --config Release std::ifstream fd("sbc3d.json"); ``` -更换算例时,修改文件名或把目标 JSON 复制为 `sbc3d.json`。修改后须**重新编译**。 +更换算例时修改文件名;修改后须**重新编译 scat3D**。 #### 步骤 2:部署输入文件 -将 JSON 及 `MeshFile` 指定的网格文件复制到 **`build/Release/`**(与 exe 同目录): +将 JSON 及 `MeshFile` 指定的网格文件复制到 **`scat3D/Release/`**(与 exe 同目录): ```powershell -Copy-Item sbc3d.json, SBCmesh.dat -Destination build\Release\ +Copy-Item sbc3d.json, SBCmesh.dat -Destination scat3D\Release\ ``` #### 步骤 3:确认求解器 -FemType=4 只需: - ``` -Release/ +scat3D/Release/ ├── OpticsFEM.exe ├── sbc3d.json ├── SBCmesh.dat @@ -496,7 +535,7 @@ Release/ #### 步骤 4:执行 ```powershell -cd build/Release +cd scat3D/Release .\OpticsFEM.exe ``` @@ -508,6 +547,48 @@ cd build/Release --- +### 5.3c 运行前准备(FemType = 5,三维本征频率) + +#### 步骤 1:指定 JSON 配置文件 + +编辑 `test/Test_Main.cpp`: + +```cpp +std::ifstream fd("eigen3d.json"); +``` + +修改后须**重新编译 eigen3D**(可与 scat3D 并存,互不干扰)。 + +#### 步骤 2:部署输入文件 + +```powershell +Copy-Item eigen3d.json, SBCmesh.dat -Destination eigen3D\Release\ +``` + +#### 步骤 3:执行 + +```powershell +cd eigen3D/Release +.\OpticsFEM.exe +``` + +控制台应打印 `OpticsFEM_All returned 5`。 + +#### 步骤 4:查看矩阵输出 + +`OutFile/` 下导出 A/B 稀疏矩阵 COO(0-based): + +| 文件 | 含义 | +|------|------| +| `Ai.txt`, `Aj.txt`, `Av.txt` | 刚度矩阵 A | +| `Bi.txt`, `Bj.txt`, `Bv.txt` | 质量矩阵 B(ε 加权) | + +可与 MATLAB `3D一阶本征问题2` 中 `export_ab_matrix.m` 输出对比验证。 + +> **当前限制**:`Run()`(广义特征值求解)与 `Post()`(场重构、freq 输出)尚未接入 `FEM_Interface.cpp` 的 FemType=5 分支。 + +--- + ### 5.3b 运行前准备(FemType 0–3,2D 遗留) #### 步骤 1:指定 JSON 配置文件 @@ -560,18 +641,26 @@ cd build/Release |------|------| | 更换 JSON | 修改 `Test_Main.cpp` 中 `fd.open()` 文件名,或将目标 JSON 复制为当前文件名 | | 更换网格 | 确保 JSON 中 `MeshFile` 与实际 `.dat` 文件名一致 | -| 更换问题类型 | 修改 JSON 中 `FemType`(0/1/2/3/4),程序自动路由 | +| 更换问题类型 | 修改 JSON 中 `FemType`(0/1/2/3/4/5),程序自动路由 | -### 5.5 快速验证(sbc3d 三维 SBC 算例) +### 5.5 快速验证(sbc3d 三维 SBC 散射) -1. 编译 Release(见 5.2 节) -2. 复制 `sbc3d.json`、`SBCmesh.dat` 到 `build/Release/` +1. 编译 `scat3D` Release(见 5.2 节) +2. 确认 `Test_Main.cpp` 读取 `sbc3d.json`,复制 `sbc3d.json`、`SBCmesh.dat` 到 `scat3D/Release/` 3. 确认 `sbc3d.json` 中 `"FemType": 4` 4. 运行 `OpticsFEM.exe` 5. 检查 `OutFile/` 下四个场文件 与 COMSOL 对比见 `compare/README.md`。 +### 5.5b 快速验证(eigen3d 三维本征频率) + +1. 编译 `eigen3D` Release,`Test_Main.cpp` 改为读取 `eigen3d.json` +2. 复制 `eigen3d.json`、`SBCmesh.dat` 到 `eigen3D/Release/` +3. 确认 `"FemType": 5` +4. 运行 `OpticsFEM.exe`,应返回 5 +5. 检查 `OutFile/` 下 `Ai/Aj/Av`、`Bi/Bj/Bv` 六个文件 + ### 5.6 快速验证(bele 二维算例,遗留) 1. 从 `../测试数据集/bele/` 复制 `bele.json` 和 `project_3.dat` 到 `build/Release/` @@ -602,7 +691,7 @@ data.test2 = 0; data.data = (char*)jsonStr.data(); int ret = OpticsFEM_API::OpticsFEM_All(data); -// ret: 1=本征模式, 2=本征频率, 3=2D散射, 4=3D散射, 0=错误 +// ret: 1=2D本征模式, 2=2D本征频率, 3=2D散射, 4=3D散射, 5=3D本征矩阵导出, 0=错误 ``` --- @@ -618,7 +707,7 @@ int ret = OpticsFEM_API::OpticsFEM_All(data); | 字段 | 类型 | 说明 | |------|------|------| -| `FemType` | int | 0=本征模式, 1=本征频率, 2=2D散射(λ), 3=2D散射(f), **4=3D散射(λ)** | +| `FemType` | int | 0=2D本征模式, 1=2D本征频率, 2=2D散射(λ), 3=2D散射(f), **4=3D散射(λ)**, **5=3D本征频率** | | `NbrBoundary` | int | 边界数量(3D 为三角面边界条数;2D 为边界面条数) | | `BoundaryFlag` | int[] | 每条边界的类型编码(见 3.3 节) | | `NbrDomain` | int | 材料物理域数量 | @@ -630,9 +719,10 @@ int ret = OpticsFEM_API::OpticsFEM_All(data); | 字段 | 说明 | |------|------| -| `NbrMode` | 求解模态数量 | -| `searchValue` | 特征值搜索目标 | -| `lambda0` | 真空波长(本征模式 FemType=0) | +| `NbrMode` | 求解模态数量(FemType 0/1/5) | +| `searchValue` | 特征值搜索目标(FemType 0/1/5) | +| `searchType`, `solverType`, `maxIterNum`, `tol`, `tolType` | 特征值求解器参数(FemType 5 JSON 已预留) | +| `lambda0` | 参考波长(FemType 0/5) | #### 散射问题专用字段 @@ -690,6 +780,30 @@ int ret = OpticsFEM_API::OpticsFEM_All(data); **BoundaryFlag 编码(三角面边界):** 0=PMC, 1=PEC, **2=SBC**, 3=ELE, 4=PBC, … +#### eigen3d 算例 JSON 摘要(FemType = 5) + +```json +{ + "FemType": 5, + "EletricType": 2, + "NbrBoundary": 14, + "BoundaryFlag": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "NbrDomain": 2, + "matType": [0, 0], + "epsilonrR": [1.0, 1.5], + "epsilonrI": [0.0, 0.0], + "murR": [1.0, 1.0], + "MeshFile": "SBCmesh.dat", + "OutFile": "./OutFile", + "NbrMode": 6, + "searchType": 0, + "searchValue": 374740572.5, + "lambda0": 0.8 +} +``` + +本算例为全 PMC 边界(`BoundaryFlag` 全 0),与 MATLAB `3D一阶本征问题2` 对齐。方程 **A·x = λ·B·x**,λ = k₀²。 + #### SBCmesh.dat 网格格式(Mesh_3D) 文本格式,按块顺序读取(1-based 索引,读入后转 0-based): @@ -734,7 +848,7 @@ int ret = OpticsFEM_API::OpticsFEM_All(data); --- -### 6.2 三维网格 SBCmesh.dat(Mesh_3D,FemType = 4) +### 6.2 三维网格 SBCmesh.dat(Mesh_3D,FemType = 4 / 5) 见上文 [SBCmesh.dat 网格格式](#sbcmeshdat-网格格式mesh_3d)。 @@ -863,16 +977,18 @@ extern "C" { | 序号 | 文件 | 阅读目标 | |------|------|----------| -| 1 | `test/Test_Main.cpp` | 入口,默认读 `sbc3d.json` | -| 2 | `Interface/FEM_Interface.cpp` | FemType 路由(含 **FemType=4 → 3D**) | +| 1 | `test/Test_Main.cpp` | 入口,默认读 `sbc3d.json`(本征改为 `eigen3d.json`) | +| 2 | `Interface/FEM_Interface.cpp` | FemType 路由(**4→3D散射**, **5→3D本征**) | | 3 | `test/Test_ReadData.cpp` | JSON → Phy/Material(含 `sbc` 块) | | 4 | `mesh/Mesh_Interface.cpp` | `Mesh_3D::GetMesh` 读 SBCmesh.dat | -| 5 | `kernel/Assemble_kernel.cpp` | `OpticsFEM_3D_Scatter` 总控 | -| 6 | `kernel/Assemble_Scatter_3D_Boundary.cpp` | 3D SBC 边界积分 | -| 7 | `kernel/Assemble_Scatter_Equation.cpp` | 四面体体积方程 | -| 8 | `complex/complexsolver.cpp` | Eigen 稀疏线性求解 | -| 9 | `post/Post_CalEletric.cpp` | `Post_3D_Scatter::GetElectric` | -| 10 | `post/Post_Output.cpp` | 写入 OutFile | +| 5 | `kernel/Assemble_kernel.cpp` | `OpticsFEM_3D_Scatter` / `OpticsFEM_3D_EigenFreq` 总控 | +| 6 | `kernel/Assemble_Scatter_3D_Boundary.cpp` | 3D SBC 边界积分(FemType=4) | +| 7 | `kernel/Assemble_Scatter_Equation.cpp` | 散射四面体体积方程(FemType=4) | +| 8 | `kernel/Assemble_EigenFreq_Equation.cpp` | 本征 A/B 体积积分(FemType=5) | +| 9 | `kernel/Assemble_EigenFreq_Boundary.cpp` | 本征 PEC/PBC(FemType=5) | +| 10 | `complex/complexsolver.cpp` | Eigen 稀疏线性求解(FemType=4) | +| 11 | `post/Post_CalEletric.cpp` | `Post_3D_Scatter::GetElectric` | +| 12 | `post/Post_Output.cpp` | 写入 OutFile | ### 调试建议 @@ -891,7 +1007,7 @@ extern "C" { | 运行无输出、无 OutFile | 缺少 `complexsolver.exe` 或路径错误 | 检查 `complex/` 目录及工作目录 | | 无法打开 `sbc3d.json` | 工作目录不含 JSON | 复制到 `Release/` 或在其中运行 | | 无法打开 `SBCmesh.dat` | `MeshFile` 路径不匹配 | 确保 `.dat` 与 exe 同目录 | -| 无法打开 `bele.json` | 2D 算例文件缺失 | 从 `测试数据集/bele/` 复制 | +| 无法打开 `eigen3d.json` | 本征算例 JSON 缺失 | 复制到 `eigen3D/Release/` 并确认 Test_Main 文件名 | | 控制台打印 `err` | JSON 缺少或错误 `FemType` | 检查 JSON 完整性 | | 编译警告 C4819 | Eigen 头文件编码 | 不影响功能,可忽略 | | BELE 表达式报错 | 表达式语法错误或变量未定义 | 检查 muparser 支持的函数(如 `sin`, `pi`, `x`) | @@ -904,11 +1020,15 @@ extern "C" { OpticsFEM_API ├── Phy_WaveOpticsModel ← JSON 边界/激励 ├── MaterialLib ← JSON 材料 - ├── Mesh_3D ← SBCmesh.dat(FemType=4) - └── OpticsFEM_3D_Scatter ← 3D 散射驱动 - ├── Assemble(体积 + SBC 边界) - ├── Solver_LdaDom → complexsolver - └── Post_3D_Scatter → OutFile + ├── Mesh_3D ← SBCmesh.dat(FemType=4/5) + ├── OpticsFEM_3D_Scatter ← FemType=4:A·x=b + │ ├── Assemble(体积 + SBC) + │ ├── Solver_LdaDom → complexsolver + │ └── Post_3D_Scatter → Ex/Ey/Ez/normE + └── OpticsFEM_3D_EigenFreq ← FemType=5:A·x=λB·x + ├── Assemble(A 刚度 + B 质量 + PEC/PBC) + ├── Test_OutputMatrix → Ai/Aj/Av, Bi/Bj/Bv + └── (待接)Solver_EigenFreq → Post_3D_EigenFreq ``` --- @@ -922,6 +1042,7 @@ OpticsFEM_API | 2 | `lambda`, 边界块 | Ex/Ey/Ez/normE | | 3 | `freq`, 边界块 | Ex/Ey/Ez/normE | | **4** | **`lambda`, `sbc` 块, `SBCmesh.dat`** | **Ex/Ey/Ez/normE** | +| **5** | **`NbrMode`, `searchValue`, `lambda0`, `SBCmesh.dat`** | **Ai/Aj/Av, Bi/Bj/Bv**(当前);后续 **freq, Ex/Ey/Ez** | --- @@ -929,9 +1050,10 @@ OpticsFEM_API | 版本 | 日期 | 修订内容 | |------|------|----------| +| v3.0 | 2026-06-09 | 新增 FemType=5 三维本征频率;scat3D/eigen3D 双构建目录;eigen3d.json;A/B 矩阵导出 | | v2.0 | 2026-06-08 | 适配 3D 版:FemType=4、sbc3d 算例、SBCmesh 格式、CMake complexsolver | | v1.0 | 2026-06-03 | 初版(2D opticsfem-master) | --- -*本文档基于 `3D opticsfem-master` 当前代码状态编制。主算例:`sbc3d.json` + `SBCmesh.dat`,FemType=4。* +*本文档基于 `3D opticsfem-master` 当前代码状态编制。三维主算例:`sbc3d.json`(散射 FemType=4)+ `eigen3d.json`(本征 FemType=5),共用 `SBCmesh.dat`。* diff --git a/3D opticsfem-master/README.md b/3D opticsfem-master/README.md index c4fefd6..a123f40 100644 --- a/3D opticsfem-master/README.md +++ b/3D opticsfem-master/README.md @@ -7,60 +7,106 @@ | FemType | 说明 | 网格 | 状态 | |---------|------|------|------| | 0–3 | 二维问题(本征/散射) | `Mesh_2D` | 遗留接口,与 2D 版相同 | -| **4** | **三维一阶散射(SBC)** | **`Mesh_3D`** | **当前主算例** | +| **4** | **三维一阶散射(SBC)** | **`Mesh_3D`** | **完整:组装 → 求解 → 后处理** | +| **5** | **三维一阶本征频率** | **`Mesh_3D`** | **组装 + A/B 矩阵导出(Run/Post 待接)** | -## 快速开始(FemType = 4) +三维主算例共用网格 **`SBCmesh.dat`**,散射与本征分别使用 `sbc3d.json`、`eigen3d.json`。 -### 环境 +## 推荐构建目录 + +为避免散射与本征互相覆盖 exe 和 OutFile,建议使用**两个独立 CMake 构建目录**: + +| 目录 | 用途 | 配置文件 | +|------|------|----------| +| `scat3D/` | FemType=4 散射 | `sbc3d.json` | +| `eigen3D/` | FemType=5 本征频率 | `eigen3d.json` | + +旧目录 `build/`、`build1/` 为早期本地构建,可保留或删除,**不必上传 Git**。 + +## 环境 - Windows 10/11 x64 - Visual Studio 2026(18),工作负载:使用 C++ 的桌面开发 - CMake ≥ 3.8(随 VS 安装) -### 编译 +若 `cmake` 不在 PATH 中: + +```powershell +$env:Path += ";C:\Program Files\Microsoft Visual Studio\18\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin" +``` + +## 编译 ```powershell cd "3D opticsfem-master" -# 若 cmake 不在 PATH 中,追加 VS 2026 自带 CMake -$env:Path += ";C:\Program Files\Microsoft Visual Studio\18\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin" +# 散射(FemType=4) +cmake -B scat3D -G "Visual Studio 18 2026" -A x64 +cmake --build scat3D --config Release -mkdir build -cd build -cmake .. -G "Visual Studio 18 2026" -A x64 -cmake --build . --config Release +# 本征频率(FemType=5) +cmake -B eigen3D -G "Visual Studio 18 2026" -A x64 +cmake --build eigen3D --config Release ``` -产物: +产物(每个构建目录各一份): -- `build/Release/OpticsFEM.exe` -- `build/Release/complex/complexsolver.exe`(CMake 自动编译) +- `scat3D/Release/OpticsFEM.exe` 或 `eigen3D/Release/OpticsFEM.exe` +- `*/Release/complex/complexsolver.exe`(FemType=4 散射求解必需,CMake 自动编译) -### 运行 sbc3d 算例 +## 运行散射算例(FemType = 4) + +1. 修改 `test/Test_Main.cpp`,确保读取 `sbc3d.json`(当前默认),重新编译 `scat3D`。 +2. 部署输入文件: ```powershell -# 复制输入文件到 exe 同目录 -Copy-Item ..\sbc3d.json, ..\SBCmesh.dat -Destination Release\ - -cd Release +Copy-Item sbc3d.json, SBCmesh.dat -Destination scat3D\Release\ +cd scat3D\Release .\OpticsFEM.exe ``` -结果输出到 `Release/OutFile/`:`Ex`、`Ey`、`Ez`、`normE`。 +3. 控制台应打印 `OpticsFEM_All returned 4`。 +4. 结果在 `OutFile/`:`Ex`、`Ey`、`Ez`、`normE`。 -### 输入文件 +## 运行本征频率算例(FemType = 5) -| 文件 | 说明 | -|------|------| -| `sbc3d.json` | 算例配置,`FemType` 必须为 **4** | -| `SBCmesh.dat` | 三维四面体网格(顶点、四面体、边、三角边界等) | +1. 修改 `test/Test_Main.cpp` 中 JSON 文件名为 `eigen3d.json`,重新编译 `eigen3D`。 +2. 部署输入文件: + +```powershell +Copy-Item eigen3d.json, SBCmesh.dat -Destination eigen3D\Release\ +cd eigen3D\Release +.\OpticsFEM.exe +``` + +3. 控制台应打印 `OpticsFEM_All returned 5`。 +4. 当前输出 `OutFile/` 下 A/B 稀疏矩阵 COO:`Ai/Aj/Av`、`Bi/Bj/Bv`(0-based),供与 MATLAB `eigs(A,B)` 对比验证。 + +> **说明**:FemType=5 的 `Run()`(特征值求解)与 `Post()`(场重构)尚未接入主流程;矩阵组装与边界处理已完成。 + +## 输入文件 + +| 文件 | FemType | 说明 | +|------|---------|------| +| `sbc3d.json` | 4 | 散射配置,含 `sbc` 边界块与 `lambda` | +| `eigen3d.json` | 5 | 本征频率配置,含 `NbrMode`、`searchValue` 等 | +| `SBCmesh.dat` | 4 / 5 | 三维四面体网格(顶点、四面体、边、三角边界等) | + +## 物理形式简述 + +| 问题 | 方程形式 | 矩阵 | +|------|----------|------| +| 散射 (4) | **A·x = b**(给定 λ/k₀) | A = curl-curl − k₀²ε + SBC;b 来自入射边界 | +| 本征 (5) | **A·x = λ·B·x** | A = curl-curl(刚度);B = ε·质量 | + +体积分核函数相同,散射将刚度与质量合并进 A 并加边界激励;本征拆成 A、B 求特征值。 ## 详细文档 -- [OpticsFEM_完整使用说明与代码介绍.md](./OpticsFEM_完整使用说明与代码介绍.md) — 模块说明、JSON 字段、FemType=4 专章 -- [compare/README.md](./compare/README.md) — 与 COMSOL 的 normE 切面对比 +- [OpticsFEM_完整使用说明与代码介绍.md](./OpticsFEM_完整使用说明与代码介绍.md) — 模块说明、JSON 字段、FemType=4/5 专章 +- [compare/README.md](./compare/README.md) — 与 COMSOL 的 normE 切面对比(散射) ## 仓库说明 -- 只需上传**源码**;`build/` 由 CMake 本地生成,不必提交。 -- 散射算例(FemType=4)的 `complexsolver.exe` 可由 CMake 从 `complex/complexsolver.cpp` 编译,无需单独 PETSc 版求解器。 +- 仅上传**源码**与算例 JSON;`scat3D/`、`eigen3D/`、`build/`、`build1/` 为本地 CMake 产物,不上传。 +- FemType=4 的 `complexsolver.exe` 由 CMake 从 `complex/complexsolver.cpp` 编译,无需单独 PETSc 版求解器。 diff --git a/3D opticsfem-master/compare/README.md b/3D opticsfem-master/compare/README.md index a215ea4..cc0b9a5 100644 --- a/3D opticsfem-master/compare/README.md +++ b/3D opticsfem-master/compare/README.md @@ -1,6 +1,6 @@ # normE 切面对比(COMSOL vs OpticsFEM) -在 **z = 0.5 m** 平面上对比 COMSOL 与 C++ OpticsFEM 的 |E| 热图。 +在 **z = 0.5 m** 平面上对比 COMSOL 与 C++ OpticsFEM 的 |E| 热图(**散射算例 FemType=4**)。 ## 目录 @@ -18,14 +18,14 @@ ## 使用步骤 -### 1. 运行 C++ 求解 +### 1. 运行 C++ 散射求解 ```powershell -cd "3D opticsfem-master\build\Release" +cd "3D opticsfem-master\scat3D\Release" .\OpticsFEM.exe ``` -确认生成 `OutFile/normE`。 +确认生成 `OutFile/normE`(需已部署 `sbc3d.json` 与 `SBCmesh.dat`,见根目录 README)。 ### 2. 导出 COMSOL 切面 diff --git a/3D opticsfem-master/eigen3d.json b/3D opticsfem-master/eigen3d.json new file mode 100644 index 0000000..aef5572 --- /dev/null +++ b/3D opticsfem-master/eigen3d.json @@ -0,0 +1,33 @@ +{ + "FemType": 5, + "EletricType": 2, + "NbrBoundary": 14, + "BoundaryFlag": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "NbrDomain": 2, + "domainType": [2, 2], + "domainIndex": [0, 1], + "matType": [0, 0], + "epsilonrR": [1.0, 1.5], + "epsilonrI": [0.0, 0.0], + "murR": [1.0, 1.0], + "murI": [0.0, 0.0], + "chiheR": [0.0, 0.0], + "chiehR": [0.0, 0.0], + "chiheI": [0.0, 0.0], + "chiehI": [0.0, 0.0], + "sigma": [0.0, 0.0], + "n": [1.0, 1.0], + "k": [0.0, 0.0], + "MeshFile": "SBCmesh.dat", + "OutFile": "./OutFile", + "NbrMode": 6, + "searchType": 0, + "searchValue": 374740572.5, + "solverType": 0, + "maxIterNum": 40, + "tol": 1.0e-6, + "tolType": 0, + "lambda0": 0.8, + "lambda": 0.0, + "freq": 0.0 +} diff --git a/3D opticsfem-master/kernel/Assemble_Base.h b/3D opticsfem-master/kernel/Assemble_Base.h index 62a3006..0ef9e54 100644 --- a/3D opticsfem-master/kernel/Assemble_Base.h +++ b/3D opticsfem-master/kernel/Assemble_Base.h @@ -165,6 +165,8 @@ public: void Assemble(); + void Test_OutputMatrix(const std::string& outDir = "./OutFile"); + private: void Assemble_WaveEquation(); void Assemble_PEC_PBC(); diff --git a/3D opticsfem-master/kernel/Assemble_EigenFreq_Equation.cpp b/3D opticsfem-master/kernel/Assemble_EigenFreq_Equation.cpp index edb578a..70399b8 100644 --- a/3D opticsfem-master/kernel/Assemble_EigenFreq_Equation.cpp +++ b/3D opticsfem-master/kernel/Assemble_EigenFreq_Equation.cpp @@ -288,7 +288,7 @@ void OpticsFEM_3D_EigenFreq::Assemble_WaveEquation() //material int domain = _mMesh->GetDomainOfTet(n); Matrix3cd epsr = _mMatLib->GetEpsr(domain); - Matrix3cd invMur = _mMatLib->GetEpsr(domain).inverse(); + Matrix3cd invMur = _mMatLib->GetMur(domain).inverse(); //mapping VectorXi MappingIndex = VectorXi::Zero(dof); diff --git a/3D opticsfem-master/kernel/Assemble_kernel.cpp b/3D opticsfem-master/kernel/Assemble_kernel.cpp index a0a2865..b8c09ab 100644 --- a/3D opticsfem-master/kernel/Assemble_kernel.cpp +++ b/3D opticsfem-master/kernel/Assemble_kernel.cpp @@ -512,10 +512,16 @@ void OpticsFEM_2D_Scatter::Post(string file) } /* -3D_EigenFreq +3D_EigenFreq - volume assembly_equ → sparse A (curl-curl) and B (eps mass) */ void OpticsFEM_3D_EigenFreq::Assemble() { + _mDof = _mMesh->GetNbrEdge(); + _mTripleA_real.clear(); + _mTripleB_real.clear(); + _mTripleA_complex.clear(); + _mTripleB_complex.clear(); + _mIsReal = _mMatLib->IsReal(); if (_mIsReal) { @@ -533,11 +539,15 @@ void OpticsFEM_3D_EigenFreq::Assemble() if (_mIsReal) { + _mA_real.resize(_mDof, _mDof); + _mB_real.resize(_mDof, _mDof); _mA_real.setFromTriplets(_mTripleA_real.begin(), _mTripleA_real.end()); _mB_real.setFromTriplets(_mTripleB_real.begin(), _mTripleB_real.end()); } else { + _mA_complex.resize(_mDof, _mDof); + _mB_complex.resize(_mDof, _mDof); _mA_complex.setFromTriplets(_mTripleA_complex.begin(), _mTripleA_complex.end()); _mB_complex.setFromTriplets(_mTripleB_complex.begin(), _mTripleB_complex.end()); } @@ -546,7 +556,24 @@ void OpticsFEM_3D_EigenFreq::Assemble() { this->Assemble_PEC_PBC(); } + else + { + if (_mIsReal) + { + _mP_real.resize(_mDof, _mDof); + _mP_real.setIdentity(); + } + else + { + _mP_complex.resize(_mDof, _mDof); + _mP_complex.setIdentity(); + } + } + std::cout << "[OpticsFEM_3D_EigenFreq] Assemble done: DOF=" << _mDof + << " nnz(A)=" << (_mIsReal ? _mA_real.nonZeros() : _mA_complex.nonZeros()) + << " nnz(B)=" << (_mIsReal ? _mB_real.nonZeros() : _mB_complex.nonZeros()) + << std::endl; } /* @@ -685,6 +712,53 @@ void OpticsFEM_3D_Scatter::Test_OutputMatrix(const std::string& outDir) } } +void OpticsFEM_3D_EigenFreq::Test_OutputMatrix(const std::string& outDir) +{ + EnsureOutputDir(outDir); + const std::string prefix = outDir + "/"; + + if (_mIsReal) + { + std::ofstream outAi(prefix + "Ai.txt"), outAj(prefix + "Aj.txt"), outAv(prefix + "Av.txt"); + std::ofstream outBi(prefix + "Bi.txt"), outBj(prefix + "Bj.txt"), outBv(prefix + "Bv.txt"); + for (const auto& t : _mTripleA_real) + { + outAi << t.row() << '\n'; + outAj << t.col() << '\n'; + outAv << t.value() << '\n'; + } + for (const auto& t : _mTripleB_real) + { + outBi << t.row() << '\n'; + outBj << t.col() << '\n'; + outBv << t.value() << '\n'; + } + std::cout << "[OpticsFEM_3D_EigenFreq] exported A/B COO to " << outDir << std::endl; + } + else + { + std::ofstream outAi(prefix + "Ai.txt"), outAj(prefix + "Aj.txt"); + std::ofstream outAv_real(prefix + "Av_real.txt"), outAv_imag(prefix + "Av_imag.txt"); + std::ofstream outBi(prefix + "Bi.txt"), outBj(prefix + "Bj.txt"); + std::ofstream outBv_real(prefix + "Bv_real.txt"), outBv_imag(prefix + "Bv_imag.txt"); + for (const auto& t : _mTripleA_complex) + { + outAi << t.row() << '\n'; + outAj << t.col() << '\n'; + outAv_real << t.value().real() << '\n'; + outAv_imag << t.value().imag() << '\n'; + } + for (const auto& t : _mTripleB_complex) + { + outBi << t.row() << '\n'; + outBj << t.col() << '\n'; + outBv_real << t.value().real() << '\n'; + outBv_imag << t.value().imag() << '\n'; + } + std::cout << "[OpticsFEM_3D_EigenFreq] exported A/B COO to " << outDir << std::endl; + } +} + /* 3D_Scatter2 prism diff --git a/3D opticsfem-master/test/Test_Main.cpp b/3D opticsfem-master/test/Test_Main.cpp index 3d49a25..2cec035 100644 --- a/3D opticsfem-master/test/Test_Main.cpp +++ b/3D opticsfem-master/test/Test_Main.cpp @@ -6,7 +6,7 @@ int main() { - std::ifstream fd("sbc3d.json"); + std::ifstream fd("sbc3d.json"); // FemType=4 散射;本征请改为 eigen3d.json 并编译 eigen3D 目录 if (!fd.is_open()) { std::cout << "cannot open sbc3d.json" << std::endl;