312 lines
11 KiB
C++
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;
|
|
}
|
|
}
|