#include"Assemble_Base.h" #include"Nedelec3D_Util.h" #include"../function/BF.h" #include"../function/Gauss.h" #include"../common/define.h" #include"../material/Material_Base.h" #include"../phy/Phy_Base.h" #include"../parser/mpParser.h" #include"SBC_NormalUtil.h" #include #include #include #include #include using namespace Eigen; namespace { Vector3cd crossNormal(const Vector3d& normal, const Vector3cd& a) { return normal.cross(a.cross(normal)); } Vector3cd evalVec3At( mup::ParserX& parser, mup::Value& xx, mup::Value& yy, mup::Value& zz, const std::string& sx, const std::string& sy, const std::string& sz, double x, double y, double z) { xx = x; yy = y; zz = z; // Use string_type explicitly; _T(c_str) is not reliable for dynamic strings. parser.SetExpr(mup::string_type(sx.begin(), sx.end())); mup::Value vx = parser.Eval(); parser.SetExpr(mup::string_type(sy.begin(), sy.end())); mup::Value vy = parser.Eval(); parser.SetExpr(mup::string_type(sz.begin(), sz.end())); mup::Value vz = parser.Eval(); return Vector3cd( std::complex(vx.GetFloat(), vx.GetImag()), std::complex(vy.GetFloat(), vy.GetImag()), std::complex(vz.GetFloat(), vz.GetImag())); } bool setupFaceIntegration( Mesh_3D* mesh, int triIdx, Matrix3d& invJac, double& integCoe, Vector3d& normal, int mappingIndex[3], int bfIndex[3], Vector3d& x2, Vector3d& y2, Vector3d& z2, Vector3d& x3, Vector3d& y3, Vector3d& z3, double xv[4], double yv[4], double zv[4]) { Vector3i conn; mesh->GetCoonOfTri(triIdx, conn); const int numTet = conn(0); const int numFace = conn(1) + 1; 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); } 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 false; } 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 false; 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; integCoe = 0.25 * std::sqrt(heron); mesh->GetNormOfFace(mesh->GetDomainOfTri(triIdx), normal); if (normal.norm() < 1e-30) { Vector3d e1 = Vector3d(x3(1) - x3(0), y3(1) - y3(0), z3(1) - z3(0)); Vector3d e2 = Vector3d(x3(2) - x3(0), y3(2) - y3(0), z3(2) - z3(0)); normal = e1.cross(e2); if (normal.norm() > 1e-30) normal.normalize(); } return true; } 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) { // Triangle barycentric (u, v, 1-u-v) on the three face vertices stored in x3/y3/z3. // Do NOT route through tet ref coords (x2,y2,z2): that only matches face 1 and // yields wrong z (and breaks z-dependent sources like SCD J0) on faces 2/3/4. (void)x2; (void)y2; (void)z2; const double w = 1.0 - u - v; px = x3(0) * u + x3(1) * v + x3(2) * w; py = y3(0) * u + y3(1) * v + y3(2) * w; pz = z3(0) * u + z3(1) * v + z3(2) * w; } bool tetContainsVertex(Mesh_3D* mesh, int tetIdx, int vertexId) { for (int j = 0; j < 4; j++) { if (mesh->GetTet(tetIdx, j) == vertexId) return true; } return false; } // IBP 边界闭合:b -= ∮ N·(n×curl E_b) dS(PML 面或 SBC 外表面,不是 Robin 的 q) void accumulateBeleFaceIbp( Mesh_3D* mesh, VectorXcd& B, mup::ParserX& parser, mup::Value& xx, mup::Value& yy, mup::Value& zz, const std::string& curlEbx, const std::string& curlEby, const std::string& curlEbz, int triIdx, const double* uFace, const double* vFace, const double* wghtFace, int nbrGPFace, BF& bfN, bool useScatterSbcNormal, int elementOrder) { Matrix3d invJac; double integCoe = 0.0; Vector3d normal = Vector3d::Zero(); int mappingIndex3[3], bfIndex3[3]; Vector3d x2, y2, z2, x3, y3, z3; double xv[4], yv[4], zv[4]; if (!setupFaceIntegration(mesh, triIdx, invJac, integCoe, normal, mappingIndex3, bfIndex3, x2, y2, z2, x3, y3, z3, xv, yv, zv)) return; int mappingIndex[8], bfIndex[8]; int nLocal = 3; if (elementOrder == 2) { Eigen::Vector3i conn; mesh->GetCoonOfTri(triIdx, conn); Nedelec3D::buildSbcSecondOrderDofMap(mesh, conn(0), conn(1) + 1, mappingIndex); Nedelec3D::sbcFaceSecondOrderBfIndex(conn(1) + 1, bfIndex); nLocal = 8; } else { for (int i = 0; i < 3; i++) { mappingIndex[i] = mappingIndex3[i]; bfIndex[i] = bfIndex3[i]; } } if (useScatterSbcNormal) { const int domain = mesh->GetDomainOfTri(triIdx); 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)); normal = OpticsFEM::computeScatterSBCNormal( false, domain, xv, yv, faceP0, faceP1, faceP2, &meshNorm); } std::vector> Egp(static_cast(nbrGPFace), std::vector(static_cast(nLocal))); for (int gp = 0; gp < nbrGPFace; gp++) { const double wgp = 1.0 - uFace[gp] - vFace[gp]; const double u2 = x2(0) * uFace[gp] + x2(1) * vFace[gp] + x2(2) * wgp; const double v2 = y2(0) * uFace[gp] + y2(1) * vFace[gp] + y2(2) * wgp; const double w2 = z2(0) * uFace[gp] + z2(1) * vFace[gp] + z2(2) * wgp; for (int j = 0; j < nLocal; j++) { bfN.GetValueBF(bfIndex[j], u2, v2, w2, Egp[static_cast(gp)][static_cast(j)]); Egp[static_cast(gp)][static_cast(j)] = invJac * Egp[static_cast(gp)][static_cast(j)]; } } std::vector bcurlE(static_cast(nbrGPFace)); for (int gp = 0; gp < nbrGPFace; gp++) { double px, py, pz; physicalPointOnFace(uFace[gp], vFace[gp], x2, y2, z2, x3, y3, z3, px, py, pz); bcurlE[static_cast(gp)] = evalVec3At(parser, xx, yy, zz, curlEbx, curlEby, curlEbz, px, py, pz); } VectorXcd bt = VectorXcd::Zero(nLocal); for (int i = 0; i < nLocal; i++) { for (int gp = 0; gp < nbrGPFace; gp++) { const Vector3d& Ei = Egp[static_cast(gp)][static_cast(i)]; const Vector3cd& curlEb = bcurlE[static_cast(gp)]; // 2D BELE PML legacy: b -= ∮ N·(n×curl E_b) const std::complex curlContrib = wghtFace[gp] * integCoe * (std::complex(Ei(0), 0.0) * (normal(1) * curlEb(2) - normal(2) * curlEb(1)) + std::complex(Ei(1), 0.0) * (normal(2) * curlEb(0) - normal(0) * curlEb(2)) + std::complex(Ei(2), 0.0) * (normal(0) * curlEb(1) - normal(1) * curlEb(0))); bt(i) -= curlContrib; } } for (int i = 0; i < nLocal; i++) B(mappingIndex[i]) += bt(i); } } // namespace void OpticsFEM_3D_Scatter::Assemble_BELE() { const double k0 = 2.0 * Pi / _mSolver->GetLda0(); const int elementOrder = _mPhy->GetElementOrder(); const int bfOrder = Nedelec3D::bfOrderParam(elementOrder); const int gaussVol = Nedelec3D::gaussOrderVol(elementOrder); const int gaussTri = Nedelec3D::gaussOrderTri(elementOrder); Gauss gauss; const int nbrGPVol = gauss.GetNbrGaussPoints(THREEDIM, TETRAHEDRON, gaussVol); double* uVol = new double[nbrGPVol]; double* vVol = new double[nbrGPVol]; double* wVol = new double[nbrGPVol]; double* wghtVol = new double[nbrGPVol]; gauss.GetGaussPoints(THREEDIM, TETRAHEDRON, uVol, vVol, wVol, wghtVol); BF bfN; const int vdof = bfN.GetNbrBF(THREEDIM, TETRAHEDRON, BF_NEDELEC, bfOrder); Vector3d** E = new Vector3d*[nbrGPVol]; for (int i = 0; i < nbrGPVol; i++) E[i] = new Vector3d[vdof]; 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)); std::string Ebx, Eby, Ebz, curlcurlEbx, curlcurlEby, curlcurlEbz; _mPhy->GetEb(Ebx, Eby, Ebz); _mPhy->GetCurlCurlEb(curlcurlEbx, curlcurlEby, curlcurlEbz); Eigen::VectorXi beleDomains; _mPhy->GetBELE(beleDomains); Vector3d vertex[4]; for (int n = 0; n < _mMesh->GetNbrTet(); n++) { const int domain = _mMesh->GetDomainOfTet(n); if (std::find(beleDomains.begin(), beleDomains.end(), domain) == beleDomains.end()) continue; for (int i = 0; i < 4; i++) _mMesh->GetVertex(_mMesh->GetTet(n, i), vertex[i]); Matrix3d Jac, InvJac; Jac(0, 0) = vertex[0](0) - vertex[3](0); Jac(0, 1) = vertex[0](1) - vertex[3](1); Jac(0, 2) = vertex[0](2) - vertex[3](2); Jac(1, 0) = vertex[1](0) - vertex[3](0); Jac(1, 1) = vertex[1](1) - vertex[3](1); Jac(1, 2) = vertex[1](2) - vertex[3](2); Jac(2, 0) = vertex[2](0) - vertex[3](0); Jac(2, 1) = vertex[2](1) - vertex[3](1); Jac(2, 2) = vertex[2](2) - vertex[3](2); const double detJac = std::abs(Jac.determinant()); if (detJac < 1e-30) continue; InvJac = Jac.inverse(); for (int gp = 0; gp < nbrGPVol; gp++) { for (int j = 0; j < vdof; j++) { bfN.GetValueBF(j + 1, uVol[gp], vVol[gp], wVol[gp], E[gp][j]); E[gp][j] = InvJac * E[gp][j]; } } Vector3cd* bE = new Vector3cd[nbrGPVol]; Vector3cd* bCurlCurlE = new Vector3cd[nbrGPVol]; for (int gp = 0; gp < nbrGPVol; gp++) { const double px = vertex[3](0) + Jac(0, 0) * uVol[gp] + Jac(1, 0) * vVol[gp] + Jac(2, 0) * wVol[gp]; const double py = vertex[3](1) + Jac(0, 1) * uVol[gp] + Jac(1, 1) * vVol[gp] + Jac(2, 1) * wVol[gp]; const double pz = vertex[3](2) + Jac(0, 2) * uVol[gp] + Jac(1, 2) * vVol[gp] + Jac(2, 2) * wVol[gp]; bE[gp] = evalVec3At(parser, xx, yy, zz, Ebx, Eby, Ebz, px, py, pz); bCurlCurlE[gp] = evalVec3At(parser, xx, yy, zz, curlcurlEbx, curlcurlEby, curlcurlEbz, px, py, pz); } Matrix3cd epsr = _mMatLib->GetEpsr(domain); const double k0sq = k0 * k0; std::vector mapBuf(static_cast(vdof), 0); Nedelec3D::buildTetDofMap(_mMesh, n, elementOrder, mapBuf.data(), vdof); for (int i = 0; i < vdof; i++) { std::complex contrib = 0.0; for (int gp = 0; gp < nbrGPVol; gp++) { const Vector3cd Jb = bCurlCurlE[gp] - k0sq * (epsr * bE[gp]); contrib += wghtVol[gp] * detJac * E[gp][i].dot(Jb); } _mB_complex(mapBuf[static_cast(i)]) -= contrib; } delete[] bE; delete[] bCurlCurlE; } const int nbrGPFace = gauss.GetNbrGaussPoints(TWODIM, TRIANGLE, gaussTri); double* uFace = new double[nbrGPFace]; double* vFace = new double[nbrGPFace]; double* wFace = new double[nbrGPFace]; double* wghtFace = new double[nbrGPFace]; gauss.GetGaussPoints(TWODIM, TRIANGLE, uFace, vFace, wFace, wghtFace); BF bfNFace; bfNFace.GetNbrBF(THREEDIM, TETRAHEDRON, BF_NEDELEC, bfOrder); std::string curlEbx, curlEby, curlEbz; _mPhy->GetCurlEb(curlEbx, curlEby, curlEbz); const int nbrPML = _mPhy->GetNbrPML(); for (int p = 0; p < nbrPML; p++) { VectorXi triIndices; _mMesh->GetTriIndicesOfDomain(_mPhy->GetPMLEdge(p) + 1, triIndices); for (int t = 0; t < triIndices.size(); t++) { accumulateBeleFaceIbp(_mMesh, _mB_complex, parser, xx, yy, zz, curlEbx, curlEby, curlEbz, triIndices(t), uFace, vFace, wghtFace, nbrGPFace, bfNFace, false, elementOrder); } } delete[] uVol; delete[] vVol; delete[] wVol; delete[] wghtVol; delete[] uFace; delete[] vFace; delete[] wFace; delete[] wghtFace; for (int i = 0; i < nbrGPVol; i++) delete[] E[i]; delete[] E; } void OpticsFEM_3D_Scatter::Assemble_MAG() { const double k0 = 2.0 * Pi / _mSolver->GetLda0(); const std::complex iUnit(0.0, 1.0); const double zFactor = std::sqrt(mu0 / epsilon0); const int elementOrder = _mPhy->GetElementOrder(); const int bfOrder = Nedelec3D::bfOrderParam(elementOrder); const int gaussTri = Nedelec3D::gaussOrderTri(elementOrder); const int nBfFace = (elementOrder == 2) ? 8 : 3; Gauss gauss; const int nbrGP = gauss.GetNbrGaussPoints(TWODIM, TRIANGLE, gaussTri); 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); 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)); for (int n = 0; n < _mPhy->GetNbrMAG(); n++) { std::string H0x, H0y, H0z; _mPhy->GetH0(H0x, H0y, H0z, n); VectorXi triIndices; _mMesh->GetTriIndicesOfDomain(_mPhy->GetMAGDomain(n), triIndices); for (int t = 0; t < triIndices.size(); t++) { Matrix3d invJac; double integCoe = 0.0; Vector3d normal = Vector3d::Zero(); int mappingIndex3[3], bfIndex3[3]; Vector3d x2, y2, z2, x3, y3, z3; double xv[4], yv[4], zv[4]; if (!setupFaceIntegration(_mMesh, triIndices(t), invJac, integCoe, normal, mappingIndex3, bfIndex3, x2, y2, z2, x3, y3, z3, xv, yv, zv)) continue; int mappingIndex[8], bfIndex[8]; int nLocal = 3; if (elementOrder == 2) { Eigen::Vector3i conn; _mMesh->GetCoonOfTri(triIndices(t), conn); Nedelec3D::buildSbcSecondOrderDofMap(_mMesh, conn(0), conn(1) + 1, mappingIndex); Nedelec3D::sbcFaceSecondOrderBfIndex(conn(1) + 1, bfIndex); nLocal = 8; } else { for (int i = 0; i < 3; i++) { mappingIndex[i] = mappingIndex3[i]; bfIndex[i] = bfIndex3[i]; } } const int triDomain = _mMesh->GetDomainOfTri(triIndices(t)); Vector3i conn; _mMesh->GetCoonOfTri(triIndices(t), conn); const int tetDomain = _mMesh->GetDomainOfTet(conn(0)); const double eps = _mMatLib->GetEpsr(tetDomain)(0, 0).real(); const std::complex nn = std::sqrt(std::complex(eps, 0.0)); Vector3d meshNorm; _mMesh->GetNormOfFace(triDomain, 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)); normal = OpticsFEM::computeScatterSBCNormal( false, triDomain, xv, yv, faceP0, faceP1, faceP2, &meshNorm); std::vector> Egp(static_cast(nbrGP), std::vector(static_cast(nLocal))); 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 < nLocal; j++) { bfN.GetValueBF(bfIndex[j], u2, v2, w2, Egp[static_cast(gp)][static_cast(j)]); Egp[static_cast(gp)][static_cast(j)] = invJac * Egp[static_cast(gp)][static_cast(j)]; } } VectorXcd bt = VectorXcd::Zero(nLocal); for (int i = 0; i < nLocal; i++) { for (int gp = 0; gp < nbrGP; gp++) { double px, py, pz; physicalPointOnFace(u[gp], v[gp], x2, y2, z2, x3, y3, z3, px, py, pz); const Vector3cd H0 = evalVec3At(parser, xx, yy, zz, H0x, H0y, H0z, px, py, pz); const Vector3cd Ms = normal.cross(H0); bt(i) -= iUnit * zFactor * k0 * nn * integCoe * wght[gp] * Egp[static_cast(gp)][static_cast(i)].dot(Ms) * 2.0; } } for (int i = 0; i < nLocal; i++) _mB_complex(mappingIndex[i]) += bt(i); } } delete[] u; delete[] v; delete[] w; delete[] wght; (void)nBfFace; } void OpticsFEM_3D_Scatter::Assemble_SCD() { const double k0 = 2.0 * Pi / _mSolver->GetLda0(); const std::complex iUnit(0.0, 1.0); const double zFactor = std::sqrt(mu0 / epsilon0); const int elementOrder = _mPhy->GetElementOrder(); const int bfOrder = Nedelec3D::bfOrderParam(elementOrder); const int gaussTri = Nedelec3D::gaussOrderTri(elementOrder); const int nBfFace = (elementOrder == 2) ? 8 : 3; Gauss gauss; const int nbrGP = gauss.GetNbrGaussPoints(TWODIM, TRIANGLE, gaussTri); 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); 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)); for (int n = 0; n < _mPhy->GetNbrSCD(); n++) { std::string J0x, J0y, J0z; _mPhy->GetJ0(J0x, J0y, J0z, n); // faceFactor / srcFactor: // Order1: empirical COMSOL SurfaceCurrent match: 2*k0/(π*max(ε_r)). // Order2: same *2.0 as MAG/SBC face sources (no extra k0). Reusing the // order1 faceFactor over-scales |E| on the SCD plane (~4.4 vs COMSOL ~2.1). double srcFactor = 2.0; if (elementOrder != 2) { double epsScdNorm = 1.0; for (int t = 0; t < _mMesh->GetNbrTet(); ++t) { const int dom = _mMesh->GetDomainOfTet(t); epsScdNorm = std::max(epsScdNorm, _mMatLib->GetEpsr(dom)(0, 0).real()); } srcFactor = 2.0 * k0 / Pi / epsScdNorm; } VectorXi triIndices; _mMesh->GetTriIndicesOfDomain(_mPhy->GetSCDDomain(n), triIndices); for (int t = 0; t < triIndices.size(); t++) { Matrix3d invJac; double integCoe = 0.0; Vector3d normal = Vector3d::Zero(); int mappingIndex3[3], bfIndex3[3]; Vector3d x2, y2, z2, x3, y3, z3; double xv[4], yv[4], zv[4]; if (!setupFaceIntegration(_mMesh, triIndices(t), invJac, integCoe, normal, mappingIndex3, bfIndex3, x2, y2, z2, x3, y3, z3, xv, yv, zv)) continue; int mappingIndex[8], bfIndex[8]; int nLocal = 3; if (elementOrder == 2) { Eigen::Vector3i conn; _mMesh->GetCoonOfTri(triIndices(t), conn); Nedelec3D::buildSbcSecondOrderDofMap(_mMesh, conn(0), conn(1) + 1, mappingIndex); Nedelec3D::sbcFaceSecondOrderBfIndex(conn(1) + 1, bfIndex); nLocal = 8; } else { for (int i = 0; i < 3; i++) { mappingIndex[i] = mappingIndex3[i]; bfIndex[i] = bfIndex3[i]; } } const int triDomain = _mMesh->GetDomainOfTri(triIndices(t)); Vector3i conn; _mMesh->GetCoonOfTri(triIndices(t), conn); Vector3d meshNorm; _mMesh->GetNormOfFace(triDomain, 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)); normal = OpticsFEM::computeScatterSBCNormal( false, triDomain, xv, yv, faceP0, faceP1, faceP2, &meshNorm); const int tetDomain = _mMesh->GetDomainOfTet(conn(0)); const double eps = _mMatLib->GetEpsr(tetDomain)(0, 0).real(); const std::complex nn = std::sqrt(std::complex(eps, 0.0)); std::vector> Egp(static_cast(nbrGP), std::vector(static_cast(nLocal))); 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 < nLocal; j++) { bfN.GetValueBF(bfIndex[j], u2, v2, w2, Egp[static_cast(gp)][static_cast(j)]); Egp[static_cast(gp)][static_cast(j)] = invJac * Egp[static_cast(gp)][static_cast(j)]; } } VectorXcd bt = VectorXcd::Zero(nLocal); for (int i = 0; i < nLocal; i++) { for (int gp = 0; gp < nbrGP; gp++) { double px, py, pz; physicalPointOnFace(u[gp], v[gp], x2, y2, z2, x3, y3, z3, px, py, pz); const Vector3cd J0 = evalVec3At(parser, xx, yy, zz, J0x, J0y, J0z, px, py, pz); const Vector3cd Jt = crossNormal(normal, J0); bt(i) -= iUnit * zFactor * k0 * nn * integCoe * wght[gp] * Egp[static_cast(gp)][static_cast(i)].dot(Jt) * srcFactor; } } for (int i = 0; i < nLocal; i++) _mB_complex(mappingIndex[i]) += bt(i); } } delete[] u; delete[] v; delete[] w; delete[] wght; (void)nBfFace; } void OpticsFEM_3D_Scatter::Assemble_MPD() { const double k0 = 2.0 * Pi / _mSolver->GetLda0(); const double zFactor = std::sqrt(mu0 / epsilon0); const int elementOrder = _mPhy->GetElementOrder(); const int bfOrder = Nedelec3D::bfOrderParam(elementOrder); const int gaussVol = Nedelec3D::gaussOrderVol(elementOrder); Eigen::VectorXi mpd; Eigen::MatrixXd m; _mPhy->GetMPD(mpd); _mPhy->GetMPDData(m); Gauss gauss; const int nbrGP = gauss.GetNbrGaussPoints(THREEDIM, TETRAHEDRON, gaussVol); double* u = new double[nbrGP]; double* v = new double[nbrGP]; double* w = new double[nbrGP]; double* wght = new double[nbrGP]; gauss.GetGaussPoints(THREEDIM, TETRAHEDRON, u, v, w, wght); BF bfCurlN; const int vdof = bfCurlN.GetNbrBF(THREEDIM, TETRAHEDRON, BF_CURL_NEDELEC, bfOrder); Vector3d** curlE = new Vector3d*[nbrGP]; for (int i = 0; i < nbrGP; i++) curlE[i] = new Vector3d[vdof]; Vector3d vertex[4]; for (int dip = 0; dip < mpd.size(); dip++) { const Vector3d mCurrent = m.row(dip); for (int n = 0; n < _mMesh->GetNbrTet(); n++) { if (!tetContainsVertex(_mMesh, n, mpd(dip))) continue; for (int i = 0; i < 4; i++) _mMesh->GetVertex(_mMesh->GetTet(n, i), vertex[i]); Matrix3d Jac, TJac; Jac(0, 0) = vertex[0](0) - vertex[3](0); Jac(0, 1) = vertex[0](1) - vertex[3](1); Jac(0, 2) = vertex[0](2) - vertex[3](2); Jac(1, 0) = vertex[1](0) - vertex[3](0); Jac(1, 1) = vertex[1](1) - vertex[3](1); Jac(1, 2) = vertex[1](2) - vertex[3](2); Jac(2, 0) = vertex[2](0) - vertex[3](0); Jac(2, 1) = vertex[2](1) - vertex[3](1); Jac(2, 2) = vertex[2](2) - vertex[3](2); const double detJac = std::abs(Jac.determinant()); if (detJac < 1e-30) continue; TJac = Jac.transpose() / Jac.determinant(); for (int gp = 0; gp < nbrGP; gp++) { for (int j = 0; j < vdof; j++) { bfCurlN.GetValueBF(j + 1, u[gp], v[gp], w[gp], curlE[gp][j]); curlE[gp][j] = TJac * curlE[gp][j]; } } VectorXcd Tt = VectorXcd::Zero(vdof); for (int i = 0; i < vdof; i++) { for (int gp = 0; gp < nbrGP; gp++) { Tt(i) += std::complex(0.0, -1.0) * zFactor * k0 * wght[gp] * detJac * curlE[gp][i].dot(mCurrent); } } std::vector mapBuf(static_cast(vdof), 0); Nedelec3D::buildTetDofMap(_mMesh, n, elementOrder, mapBuf.data(), vdof); for (int i = 0; i < vdof; i++) _mB_complex(mapBuf[static_cast(i)]) += Tt(i); } } delete[] u; delete[] v; delete[] w; delete[] wght; for (int i = 0; i < nbrGP; i++) delete[] curlE[i]; delete[] curlE; } void OpticsFEM_3D_Scatter::Assemble_EPD() { const double k0 = 2.0 * Pi / _mSolver->GetLda0(); const double zFactor = std::sqrt(mu0 / epsilon0); const int elementOrder = _mPhy->GetElementOrder(); const int bfOrder = Nedelec3D::bfOrderParam(elementOrder); const int gaussVol = Nedelec3D::gaussOrderVol(elementOrder); Eigen::VectorXi epd; Eigen::MatrixXd p; _mPhy->GetEPD(epd); _mPhy->GetEPDData(p); Gauss gauss; const int nbrGP = gauss.GetNbrGaussPoints(THREEDIM, TETRAHEDRON, gaussVol); double* u = new double[nbrGP]; double* v = new double[nbrGP]; double* w = new double[nbrGP]; double* wght = new double[nbrGP]; gauss.GetGaussPoints(THREEDIM, TETRAHEDRON, u, v, w, wght); BF bfN; const int vdof = bfN.GetNbrBF(THREEDIM, TETRAHEDRON, BF_NEDELEC, bfOrder); Vector3d** E = new Vector3d*[nbrGP]; for (int i = 0; i < nbrGP; i++) E[i] = new Vector3d[vdof]; Vector3d vertex[4]; for (int dip = 0; dip < epd.size(); dip++) { const Vector3d pCurrent = p.row(dip); for (int n = 0; n < _mMesh->GetNbrTet(); n++) { if (!tetContainsVertex(_mMesh, n, epd(dip))) continue; for (int i = 0; i < 4; i++) _mMesh->GetVertex(_mMesh->GetTet(n, i), vertex[i]); Matrix3d Jac, InvJac; Jac(0, 0) = vertex[0](0) - vertex[3](0); Jac(0, 1) = vertex[0](1) - vertex[3](1); Jac(0, 2) = vertex[0](2) - vertex[3](2); Jac(1, 0) = vertex[1](0) - vertex[3](0); Jac(1, 1) = vertex[1](1) - vertex[3](1); Jac(1, 2) = vertex[1](2) - vertex[3](2); Jac(2, 0) = vertex[2](0) - vertex[3](0); Jac(2, 1) = vertex[2](1) - vertex[3](1); Jac(2, 2) = vertex[2](2) - vertex[3](2); const double detJac = std::abs(Jac.determinant()); if (detJac < 1e-30) continue; InvJac = Jac.inverse(); for (int gp = 0; gp < nbrGP; gp++) { for (int j = 0; j < vdof; j++) { bfN.GetValueBF(j + 1, u[gp], v[gp], w[gp], E[gp][j]); E[gp][j] = InvJac * E[gp][j]; } } VectorXcd Tt = VectorXcd::Zero(vdof); for (int i = 0; i < vdof; i++) { for (int gp = 0; gp < nbrGP; gp++) { Tt(i) += std::complex(0.0, -1.0) * zFactor * k0 * wght[gp] * detJac * E[gp][i].dot(pCurrent); } } std::vector mapBuf(static_cast(vdof), 0); Nedelec3D::buildTetDofMap(_mMesh, n, elementOrder, mapBuf.data(), vdof); for (int i = 0; i < vdof; i++) _mB_complex(mapBuf[static_cast(i)]) += Tt(i); } } delete[] u; delete[] v; delete[] w; delete[] wght; for (int i = 0; i < nbrGP; i++) delete[] E[i]; delete[] E; }