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

312 lines
11 KiB
C++

#include "Assemble_Base.h"
#include "../function/BF.h"
#include "../function/Gauss.h"
#include "../common/define.h"
#include "../material/Material_Base.h"
#include "../phy/PortModeData.h"
#include <cmath>
#include <complex>
#include <iostream>
#include <vector>
using namespace Eigen;
namespace {
Vector3cd crossMuN(const Vector3d& n, const Vector3cd& a)
{
return n.cross(a);
}
// MATLAB PortBCMatrixAssembly: sum(Alphaj .* temp) — no conjugate on either factor.
std::complex<double> matlabSumProduct(const Vector3d& aReal, const Vector3cd& b)
{
return std::complex<double>(aReal(0), 0.0) * b(0)
+ std::complex<double>(aReal(1), 0.0) * b(1)
+ std::complex<double>(aReal(2), 0.0) * b(2);
}
void getTetFaceData(
Mesh_3D* mesh, int numEle, int numFace,
int faceNode[3], int faceBF[3],
double x[4], double y[4], double z[4], double l[6])
{
for (int i = 0; i < 4; i++)
{
Vector3d v;
mesh->GetVertex(mesh->GetTet(numEle, i), v);
x[i] = v(0); y[i] = v(1); z[i] = v(2);
}
auto edgeLen = [](double x1, double y1, double z1, double x2, double y2, double z2) {
return std::sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) + (z1 - z2) * (z1 - z2));
};
l[0] = edgeLen(x[0], y[0], z[0], x[1], y[1], z[1]);
l[1] = edgeLen(x[0], y[0], z[0], x[2], y[2], z[2]);
l[2] = edgeLen(x[0], y[0], z[0], x[3], y[3], z[3]);
l[3] = edgeLen(x[1], y[1], z[1], x[2], y[2], z[2]);
l[4] = edgeLen(x[1], y[1], z[1], x[3], y[3], z[3]);
l[5] = edgeLen(x[2], y[2], z[2], x[3], y[3], z[3]);
if (numFace == 1) { faceNode[0] = 0; faceNode[1] = 1; faceNode[2] = 2; faceBF[0] = 0; faceBF[1] = 1; faceBF[2] = 3; }
else if (numFace == 2) { faceNode[0] = 0; faceNode[1] = 1; faceNode[2] = 3; faceBF[0] = 0; faceBF[1] = 2; faceBF[2] = 4; }
else if (numFace == 3) { faceNode[0] = 0; faceNode[1] = 2; faceNode[2] = 3; faceBF[0] = 1; faceBF[1] = 2; faceBF[2] = 5; }
else { faceNode[0] = 1; faceNode[1] = 2; faceNode[2] = 3; faceBF[0] = 3; faceBF[1] = 4; faceBF[2] = 5; }
}
void assembleOnePortFace(
Mesh_3D* mesh, MaterialLib* matLib,
const PortNumericMode& mode, int faceIdx,
VectorXcd& G, VectorXcd& S_or_T, VectorXcd& bEdge,
std::complex<double>& P, std::complex<double>& b1,
bool isInput)
{
const int numEle = mode.facesConn(faceIdx, 0) - 1;
const int numFace = mode.facesConn(faceIdx, 1);
int faceNode[3], faceBF[3];
double x[4], y[4], z[4], l[6];
getTetFaceData(mesh, numEle, numFace, faceNode, faceBF, x, y, z, l);
const int domain = mesh->GetDomainOfTet(numEle);
const std::complex<double> mu = 1.0 / matLib->GetMur(domain)(0, 0);
Matrix3d Jac;
Jac(0, 0) = x[0] - x[3]; Jac(0, 1) = y[0] - y[3]; Jac(0, 2) = z[0] - z[3];
Jac(1, 0) = x[1] - x[3]; Jac(1, 1) = y[1] - y[3]; Jac(1, 2) = z[1] - z[3];
Jac(2, 0) = x[2] - x[3]; Jac(2, 1) = y[2] - y[3]; Jac(2, 2) = z[2] - z[3];
const Matrix3d InvJac = Jac.inverse();
double xx[3], yy[3], zz[3];
for (int i = 0; i < 3; i++)
{
xx[i] = x[faceNode[i]];
yy[i] = y[faceNode[i]];
zz[i] = z[faceNode[i]];
}
Matrix3d fJac = Matrix3d::Zero();
fJac(0, 0) = -xx[0] + xx[1]; fJac(0, 1) = -yy[0] + yy[1];
fJac(1, 0) = -xx[0] + xx[2]; fJac(1, 1) = -yy[0] + yy[2]; fJac(2, 2) = 1.0;
const Matrix3d InvfJac = fJac.inverse();
Matrix3d fJacS = Matrix3d::Zero();
fJacS(0, 0) = InvfJac(1, 1); fJacS(0, 1) = -InvfJac(1, 0);
fJacS(1, 0) = -InvfJac(0, 1); fJacS(1, 1) = InvfJac(0, 0); fJacS(2, 2) = 1.0;
const Matrix3d fTJac = fJac.transpose() / fJac.determinant();
const double fDetJac = std::abs(fJac.determinant());
Gauss gauss;
BF bfEt, bfEz, bfEdge, bfCurlEt, bfCurlEz;
bfEt.GetNbrBF(TWODIM, TRIANGLE, BF_NEDELEC, BF_LINEFUNC);
bfEz.GetNbrBF(TWODIM, TRIANGLE, BF_LAGRANGE, BF_LINEFUNC);
bfEdge.GetNbrBF(THREEDIM, TETRAHEDRON, BF_NEDELEC, BF_LINEFUNC);
bfCurlEt.GetNbrBF(TWODIM, TRIANGLE, BF_CURL_NEDELEC, BF_LINEFUNC);
bfCurlEz.GetNbrBF(TWODIM, TRIANGLE, BF_CURL_LAGRANGE, BF_LINEFUNC);
const int nGP = gauss.GetNbrGaussPoints(TWODIM, TRIANGLE, BF_LINEFUNC * 2);
std::vector<double> u(nGP), v(nGP), w(nGP), wght(nGP);
gauss.GetGaussPoints(TWODIM, TRIANGLE, u.data(), v.data(), w.data(), wght.data());
Matrix2d fJac2;
fJac2(0, 0) = -xx[0] + xx[1]; fJac2(0, 1) = -yy[0] + yy[1];
fJac2(1, 0) = -xx[0] + xx[2]; fJac2(1, 1) = -yy[0] + yy[2];
const Matrix3d vJac = Jac.inverse();
for (int gp = 0; gp < nGP; gp++)
{
Vector3cd fE = Vector3cd::Zero();
Vector3cd fcurlE = Vector3cd::Zero();
for (int i = 0; i < 3; i++)
{
Vector3d tempEt, tempGradEz, tempCurlEt, tempEz;
bfEt.GetValueBF(i + 1, u[gp], v[gp], 0.0, tempEt);
Vector3d et = InvfJac * tempEt * l[faceBF[i]];
bfEz.GetValueBF(i + 1, u[gp], v[gp], 0.0, tempEz);
Vector3d ezVec(0, 0, tempEz(2));
bfCurlEt.GetValueBF(i + 1, u[gp], v[gp], 0.0, tempCurlEt);
Vector3d curlEtVec = fTJac * Vector3d(0, 0, tempCurlEt(2)) * l[faceBF[i]];
bfCurlEz.GetValueBF(i + 1, u[gp], v[gp], 0.0, tempGradEz);
Vector3d curlEzVec = fJacS * Vector3d(tempGradEz(0), tempGradEz(1), 0.0);
const int mapEt = mode.portEdgeOfFace(faceIdx, i);
const int mapEz = mode.portNewFaces(faceIdx, i);
fE += et.cast<std::complex<double>>() * mode.Et(mapEt)
+ ezVec.cast<std::complex<double>>() * mode.Ez(mapEz);
fcurlE += curlEtVec.cast<std::complex<double>>() * mode.Et(mapEt)
+ curlEzVec.cast<std::complex<double>>() * mode.Ez(mapEz);
}
Vector3cd fN0, fcurlN0, fN1, fcurlN1, fN2, fcurlN2, fW;
if (isInput)
{
fN0 = fE * mode.powerCoef;
fcurlN0 = (fcurlE + mode.gamma * Vector3cd(fE(1), -fE(0), 0.0)) * mode.powerCoef;
fN1 = fE;
fcurlN1 = fcurlE - mode.gamma * Vector3cd(fE(1), -fE(0), 0.0);
}
else
{
fN2 = fE;
fcurlN2 = fcurlE + mode.gamma * Vector3cd(fE(1), -fE(0), 0.0);
}
fW = fE;
Vector3d alphaPhys[3];
for (int i = 0; i < 3; i++)
{
// Match MATLAB: temp = [u,v]*fJac2 + [xx(1),yy(1),zz(1)]
Vector3d refPt(
fJac2(0, 0) * u[gp] + fJac2(1, 0) * v[gp] + xx[0],
fJac2(0, 1) * u[gp] + fJac2(1, 1) * v[gp] + yy[0],
zz[0]);
// Match MATLAB: temp = [u,v]*fJac2 + xyz; ref3 = temp*vJac - x4*vJac (row-vector multiply)
Vector3d ref3 = vJac.transpose() * refPt - vJac.transpose() * Vector3d(x[3], y[3], z[3]);
Vector3d bf;
bfEdge.GetValueBF(faceBF[i] + 1, ref3(0), ref3(1), ref3(2), bf);
alphaPhys[i] = InvJac * bf * l[faceBF[i]];
}
const int mappingEdge[3] = {
mesh->GetEdgeOfTet(numEle, faceBF[0]),
mesh->GetEdgeOfTet(numEle, faceBF[1]),
mesh->GetEdgeOfTet(numEle, faceBF[2]) };
if (isInput)
{
for (int i = 0; i < 3; i++)
{
G(mappingEdge[i]) += wght[gp] * fDetJac * (fW(0) * alphaPhys[i](0) + fW(1) * alphaPhys[i](1));
const Vector3cd temp = mu * crossMuN(mode.normal, fcurlN1);
S_or_T(mappingEdge[i]) += wght[gp] * fDetJac * matlabSumProduct(alphaPhys[i], temp);
const Vector3cd temp0 = mu * crossMuN(mode.normal, fcurlN0.conjugate());
bEdge(mappingEdge[i]) += wght[gp] * fDetJac * matlabSumProduct(alphaPhys[i], temp0);
}
P += wght[gp] * fDetJac * (fW(0) * fN1(0) + fW(1) * fN1(1));
b1 += wght[gp] * fDetJac * (fW(0) * fN0(0) + fW(1) * fN0(1));
}
else
{
for (int i = 0; i < 3; i++)
{
G(mappingEdge[i]) += wght[gp] * fDetJac * (fW(0) * alphaPhys[i](0) + fW(1) * alphaPhys[i](1));
const Vector3cd temp = mu * crossMuN(mode.normal, fcurlN2);
S_or_T(mappingEdge[i]) += wght[gp] * fDetJac * matlabSumProduct(alphaPhys[i], temp);
}
P += wght[gp] * fDetJac * (fW(0) * fN2(0) + fW(1) * fN2(1));
}
}
}
void blockAppendInputPort(
SparseMatrix<std::complex<double>, RowMajor>& A,
VectorXcd& b,
int edgeDof,
const VectorXcd& S, const VectorXcd& G,
std::complex<double> P, std::complex<double> b1)
{
const int n = static_cast<int>(b.size());
SparseMatrix<std::complex<double>, RowMajor> Aold = A;
A = SparseMatrix<std::complex<double>, RowMajor>(n + 1, n + 1);
std::vector<Triplet<std::complex<double>>> trips;
for (int k = 0; k < Aold.outerSize(); k++)
for (SparseMatrix<std::complex<double>, RowMajor>::InnerIterator it(Aold, k); it; ++it)
trips.emplace_back(it.row(), it.col(), it.value());
for (int i = 0; i < edgeDof; i++)
if (std::abs(S(i)) > 0)
// Port column must match MATLAB PortBC (not Hermitian transpose).
trips.emplace_back(i, n, std::conj(S(i)));
for (int i = 0; i < edgeDof; i++)
if (std::abs(G(i)) > 0)
trips.emplace_back(n, i, G(i));
trips.emplace_back(n, n, -P);
A.setFromTriplets(trips.begin(), trips.end());
VectorXcd bnew(n + 1);
bnew.head(n) = -b;
bnew(n) = b1;
b = bnew;
}
void blockAppendOutputPort(
SparseMatrix<std::complex<double>, RowMajor>& A,
VectorXcd& b,
int edgeDof,
const VectorXcd& T, const VectorXcd& G,
std::complex<double> P)
{
const int n = static_cast<int>(b.size());
SparseMatrix<std::complex<double>, RowMajor> Aold = A;
A = SparseMatrix<std::complex<double>, RowMajor>(n + 1, n + 1);
std::vector<Triplet<std::complex<double>>> trips;
for (int k = 0; k < Aold.outerSize(); k++)
for (SparseMatrix<std::complex<double>, RowMajor>::InnerIterator it(Aold, k); it; ++it)
trips.emplace_back(it.row(), it.col(), it.value());
for (int i = 0; i < edgeDof; i++)
if (std::abs(T(i)) > 0)
trips.emplace_back(i, n, std::conj(T(i)));
for (int i = 0; i < edgeDof; i++)
if (std::abs(G(i)) > 0)
trips.emplace_back(n, i, G(i));
trips.emplace_back(n, n, -P);
A.setFromTriplets(trips.begin(), trips.end());
VectorXcd bnew(n + 1);
bnew.head(n) = b;
bnew(n) = 0.0;
b = bnew;
}
} // namespace
void OpticsFEM_3D_Scatter::Assemble_Port()
{
if (!_mPhy->HasNumericPort())
return;
_mIsReal = false;
const int dof = _mDof;
for (int p = 0; p < _mPhy->GetNbrNumericPort(); p++)
{
const PortNumericMode& mode = _mPhy->GetNumericPort(p);
const bool isInput = (mode.type == 1);
const int nf = mode.facesConn.rows();
VectorXcd G = VectorXcd::Zero(dof);
VectorXcd S_or_T = VectorXcd::Zero(dof);
VectorXcd bEdge = VectorXcd::Zero(dof);
std::complex<double> P(0.0, 0.0);
std::complex<double> b1(0.0, 0.0);
for (int f = 0; f < nf; f++)
assembleOnePortFace(_mMesh, _mMatLib, mode, f, G, S_or_T, bEdge, P, b1, isInput);
if (isInput)
{
_mB_complex += bEdge;
std::cout << "[OpticsFEM_3D_Scatter] Port input"
<< " |S|=" << S_or_T.norm()
<< " |G|=" << G.norm()
<< " |bEdge|=" << bEdge.norm()
<< " |P|=" << std::abs(P)
<< " b1=" << b1 << std::endl;
blockAppendInputPort(_mA_complex, _mB_complex, dof, S_or_T, G, P, b1);
}
else
{
std::cout << "[OpticsFEM_3D_Scatter] Port output"
<< " |T|=" << S_or_T.norm()
<< " |G|=" << G.norm()
<< " |P|=" << std::abs(P) << std::endl;
blockAppendOutputPort(_mA_complex, _mB_complex, dof, S_or_T, G, P);
}
}
_mPortExtraDof = static_cast<int>(_mB_complex.size()) - dof;
const int sysSize = static_cast<int>(_mB_complex.size());
if (_mP_complex.rows() < sysSize)
{
Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor> Pnew(sysSize, sysSize);
Pnew.setIdentity();
_mP_complex = Pnew;
}
}