803 lines
24 KiB
C++
803 lines
24 KiB
C++
#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(γ) 进 A;E_b 的 ABC 切向项 + IBP 面项 → Assemble_BELE() 进 b
|
||
// Robin q=0:SBC 上不组装 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);
|
||
}
|