#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 #include #include #include 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 matlabSumProduct(const Vector3d& aReal, const Vector3cd& b) { return std::complex(aReal(0), 0.0) * b(0) + std::complex(aReal(1), 0.0) * b(1) + std::complex(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& P, std::complex& 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 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 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>() * mode.Et(mapEt) + ezVec.cast>() * mode.Ez(mapEz); fcurlE += curlEtVec.cast>() * mode.Et(mapEt) + curlEzVec.cast>() * 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, RowMajor>& A, VectorXcd& b, int edgeDof, const VectorXcd& S, const VectorXcd& G, std::complex P, std::complex b1) { const int n = static_cast(b.size()); SparseMatrix, RowMajor> Aold = A; A = SparseMatrix, RowMajor>(n + 1, n + 1); std::vector>> trips; for (int k = 0; k < Aold.outerSize(); k++) for (SparseMatrix, 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, RowMajor>& A, VectorXcd& b, int edgeDof, const VectorXcd& T, const VectorXcd& G, std::complex P) { const int n = static_cast(b.size()); SparseMatrix, RowMajor> Aold = A; A = SparseMatrix, RowMajor>(n + 1, n + 1); std::vector>> trips; for (int k = 0; k < Aold.outerSize(); k++) for (SparseMatrix, 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 P(0.0, 0.0); std::complex 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(_mB_complex.size()) - dof; const int sysSize = static_cast(_mB_complex.size()); if (_mP_complex.rows() < sysSize) { Eigen::SparseMatrix, Eigen::RowMajor> Pnew(sysSize, sysSize); Pnew.setIdentity(); _mP_complex = Pnew; } }