XIAN-FEM-2026June/3D opticsfem-master/kernel/Assemble_Scatter_3D_Boundar...

803 lines
24 KiB
C++
Raw 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.

#include"Assemble_Base.h"
#include"Nedelec3D_Util.h"
#include"PBC_Util.h"
#include"SBC_NormalUtil.h"
#include"../function/BF.h"
#include"../function/Gauss.h"
#include"../common/define.h"
#include"../common/util.h"
#include"../material/Material_Base.h"
#include"../phy/Phy_Base.h"
#include"../parser/mpParser.h"
#include<cmath>
#include<complex>
#include<string>
#include<vector>
#include<algorithm>
using namespace Eigen;
// SBC 边界组装:按第三类边界条件 Robin 形式 K += γb += q
//
// 散射场 SBC教材: dE_sc/dn + ik₀ E_sc = 0 → γ = ik₀q = 0
// BELE 算例 (SBCType=0): E_s → Ae(γ) 进 AE_b 的 ABC 切向项 + IBP 面项 → Assemble_BELE() 进 b
// Robin q=0SBC 上不组装 Be(E_inc)
//
// Nedelec 边元上面上的 γ 不能写成单个 K_nn+=γ,需面积分。
// 代码中 Ae(i,j) 来自 assembly_out.mγ=ik₀√ε 在 Nedelec 基上的离散:
// A_ij += ik₀√ε ∫ N_i·(n×(n×N_j)) dS
// 这与标量 K+=γ 是同一 Robin 项在矢量一阶基下的写法,不是体域 Se/Te。
//
// Be 仅用于 SBCType=1 显式入射(已知 q = Einc不用于 BELE。
namespace {
Vector3d crossNormal(const Vector3d& normal, const Vector3d& a)
{
return normal.cross(a.cross(normal));
}
Vector3cd crossNormal(const Vector3d& normal, const Vector3cd& a)
{
return normal.cross(a.cross(normal));
}
double getScalarEps(int domainOfTet, MaterialLib* matLib)
{
return matLib->GetEpsr(domainOfTet)(0, 0).real();
}
bool getFaceEdgeIndices(Mesh_3D* mesh, int triIdx, int outEdges[3])
{
Vector3i conn;
mesh->GetCoonOfTri(triIdx, conn);
const int numTet = conn(0);
const int numFace = conn(1) + 1;
if (numFace == 1)
{
outEdges[0] = mesh->GetEdgeOfTet(numTet, 0);
outEdges[1] = mesh->GetEdgeOfTet(numTet, 1);
outEdges[2] = mesh->GetEdgeOfTet(numTet, 3);
}
else if (numFace == 2)
{
outEdges[0] = mesh->GetEdgeOfTet(numTet, 0);
outEdges[1] = mesh->GetEdgeOfTet(numTet, 2);
outEdges[2] = mesh->GetEdgeOfTet(numTet, 4);
}
else if (numFace == 3)
{
outEdges[0] = mesh->GetEdgeOfTet(numTet, 1);
outEdges[1] = mesh->GetEdgeOfTet(numTet, 2);
outEdges[2] = mesh->GetEdgeOfTet(numTet, 5);
}
else if (numFace == 4)
{
outEdges[0] = mesh->GetEdgeOfTet(numTet, 3);
outEdges[1] = mesh->GetEdgeOfTet(numTet, 4);
outEdges[2] = mesh->GetEdgeOfTet(numTet, 5);
}
else
return false;
return true;
}
void getEdgeGeometry(Mesh_3D* mesh, int edgeId, Vector3d& midpoint, Vector3d& edgeVec)
{
const int v0 = mesh->GetEdge(edgeId, 0);
const int v1 = mesh->GetEdge(edgeId, 1);
Vector3d p0, p1;
mesh->GetVertex(v0, p0);
mesh->GetVertex(v1, p1);
edgeVec = p1 - p0;
midpoint = (p0 + p1) * 0.5;
}
Vector3cd evalE0AtPoint(
mup::ParserX& parser,
mup::Value& xx, mup::Value& yy, mup::Value& zz,
const std::string& E0x, const std::string& E0y, const std::string& E0z,
double x, double y, double z)
{
xx = x;
yy = y;
zz = z;
parser.SetExpr(_T(E0x.c_str()));
mup::Value vx = parser.Eval();
parser.SetExpr(_T(E0y.c_str()));
mup::Value vy = parser.Eval();
parser.SetExpr(_T(E0z.c_str()));
mup::Value vz = parser.Eval();
return Vector3cd(
std::complex<double>(vx.GetFloat(), vx.GetImag()),
std::complex<double>(vy.GetFloat(), vy.GetImag()),
std::complex<double>(vz.GetFloat(), vz.GetImag()));
}
Vector3cd parseEincConstant(
mup::ParserX& parser,
mup::Value& xx, mup::Value& yy, mup::Value& zz,
const std::string& Eincx, const std::string& Eincy, const std::string& Eincz)
{
return evalE0AtPoint(parser, xx, yy, zz, Eincx, Eincy, Eincz, 0.0, 0.0, 0.0);
}
void applyDirichletBCReal(
Eigen::SparseMatrix<double, Eigen::RowMajor>& A,
Eigen::VectorXd& B,
const Eigen::VectorXi& BCIndex,
const Eigen::VectorXcd& BCValue)
{
for (int i = 0; i < BCValue.rows(); i++)
{
Eigen::VectorXd tempValue = Eigen::VectorXd::Zero(A.rows());
tempValue(BCIndex(i)) = 1.0;
B = B - A * tempValue * BCValue(i).real();
}
Eigen::SparseMatrix<double, Eigen::RowMajor> P(A.rows(), A.rows());
std::vector<Eigen::Triplet<double>> tempTriple;
int num = 0;
for (int i = 0; i < A.rows(); i++)
{
if (num < BCIndex.rows() && i == BCIndex(num))
num++;
else
tempTriple.emplace_back(i, i, 1.0);
}
P.setFromTriplets(tempTriple.begin(), tempTriple.end());
A = P * A * P;
tempTriple.clear();
for (int i = 0; i < BCIndex.rows(); i++)
{
tempTriple.emplace_back(BCIndex(i), BCIndex(i), 1.0);
B(BCIndex(i)) = BCValue(i).real();
}
P.setZero();
P.setFromTriplets(tempTriple.begin(), tempTriple.end());
A = A + P;
A.makeCompressed();
}
void applyDirichletBCComplex(
Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor>& A,
Eigen::VectorXcd& B,
const Eigen::VectorXi& BCIndex,
const Eigen::VectorXcd& BCValue)
{
for (int i = 0; i < BCValue.rows(); i++)
{
Eigen::VectorXcd tempValue = Eigen::VectorXcd::Zero(A.rows());
tempValue(BCIndex(i)) = 1.0;
B = B - A * tempValue * BCValue(i);
}
Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor> P(A.rows(), A.rows());
std::vector<Eigen::Triplet<std::complex<double>>> tempTriple;
int num = 0;
for (int i = 0; i < A.rows(); i++)
{
if (num < BCIndex.rows() && i == BCIndex(num))
num++;
else
tempTriple.emplace_back(i, i, 1.0);
}
P.setFromTriplets(tempTriple.begin(), tempTriple.end());
A = P * A * P;
tempTriple.clear();
for (int i = 0; i < BCIndex.rows(); i++)
{
tempTriple.emplace_back(BCIndex(i), BCIndex(i), 1.0);
B(BCIndex(i)) = BCValue(i);
}
P.setZero();
P.setFromTriplets(tempTriple.begin(), tempTriple.end());
A = A + P;
A.makeCompressed();
}
void eliminateDirichletDofsComplex(
Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor>& A,
Eigen::VectorXcd& B,
const Eigen::VectorXi& elimIndices,
std::vector<int>& freeIndicesOut)
{
const int n = static_cast<int>(A.rows());
std::vector<char> elim(static_cast<size_t>(n), 0);
for (int i = 0; i < elimIndices.rows(); i++)
{
const int idx = elimIndices(i);
if (idx >= 0 && idx < n)
elim[static_cast<size_t>(idx)] = 1;
}
freeIndicesOut.clear();
freeIndicesOut.reserve(static_cast<size_t>(n));
for (int i = 0; i < n; i++)
if (!elim[static_cast<size_t>(i)])
freeIndicesOut.push_back(i);
const int nf = static_cast<int>(freeIndicesOut.size());
if (nf == n)
return;
std::vector<int> oldToNew(static_cast<size_t>(n), -1);
for (int i = 0; i < nf; i++)
oldToNew[static_cast<size_t>(freeIndicesOut[static_cast<size_t>(i)])] = i;
std::vector<Eigen::Triplet<std::complex<double>>> trips;
trips.reserve(static_cast<size_t>(A.nonZeros()));
for (int col = 0; col < A.outerSize(); col++)
{
for (Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor>::InnerIterator it(A, col); it; ++it)
{
const int nr = oldToNew[static_cast<size_t>(it.row())];
const int nc = oldToNew[static_cast<size_t>(it.col())];
if (nr >= 0 && nc >= 0)
trips.emplace_back(nr, nc, it.value());
}
}
Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor> Anew(nf, nf);
Anew.setFromTriplets(trips.begin(), trips.end());
A = Anew;
Eigen::VectorXcd Bnew(nf);
for (int i = 0; i < nf; i++)
Bnew(i) = B(freeIndicesOut[static_cast<size_t>(i)]);
B = Bnew;
A.makeCompressed();
}
void physicalPointOnFace(
double u, double v,
const Vector3d& x2, const Vector3d& y2, const Vector3d& z2,
const Vector3d& x3, const Vector3d& y3, const Vector3d& z3,
double& px, double& py, double& pz)
{
const double w = 1.0 - u - v;
const double u2 = x2(0) * u + x2(1) * v + x2(2) * w;
const double v2 = y2(0) * u + y2(1) * v + y2(2) * w;
const double w2 = z2(0) * u + z2(1) * v + z2(2) * w;
px = x3(0) * u2 + x3(1) * v2 + x3(2) * w2;
py = y3(0) * u2 + y3(1) * v2 + y3(2) * w2;
pz = z3(0) * u2 + z3(1) * v2 + z3(2) * w2;
}
void assembleSBCFace(
Mesh_3D* mesh,
MaterialLib* matLib,
std::vector<Triplet<std::complex<double>>>& tripleA,
VectorXcd& B,
double k0,
double* u, double* v, double* wght, int nbrGP,
BF& bfN,
bool isInc,
const Vector3cd& Einc,
int triIdx,
int elementOrder)
{
const int nBfFace = (elementOrder == 2) ? 8 : 3;
const int domain = mesh->GetDomainOfTri(triIdx);
Vector3i conn;
mesh->GetCoonOfTri(triIdx, conn);
const int numTet = conn(0);
const int numFace = conn(1) + 1;
double xv[4], yv[4], zv[4];
for (int i = 0; i < 4; i++)
{
Vector3d vtx;
mesh->GetVertex(mesh->GetTet(numTet, i), vtx);
xv[i] = vtx(0); yv[i] = vtx(1); zv[i] = vtx(2);
}
Vector3d x2, y2, z2;
Vector3d x3, y3, z3;
std::vector<int> bfIndex(nBfFace);
std::vector<int> mappingIndex(nBfFace);
if (elementOrder == 2)
{
if (numFace == 1)
{
x2 << 1, 0, 0; y2 << 0, 1, 0; z2 << 0, 0, 1;
x3 << xv[0], xv[1], xv[2]; y3 << yv[0], yv[1], yv[2]; z3 << zv[0], zv[1], zv[2];
}
else if (numFace == 2)
{
x2 << 1, 0, 0; y2 << 0, 1, 0; z2 << 0, 0, 0;
x3 << xv[0], xv[1], xv[3]; y3 << yv[0], yv[1], yv[3]; z3 << zv[0], zv[1], zv[3];
}
else if (numFace == 3)
{
x2 << 1, 0, 0; y2 << 0, 0, 0; z2 << 0, 1, 0;
x3 << xv[0], xv[2], xv[3]; y3 << yv[0], yv[2], yv[3]; z3 << zv[0], zv[2], zv[3];
}
else if (numFace == 4)
{
x2 << 0, 0, 0; y2 << 1, 0, 0; z2 << 0, 1, 0;
x3 << xv[1], xv[2], xv[3]; y3 << yv[1], yv[2], yv[3]; z3 << zv[1], zv[2], zv[3];
}
else
return;
int bfIdx[8], mapIdx[8];
Nedelec3D::sbcFaceSecondOrderBfIndex(numFace, bfIdx);
Nedelec3D::buildSbcSecondOrderDofMap(mesh, numTet, numFace, mapIdx);
for (int i = 0; i < 8; i++)
{
bfIndex[static_cast<size_t>(i)] = bfIdx[i];
mappingIndex[static_cast<size_t>(i)] = mapIdx[i];
}
}
else if (numFace == 1)
{
x2 << 1, 0, 0; y2 << 0, 1, 0; z2 << 0, 0, 1;
x3 << xv[0], xv[1], xv[2]; y3 << yv[0], yv[1], yv[2]; z3 << zv[0], zv[1], zv[2];
bfIndex[0] = 1; bfIndex[1] = 2; bfIndex[2] = 4;
mappingIndex[0] = mesh->GetEdgeOfTet(numTet, 0);
mappingIndex[1] = mesh->GetEdgeOfTet(numTet, 1);
mappingIndex[2] = mesh->GetEdgeOfTet(numTet, 3);
}
else if (numFace == 2)
{
x2 << 1, 0, 0; y2 << 0, 1, 0; z2 << 0, 0, 0;
x3 << xv[0], xv[1], xv[3]; y3 << yv[0], yv[1], yv[3]; z3 << zv[0], zv[1], zv[3];
bfIndex[0] = 1; bfIndex[1] = 3; bfIndex[2] = 5;
mappingIndex[0] = mesh->GetEdgeOfTet(numTet, 0);
mappingIndex[1] = mesh->GetEdgeOfTet(numTet, 2);
mappingIndex[2] = mesh->GetEdgeOfTet(numTet, 4);
}
else if (numFace == 3)
{
x2 << 1, 0, 0; y2 << 0, 0, 0; z2 << 0, 1, 0;
x3 << xv[0], xv[2], xv[3]; y3 << yv[0], yv[2], yv[3]; z3 << zv[0], zv[2], zv[3];
bfIndex[0] = 2; bfIndex[1] = 3; bfIndex[2] = 6;
mappingIndex[0] = mesh->GetEdgeOfTet(numTet, 1);
mappingIndex[1] = mesh->GetEdgeOfTet(numTet, 2);
mappingIndex[2] = mesh->GetEdgeOfTet(numTet, 5);
}
else if (numFace == 4)
{
x2 << 0, 0, 0; y2 << 1, 0, 0; z2 << 0, 1, 0;
x3 << xv[1], xv[2], xv[3]; y3 << yv[1], yv[2], yv[3]; z3 << zv[1], zv[2], zv[3];
bfIndex[0] = 4; bfIndex[1] = 5; bfIndex[2] = 6;
mappingIndex[0] = mesh->GetEdgeOfTet(numTet, 3);
mappingIndex[1] = mesh->GetEdgeOfTet(numTet, 4);
mappingIndex[2] = mesh->GetEdgeOfTet(numTet, 5);
}
else
{
return;
}
Matrix3d Jac;
Jac(0, 0) = xv[0] - xv[3]; Jac(0, 1) = yv[0] - yv[3]; Jac(0, 2) = zv[0] - zv[3];
Jac(1, 0) = xv[1] - xv[3]; Jac(1, 1) = yv[1] - yv[3]; Jac(1, 2) = zv[1] - zv[3];
Jac(2, 0) = xv[2] - xv[3]; Jac(2, 1) = yv[2] - yv[3]; Jac(2, 2) = zv[2] - zv[3];
const double detJ = Jac.determinant();
if (std::abs(detJ) < 1e-30)
return;
const Matrix3d InvJac = Jac.inverse();
const double a = std::sqrt((x3(0) - x3(1)) * (x3(0) - x3(1)) + (y3(0) - y3(1)) * (y3(0) - y3(1)) + (z3(0) - z3(1)) * (z3(0) - z3(1)));
const double b = std::sqrt((x3(0) - x3(2)) * (x3(0) - x3(2)) + (y3(0) - y3(2)) * (y3(0) - y3(2)) + (z3(0) - z3(2)) * (z3(0) - z3(2)));
const double c = std::sqrt((x3(1) - x3(2)) * (x3(1) - x3(2)) + (y3(1) - y3(2)) * (y3(1) - y3(2)) + (z3(1) - z3(2)) * (z3(1) - z3(2)));
double heron = (a + b + c) * (a + b - c) * (a - b + c) * (b + c - a);
if (heron < 0.0) heron = 0.0;
const double integCoe = 0.25 * std::sqrt(heron);
Vector3d meshNorm;
mesh->GetNormOfFace(domain, meshNorm);
const Vector3d faceP0(x3(0), y3(0), z3(0));
const Vector3d faceP1(x3(1), y3(1), z3(1));
const Vector3d faceP2(x3(2), y3(2), z3(2));
const Vector3d normal = OpticsFEM::computeScatterSBCNormal(
isInc, domain, xv, yv, faceP0, faceP1, faceP2, &meshNorm);
std::vector<std::vector<Vector3d>> E(static_cast<size_t>(nbrGP), std::vector<Vector3d>(nBfFace));
for (int gp = 0; gp < nbrGP; gp++)
{
const double wgp = 1.0 - u[gp] - v[gp];
const double u2 = x2(0) * u[gp] + x2(1) * v[gp] + x2(2) * wgp;
const double v2 = y2(0) * u[gp] + y2(1) * v[gp] + y2(2) * wgp;
const double w2 = z2(0) * u[gp] + z2(1) * v[gp] + z2(2) * wgp;
for (int j = 0; j < nBfFace; j++)
{
bfN.GetValueBF(bfIndex[static_cast<size_t>(j)], u2, v2, w2, E[static_cast<size_t>(gp)][static_cast<size_t>(j)]);
E[static_cast<size_t>(gp)][static_cast<size_t>(j)] = InvJac * E[static_cast<size_t>(gp)][static_cast<size_t>(j)];
}
}
const int tetDomain = mesh->GetDomainOfTet(numTet);
const double eps = getScalarEps(tetDomain, matLib);
const std::complex<double> nn = std::sqrt(std::complex<double>(eps, 0.0));
const std::complex<double> iUnit(0.0, 1.0);
MatrixXcd Ae = MatrixXcd::Zero(nBfFace, nBfFace);
for (int i = 0; i < nBfFace; i++)
{
for (int j = 0; j < nBfFace; j++)
{
for (int gp = 0; gp < nbrGP; gp++)
{
const Vector3d tEj = crossNormal(normal, E[static_cast<size_t>(gp)][static_cast<size_t>(j)]);
Ae(i, j) += iUnit * k0 * nn * integCoe * wght[gp]
* E[static_cast<size_t>(gp)][static_cast<size_t>(i)].dot(tEj) * 2.0;
}
}
}
std::vector<std::complex<double>> Be(static_cast<size_t>(nBfFace), 0.0);
if (isInc)
{
const Vector3cd tEinc = crossNormal(normal, Einc);
for (int i = 0; i < nBfFace; i++)
{
for (int gp = 0; gp < nbrGP; gp++)
{
const std::complex<double> dotE =
E[static_cast<size_t>(gp)][static_cast<size_t>(i)](0) * tEinc(0)
+ E[static_cast<size_t>(gp)][static_cast<size_t>(i)](1) * tEinc(1)
+ E[static_cast<size_t>(gp)][static_cast<size_t>(i)](2) * tEinc(2);
Be[static_cast<size_t>(i)] -= iUnit * k0 * nn * 2.0 * integCoe * wght[gp] * dotE * 2.0;
}
}
}
for (int i = 0; i < nBfFace; i++)
{
for (int j = 0; j < nBfFace; j++)
tripleA.emplace_back(mappingIndex[static_cast<size_t>(i)], mappingIndex[static_cast<size_t>(j)], Ae(i, j));
if (isInc)
B(mappingIndex[static_cast<size_t>(i)]) += Be[static_cast<size_t>(i)];
}
}
void assembleSBCByType(
Mesh_3D* mesh,
MaterialLib* matLib,
Phy_WaveOpticsModel* phy,
std::vector<Triplet<std::complex<double>>>& tripleA,
VectorXcd& B,
double k0,
int sbcTypeFilter,
bool isInc,
const Vector3cd& Einc)
{
const int elementOrder = phy->GetElementOrder();
const int gaussTriOrder = Nedelec3D::gaussOrderTri(elementOrder);
const int bfOrder = Nedelec3D::bfOrderParam(elementOrder);
Gauss gauss;
const int nbrGP = gauss.GetNbrGaussPoints(TWODIM, TRIANGLE, gaussTriOrder);
double* u = new double[nbrGP];
double* v = new double[nbrGP];
double* w = new double[nbrGP];
double* wght = new double[nbrGP];
gauss.GetGaussPoints(TWODIM, TRIANGLE, u, v, w, wght);
BF bfN;
bfN.GetNbrBF(THREEDIM, TETRAHEDRON, BF_NEDELEC, bfOrder);
const int nbrSBC = phy->GetNbrSBC();
for (int s = 0; s < nbrSBC; s++)
{
if (phy->GetSBCType(s) != sbcTypeFilter)
continue;
VectorXi triIndices;
mesh->GetTriIndicesOfDomain(phy->GetSBCDomain(s), triIndices);
for (int n = 0; n < triIndices.size(); n++)
{
assembleSBCFace(mesh, matLib, tripleA, B, k0, u, v, wght, nbrGP, bfN,
isInc, Einc, triIndices(n), elementOrder);
}
}
delete[] u;
delete[] v;
delete[] w;
delete[] wght;
}
void assembleRobinOnDomains(
Mesh_3D* mesh,
MaterialLib* matLib,
std::vector<Triplet<std::complex<double>>>& tripleA,
VectorXcd& B,
double k0,
const Eigen::VectorXi& domains)
{
Gauss gauss;
const int nbrGP = gauss.GetNbrGaussPoints(TWODIM, TRIANGLE, BF_LINEFUNC * 2);
double* u = new double[nbrGP];
double* v = new double[nbrGP];
double* w = new double[nbrGP];
double* wght = new double[nbrGP];
gauss.GetGaussPoints(TWODIM, TRIANGLE, u, v, w, wght);
BF bfN;
bfN.GetNbrBF(THREEDIM, TETRAHEDRON, BF_NEDELEC, BF_LINEFUNC);
for (int d = 0; d < domains.rows(); d++)
{
VectorXi triIndices;
mesh->GetTriIndicesOfDomain(domains(d), triIndices);
for (int n = 0; n < triIndices.size(); n++)
{
assembleSBCFace(mesh, matLib, tripleA, B, k0, u, v, wght, nbrGP, bfN,
false, Vector3cd::Zero(), triIndices(n), 1);
}
}
delete[] u;
delete[] v;
delete[] w;
delete[] wght;
}
void collectBoundaryEdgesOnDomains(
Mesh_3D* mesh,
const Eigen::VectorXi& domains,
std::vector<int>& edgeIds)
{
for (int i = 0; i < domains.rows(); i++)
{
Eigen::VectorXi triIndices;
mesh->GetTriIndicesOfDomain(domains(i), triIndices);
for (int t = 0; t < triIndices.size(); t++)
{
int faceEdges[3];
if (!getFaceEdgeIndices(mesh, triIndices(t), faceEdges))
continue;
for (int n = 0; n < 3; n++)
edgeIds.push_back(faceEdges[n]);
}
}
std::sort(edgeIds.begin(), edgeIds.end());
edgeIds.erase(std::unique(edgeIds.begin(), edgeIds.end()), edgeIds.end());
}
void collectBoundaryFacesOnDomains(
Mesh_3D* mesh,
const Eigen::VectorXi& domains,
std::vector<int>& faceIds)
{
for (int i = 0; i < domains.rows(); i++)
{
Eigen::VectorXi triIndices;
mesh->GetTriIndicesOfDomain(domains(i), triIndices);
for (int t = 0; t < triIndices.size(); t++)
{
Eigen::Vector3i conn;
mesh->GetCoonOfTri(triIndices(t), conn);
faceIds.push_back(mesh->GetFaceOfTet(conn(0), conn(1)));
}
}
std::sort(faceIds.begin(), faceIds.end());
faceIds.erase(std::unique(faceIds.begin(), faceIds.end()), faceIds.end());
}
void collectPecDofIndices(
Mesh_3D* mesh,
const Eigen::VectorXi& pecDomains,
int elementOrder,
std::vector<int>& dofIndices)
{
std::vector<int> pecEdges;
collectBoundaryEdgesOnDomains(mesh, pecDomains, pecEdges);
const int nE = mesh->GetNbrEdge();
const int nF = mesh->GetNbrFace();
for (int edgeId : pecEdges)
{
dofIndices.push_back(Nedelec3D::edgeGlobalDof(edgeId, 0, nE));
if (elementOrder == 2)
dofIndices.push_back(Nedelec3D::edgeGlobalDof(edgeId, 1, nE));
}
if (elementOrder == 2)
{
std::vector<int> pecFaces;
collectBoundaryFacesOnDomains(mesh, pecDomains, pecFaces);
for (int faceId : pecFaces)
{
dofIndices.push_back(Nedelec3D::faceGlobalDof(faceId, 0, nE, nF));
dofIndices.push_back(Nedelec3D::faceGlobalDof(faceId, 1, nE, nF));
}
}
std::sort(dofIndices.begin(), dofIndices.end());
dofIndices.erase(std::unique(dofIndices.begin(), dofIndices.end()), dofIndices.end());
}
} // namespace
void OpticsFEM_3D_Scatter::Assemble_SBC()
{
const double k0 = 2.0 * Pi / _mSolver->GetLda0();
// SBCType=0: γ=ik₀q=0 → 只修正 A
assembleSBCByType(_mMesh, _mMatLib, _mPhy, _mTripleA_complex, _mB_complex,
k0, 0, false, Vector3cd::Zero());
Vector3cd Einc = Vector3cd::Zero();
for (int s = 0; s < _mPhy->GetNbrSBC(); s++)
{
if (_mPhy->GetSBCType(s) != 1)
continue;
std::string sx, sy, sz;
_mPhy->GetEinc(s, sx, sy, sz);
mup::ParserX parser(mup::pckALL_COMPLEX);
parser.EnableAutoCreateVar(true);
mup::Value xx(0.0), yy(0.0), zz(0.0);
parser.DefineVar(_T("x"), mup::Variable(&xx));
parser.DefineVar(_T("y"), mup::Variable(&yy));
parser.DefineVar(_T("z"), mup::Variable(&zz));
Einc = parseEincConstant(parser, xx, yy, zz, sx, sy, sz);
break;
}
assembleSBCByType(_mMesh, _mMatLib, _mPhy, _mTripleA_complex, _mB_complex,
k0, 1, true, Einc);
}
void OpticsFEM_3D_Scatter::Assemble_PEC_ELE()
{
const int elementOrder = _mPhy->GetElementOrder();
const int nbrPEC = _mPhy->GetNbrPEC();
const int nbrELE = _mPhy->GetNbrElE();
if (nbrPEC == 0 && nbrELE == 0)
return;
if (nbrELE > 0 && elementOrder == 2)
{
std::cerr << "[OpticsFEM] ElementOrder=2: ELE uses edge DOFs (×2) + zero face DOFs "
<< "(midpoint tangential projection)." << std::endl;
}
std::vector<int> pecDofs;
if (nbrPEC > 0)
{
Eigen::VectorXi PECDomain = Eigen::VectorXi::Zero(nbrPEC);
for (int i = 0; i < nbrPEC; i++)
PECDomain(i) = _mPhy->GetPECDomain(i) + 1;
collectPecDofIndices(_mMesh, PECDomain, elementOrder, pecDofs);
}
Eigen::VectorXi triIndexOfELE, triNumOfELE;
if (nbrELE > 0)
{
Eigen::VectorXi ELEDomain = Eigen::VectorXi::Zero(nbrELE);
Eigen::VectorXi indexNum = Eigen::VectorXi::Zero(nbrELE);
for (int i = 0; i < nbrELE; i++)
{
ELEDomain(i) = _mPhy->GetELEDomain(i);
indexNum(i) = i;
}
_mMesh->GetTriIndexOfDomain2(ELEDomain, indexNum, triIndexOfELE, triNumOfELE);
}
const int dofsPerEleFace = (elementOrder == 2) ? 8 : 3;
const int nbrELEEdges = (nbrELE > 0) ? static_cast<int>(triIndexOfELE.size()) * dofsPerEleFace : 0;
const int nbrPECDofs = (nbrPEC > 0) ? static_cast<int>(pecDofs.size()) : 0;
const int nbrBC = nbrPECDofs + nbrELEEdges;
if (nbrBC == 0)
return;
Eigen::VectorXi BCIndex = Eigen::VectorXi::Zero(nbrBC);
Eigen::VectorXcd BCValue = Eigen::VectorXcd::Zero(nbrBC);
mup::ParserX parser(mup::pckALL_COMPLEX);
parser.EnableAutoCreateVar(true);
mup::Value xx, yy, zz;
parser.DefineVar(_T("x"), mup::Variable(&xx));
parser.DefineVar(_T("y"), mup::Variable(&yy));
parser.DefineVar(_T("z"), mup::Variable(&zz));
int bcOffset = 0;
for (int m = 0; m < triIndexOfELE.size(); m++)
{
int faceEdges[3];
if (!getFaceEdgeIndices(_mMesh, triIndexOfELE(m), faceEdges))
continue;
std::string E0x, E0y, E0z;
_mPhy->GetE0(E0x, E0y, E0z, triNumOfELE(m));
std::string ExFunc = E0x, EyFunc = E0y, EzFunc = E0z;
if (_mPhy->GetNbrBELE() > 0)
{
std::string Ebx, Eby, Ebz;
_mPhy->GetEb(Ebx, Eby, Ebz);
ExFunc = E0x + "-" + Ebx;
EyFunc = E0y + "-" + Eby;
EzFunc = E0z + "-" + Ebz;
}
const int nE = _mMesh->GetNbrEdge();
if (elementOrder == 2)
{
Eigen::Vector3i conn;
_mMesh->GetCoonOfTri(triIndexOfELE(m), conn);
int mapIdx[8];
Nedelec3D::buildSbcSecondOrderDofMap(_mMesh, conn(0), conn(1) + 1, mapIdx);
for (int n = 0; n < 3; n++)
{
Vector3d midpoint, edgeVec;
getEdgeGeometry(_mMesh, faceEdges[n], midpoint, edgeVec);
xx = midpoint(0);
yy = midpoint(1);
zz = midpoint(2);
const Vector3cd E0 = evalE0AtPoint(parser, xx, yy, zz, ExFunc, EyFunc, EzFunc,
midpoint(0), midpoint(1), midpoint(2));
const std::complex<double> edgeVal = E0.dot(edgeVec);
BCIndex(bcOffset + n) = mapIdx[n];
BCValue(bcOffset + n) = edgeVal;
BCIndex(bcOffset + 3 + n) = mapIdx[3 + n];
BCValue(bcOffset + 3 + n) = edgeVal;
}
BCIndex(bcOffset + 6) = mapIdx[6];
BCValue(bcOffset + 6) = 0.0;
BCIndex(bcOffset + 7) = mapIdx[7];
BCValue(bcOffset + 7) = 0.0;
bcOffset += 8;
(void)nE;
}
else
{
for (int n = 0; n < 3; n++)
{
Vector3d midpoint, edgeVec;
getEdgeGeometry(_mMesh, faceEdges[n], midpoint, edgeVec);
xx = midpoint(0);
yy = midpoint(1);
zz = midpoint(2);
const Vector3cd E0 = evalE0AtPoint(parser, xx, yy, zz, ExFunc, EyFunc, EzFunc,
midpoint(0), midpoint(1), midpoint(2));
BCIndex(bcOffset + n) = faceEdges[n];
BCValue(bcOffset + n) = E0.dot(edgeVec);
}
bcOffset += 3;
}
}
for (int i = 0; i < nbrPECDofs; i++)
{
BCIndex(bcOffset + i) = pecDofs[static_cast<size_t>(i)];
BCValue(bcOffset + i) = 0.0;
}
Eigen::VectorXi tempIndex = Eigen::VectorXi::LinSpaced(nbrBC, 0, nbrBC - 1);
QuickSort(BCIndex, tempIndex, 0, nbrBC - 1);
Unique(BCIndex, tempIndex);
Eigen::VectorXcd tempValue = Eigen::VectorXcd::Zero(BCIndex.rows());
for (int i = 0; i < BCIndex.rows(); i++)
tempValue(i) = BCValue(tempIndex(i));
BCValue = tempValue;
// Port + PEC-only: match MATLAB FemMatrixAssembly (eliminate PEC DOFs before solve).
// Do NOT use applyDirichletBC here — it breaks the augmented port system.
if (!_mIsReal && _mPhy->HasNumericPort() && nbrELE == 0 && nbrPECDofs > 0)
{
eliminateDirichletDofsComplex(_mA_complex, _mB_complex, BCIndex, _mFreeDofIndices);
return;
}
if (_mIsReal)
applyDirichletBCReal(_mA_real, _mB_real, BCIndex, BCValue);
else
applyDirichletBCComplex(_mA_complex, _mB_complex, BCIndex, BCValue);
}
void OpticsFEM_3D_Scatter::Assemble_PBC()
{
if (_mPhy->GetNbrPBCGroups() <= 0)
return;
const int elementOrder = _mPhy->GetElementOrder();
const int dof = Nedelec3D::globalDofCount(_mMesh, elementOrder);
assemblePbcProjectionMatrix(_mMesh, _mPhy, elementOrder, dof, _mIsReal, _mP_real, _mP_complex);
}