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

1056 lines
28 KiB
C++

#include"../common/define.h"
#include"Assemble_Base.h"
#include"Nedelec3D_Util.h"
#include "../Eigen/SparseLU"
#include <cmath>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <string>
#ifdef _WIN32
#include <direct.h>
static void EnsureOutputDir(const std::string& dir)
{
_mkdir(dir.c_str());
}
#else
#include <sys/stat.h>
static void EnsureOutputDir(const std::string& dir)
{
mkdir(dir.c_str(), 0755);
}
#endif
/*
2D_EigenMode
*/
void OpticsFEM_2D_EigenMode::Assemble()
{
//??????????
_mIsReal = _mMatLib->IsReal();
if (_mIsReal)
{
if (_mPhy->GetNbrPML() > 0)
_mIsReal = false;
}
if (_mIsReal)
{
for (int i = 0; i < _mPhy->GetNbrPBC(); i++)
{
if (_mPhy->GetPBCPhi(i).imag()!= 0.0)
{
_mIsReal = false;
break;
}
}
}
//????????? ????????
_mDof = _mMesh->GetNbrVertex() + _mMesh->GetNbrEdge();
//????????
this->Assemble_WaveEquation();
if (_mIsReal)
{
_mA_real = Eigen::SparseMatrix<double, Eigen::RowMajor>(_mDof, _mDof);
_mB_real = Eigen::SparseMatrix<double, Eigen::RowMajor>(_mDof, _mDof);
_mA_real.setFromTriplets(_mTripleA_real.begin(), _mTripleA_real.end());
_mB_real.setFromTriplets(_mTripleB_real.begin(), _mTripleB_real.end());
}
else
{
_mA_complex = Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor>(_mDof, _mDof);
_mB_complex = Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor>(_mDof, _mDof);
_mA_complex.setFromTriplets(_mTripleA_complex.begin(), _mTripleA_complex.end());
_mB_complex.setFromTriplets(_mTripleB_complex.begin(), _mTripleB_complex.end());
}
//????????????????????????
if ((_mPhy->GetNbrPEC() + _mPhy->GetNbrPBCGroups() > 0))
{
this->Assemble_PEC_PBC();
if (_mIsReal)
{
_mA_real = (_mP_real.transpose() * _mA_real) * _mP_real;
_mB_real = (_mP_real.transpose() * _mB_real) * _mP_real;
}
else
{
_mA_complex = (_mP_complex.adjoint() * _mA_complex) * _mP_complex;
_mB_complex = (_mP_complex.adjoint() * _mB_complex) * _mP_complex;
}
}
else
{
if (_mIsReal)
{
_mP_real = Eigen::SparseMatrix<double, Eigen::RowMajor>(_mDof, _mDof);
_mP_real.setIdentity();
}
else
{
_mP_complex = Eigen::SparseMatrix<complex<double>, Eigen::RowMajor>(_mDof, _mDof);
_mP_complex.setIdentity();
}
}
//?????????????
/*
double k0 = 2 * Pi / _mSolver->GetLda0();
double theta2 = k0 * k0 * _mSolver->GetSearchValue() * _mSolver->GetSearchValue();
if (_mIsReal)
{
Eigen::SparseMatrix<double, Eigen::RowMajor> tempA = _mA_real;
_mA_real = _mB_real;
_mB_real = (1 / theta2) * tempA + _mB_real;
}
else
{
Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor> tempA = _mA_complex;
_mA_complex = _mB_complex;
_mB_complex = (1 / theta2) * tempA + _mB_complex;
}
*/
}
void OpticsFEM_2D_EigenMode::Run()
{
double k0 = 2. * Pi / _mSolver->GetLda0();
double search = -k0 * k0 * _mSolver->GetSearchValue() * _mSolver->GetSearchValue();
if (_mIsReal)
{
_mSolver->GetRealFlag(_mIsReal);
_mSolver->SetParam(&_mA_real, &_mB_real, &_mP_real, search);
}
else
{
_mSolver->GetRealFlag(_mIsReal);
_mSolver->SetParam(&_mA_complex, &_mB_complex, &_mP_complex, search);
}
_mSolver->Run(&_mX, &_mLambda);
}
void OpticsFEM_2D_EigenMode::Post(string file)
{
_mPost->GetMesh(_mMesh);
_mPost->GetSolver(_mSolver);
_mPost->GetResult(&_mX, &_mLambda);
_mPost->GetElectric();
_mPost->OutputData(file);
}
/*
2D_EigenFreq
*/
void OpticsFEM_2D_EigenFreq::Assemble()
{
//??????????
_mIsReal = _mMatLib->IsReal();
if (_mIsReal)
{
if (_mPhy->GetNbrPML() > 0)
_mIsReal = false;
}
if (_mIsReal)
{
for (int i = 0; i < _mPhy->GetNbrPBC(); i++)
{
if (_mPhy->GetPBCPhi(i).imag()!= 0.0)
{
_mIsReal = false;
break;
}
}
}
//????????
this->Assemble_WaveEquation();
//?????????
if (_mIsReal)
{
int tempDof = _mMesh->GetNbrVertex() + _mMesh->GetNbrEdge();
_mA_real = Eigen::SparseMatrix<double, Eigen::RowMajor>(tempDof, tempDof);
_mB_real = Eigen::SparseMatrix<double, Eigen::RowMajor>(tempDof, tempDof);
_mA_real.setFromTriplets(_mTripleA_real.begin(), _mTripleA_real.end());
_mB_real.setFromTriplets(_mTripleB_real.begin(), _mTripleB_real.end());
}
else
{
int tempDof = _mMesh->GetNbrVertex() + _mMesh->GetNbrEdge();
_mA_complex = Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor>(tempDof, tempDof);
_mB_complex = Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor>(tempDof, tempDof);
_mA_complex.setFromTriplets(_mTripleA_complex.begin(), _mTripleA_complex.end());
_mB_complex.setFromTriplets(_mTripleB_complex.begin(), _mTripleB_complex.end());
}
//????????????????????????
_mElectricType = _mSolver->GetElectricType();
if ((_mPhy->GetNbrPEC() + _mPhy->GetNbrPBCGroups() > 0))
{
this->Assemble_PEC_PBC();
}
else
{
//??T???????? 0-Et 1-Ez 2-E
if (_mIsReal)
{
std::vector<Eigen::Triplet<double>> tempTriple;
if (_mElectricType == 0)
{
_mDof = _mMesh->GetNbrEdge();
_mP_real = Eigen::SparseMatrix<double, Eigen::RowMajor>(_mMesh->GetNbrVertex() + _mMesh->GetNbrEdge(), _mDof);
for (int i = 0; i < _mDof; i++)
{
tempTriple.push_back(Eigen::Triplet<double>(_mMesh->GetNbrVertex() + i, i, 1.));
}
_mP_real.setFromTriplets(tempTriple.begin(), tempTriple.end());
}
else if (_mElectricType == 1)
{
_mDof = _mMesh->GetNbrVertex();
_mP_real = Eigen::SparseMatrix<double, Eigen::RowMajor>(_mMesh->GetNbrVertex() + _mMesh->GetNbrEdge(), _mDof);
for (int i = 0; i < _mDof; i++)
{
tempTriple.push_back(Eigen::Triplet<double>(i, i, 1.));
}
_mP_real.setFromTriplets(tempTriple.begin(), tempTriple.end());
}
else
{
_mDof = _mMesh->GetNbrVertex() + _mMesh->GetNbrEdge();
_mP_real = Eigen::SparseMatrix<double, Eigen::RowMajor>(_mDof, _mDof);
_mP_real.setIdentity();
}
}
else
{
std::vector<Eigen::Triplet<complex<double>>> tempTriple;
if (_mElectricType == 0)
{
_mDof = _mMesh->GetNbrEdge();
_mP_complex = Eigen::SparseMatrix<complex<double>, Eigen::RowMajor>(_mMesh->GetNbrVertex() + _mMesh->GetNbrEdge(), _mDof);
for (int i = 0; i < _mDof; i++)
{
/*tempTriple.push_back(Eigen::Triplet<complex<double>>(_mMesh->GetNbrVertex() + i, _mMesh->GetNbrVertex() + i, 1.));*/
tempTriple.push_back(Eigen::Triplet<complex<double>>(_mMesh->GetNbrVertex() + i, i, 1.));
}
_mP_complex.setFromTriplets(tempTriple.begin(), tempTriple.end());
}
else if (_mElectricType == 1)
{
_mDof = _mMesh->GetNbrVertex();
_mP_complex = Eigen::SparseMatrix<complex<double>, Eigen::RowMajor>(_mMesh->GetNbrVertex() + _mMesh->GetNbrEdge(), _mDof);
for (int i = 0; i < _mDof; i++)
{
tempTriple.push_back(Eigen::Triplet<complex<double>>(i, i, 1.));
}
_mP_complex.setFromTriplets(tempTriple.begin(), tempTriple.end());
}
else
{
_mDof = _mMesh->GetNbrVertex() + _mMesh->GetNbrEdge();
_mP_complex = Eigen::SparseMatrix<complex<double>, Eigen::RowMajor>(_mDof, _mDof);
_mP_complex.setIdentity();
}
}
}
if (_mIsReal)
{
_mA_real = (_mP_real.transpose() * _mA_real) * _mP_real;
_mB_real = (_mP_real.transpose() * _mB_real) * _mP_real;
}
else
{
_mA_complex = (_mP_complex.adjoint() * _mA_complex) * _mP_complex;
_mB_complex = (_mP_complex.adjoint() * _mB_complex) * _mP_complex;
}
}
void OpticsFEM_2D_EigenFreq::Run()
{
double k0 = 2. * Pi / (c_const / _mSolver->GetSearchValue());
double search = k0 * k0;
if (_mIsReal)
{
_mSolver->GetRealFlag(_mIsReal);
_mSolver->SetParam(&_mA_real, &_mB_real, &_mP_real, search);
}
else
{
_mSolver->GetRealFlag(_mIsReal);
_mSolver->SetParam(&_mA_complex, &_mB_complex, &_mP_complex, search);
}
_mSolver->Run(&_mX, &_mLambda);
}
void OpticsFEM_2D_EigenFreq::Post(string file)
{
_mPost->GetMesh(_mMesh);
_mPost->GetSolver(_mSolver);
_mPost->GetResult(&_mX, &_mLambda);
_mPost->GetElectric();
_mPost->OutputData(file);
}
/*
2D_Scatter
*/
void OpticsFEM_2D_Scatter::Assemble()
{
_mIsReal = _mMatLib->IsReal();
if (_mIsReal)
{
if (_mPhy->GetNbrPML() > 0)
_mIsReal = false;
}
if (_mIsReal)
{
if (_mPhy->GetNbrSBC() > 0)
_mIsReal = false;
}
if (_mIsReal)
{
/*for (int i = 0; i < _mPhy->GetNbrElE(); i++)
{
Eigen::Vector3cd E0;
_mPhy->GetE0(i, E0);
for (int j = 0; j < 3; j++)
{
if (E0(j).imag() != 0)
{
_mIsReal = false;
}
}
}*/
}
if (_mIsReal)
{
for (int i = 0; i < _mPhy->GetNbrPBC(); i++)
{
if (_mPhy->GetPBCPhi(i).imag()!= 0.0)
{
_mIsReal = false;
break;
}
}
}
if (_mIsReal)
{
if (_mPhy->GetBeamState() != 0)
_mIsReal = false;
}
//???????????
this->Assemble_WaveEquation();
if (_mIsReal)
{
int tempDof = _mMesh->GetNbrVertex() + _mMesh->GetNbrEdge();
_mB_real = Eigen::VectorXd::Zero(tempDof);
}
else
{
int tempDof = _mMesh->GetNbrVertex() + _mMesh->GetNbrEdge();
_mB_complex = Eigen::VectorXcd::Zero(tempDof);
}
//SBC
if (_mPhy->GetNbrSBC() > 0)
this->Assemble_SBC();
//BELE
if (_mPhy->GetNbrBELE())
this->Assemble_BELE();
if (_mIsReal)
{
int tempDof = _mMesh->GetNbrVertex() + _mMesh->GetNbrEdge();
_mA_real = Eigen::SparseMatrix<double, Eigen::RowMajor>(tempDof, tempDof);
_mA_real.setFromTriplets(_mTripleA_real.begin(), _mTripleA_real.end());
}
else
{
int tempDof = _mMesh->GetNbrVertex() + _mMesh->GetNbrEdge();
_mA_complex = Eigen::SparseMatrix<complex<double>, Eigen::RowMajor>(tempDof, tempDof);
_mA_complex.setFromTriplets(_mTripleA_complex.begin(), _mTripleA_complex.end());
}
//MAG
if (_mPhy->GetNbrMAG())
this->Assemble_MAG();
//SCD
if (_mPhy->GetNbrSCD())
this->Assemble_SCD();
//MPD
if (_mPhy->GetNbrMPD())
this->Assemble_MPD();
//EPD
if (_mPhy->GetNbrEPD())
this->Assemble_EPD();
//PEC+ELE
this->Assemble_PEC_ELE();
//PBC
if (_mPhy->GetNbrPBCGroups() > 0)
{
this->Assemble_PBC();
}
else
{
if (_mIsReal)
{
std::vector<Eigen::Triplet<double>> tempTriple;
if (_mSolver->GetElectricType() == 0)
{
_mDof = _mMesh->GetNbrEdge();
_mP_real = Eigen::SparseMatrix<double, Eigen::RowMajor>(_mMesh->GetNbrVertex() + _mMesh->GetNbrEdge(), _mDof);
for (int i = 0; i < _mDof; i++)
{
tempTriple.push_back(Eigen::Triplet<double>(_mMesh->GetNbrVertex() + i, i, 1.));
}
_mP_real.setFromTriplets(tempTriple.begin(), tempTriple.end());
}
else if (_mSolver->GetElectricType() == 1)
{
_mDof = _mMesh->GetNbrVertex();
_mP_real = Eigen::SparseMatrix<double, Eigen::RowMajor>(_mMesh->GetNbrVertex() + _mMesh->GetNbrEdge(), _mDof);
for (int i = 0; i < _mDof; i++)
{
tempTriple.push_back(Eigen::Triplet<double>(i, i, 1.));
}
_mP_real.setFromTriplets(tempTriple.begin(), tempTriple.end());
}
else
{
_mDof = _mMesh->GetNbrVertex() + _mMesh->GetNbrEdge();
_mP_real = Eigen::SparseMatrix<double, Eigen::RowMajor>(_mDof, _mDof);
_mP_real.setIdentity();
}
}
else
{
std::vector<Eigen::Triplet<complex<double>>> tempTriple;
if (_mSolver->GetElectricType() == 0)
{
_mDof = _mMesh->GetNbrEdge();
_mP_complex = Eigen::SparseMatrix<complex<double>, Eigen::RowMajor>(_mMesh->GetNbrVertex() + _mMesh->GetNbrEdge(), _mDof);
for (int i = 0; i < _mDof; i++)
{
tempTriple.push_back(Eigen::Triplet<complex<double>>(_mMesh->GetNbrVertex() + i, i, 1.));
}
_mP_complex.setFromTriplets(tempTriple.begin(), tempTriple.end());
}
else if (_mSolver->GetElectricType() == 1)
{
_mDof = _mMesh->GetNbrVertex();
_mP_complex = Eigen::SparseMatrix<complex<double>, Eigen::RowMajor>(_mMesh->GetNbrVertex() + _mMesh->GetNbrEdge(), _mDof);
for (int i = 0; i < _mDof; i++)
{
tempTriple.push_back(Eigen::Triplet<complex<double>>(i, i, 1.));
}
_mP_complex.setFromTriplets(tempTriple.begin(), tempTriple.end());
}
else
{
_mDof = _mMesh->GetNbrVertex() + _mMesh->GetNbrEdge();
_mP_complex = Eigen::SparseMatrix<complex<double>, Eigen::RowMajor>(_mDof, _mDof);
_mP_complex.setIdentity();
}
}
}
if (_mIsReal)
{
_mA_real = (_mP_real.transpose() * _mA_real) * _mP_real;
_mB_real = _mP_real.transpose() * _mB_real;
}
else
{
_mA_complex = (_mP_complex.adjoint() * _mA_complex) * _mP_complex;
_mB_complex = _mP_complex.adjoint() * _mB_complex;
}
}
void OpticsFEM_2D_Scatter::Run()
{
if (_mIsReal)
{
_mSolver->GetRealFlag(_mIsReal);
_mSolver->SetParam(&_mA_real, &_mB_real, &_mP_real);
}
else
{
_mSolver->GetRealFlag(_mIsReal);
_mSolver->SetParam(&_mA_complex, &_mB_complex, &_mP_complex);
}
_mSolver->Run(&_mX);
}
void OpticsFEM_2D_Scatter::Post(string file)
{
_mPost->GetMesh(_mMesh);
_mPost->GetSolver(_mSolver);
_mPost->GetPhy(_mPhy);
_mPost->GetResult(&_mX);
_mPost->GetElectric();
_mPost->OutputData(file);
}
/*
3D_EigenFreq - volume assembly_equ → sparse A (curl-curl) and B (eps mass)
*/
void OpticsFEM_3D_EigenFreq::Assemble()
{
const int elementOrder = _mPhy->GetElementOrder();
_mDof = Nedelec3D::globalDofCount(_mMesh, elementOrder);
_mTripleA_real.clear();
_mTripleB_real.clear();
_mTripleA_complex.clear();
_mTripleB_complex.clear();
_mIsReal = _mMatLib->IsReal();
if (_mIsReal)
{
for (int i = 0; i < _mPhy->GetNbrPBC(); i++)
{
if (_mPhy->GetPBCPhi(i).imag()!= 0.0)
{
_mIsReal = false;
break;
}
}
}
this->Assemble_WaveEquation();
if (_mIsReal)
{
_mA_real.resize(_mDof, _mDof);
_mB_real.resize(_mDof, _mDof);
_mA_real.setFromTriplets(_mTripleA_real.begin(), _mTripleA_real.end());
_mB_real.setFromTriplets(_mTripleB_real.begin(), _mTripleB_real.end());
}
else
{
_mA_complex.resize(_mDof, _mDof);
_mB_complex.resize(_mDof, _mDof);
_mA_complex.setFromTriplets(_mTripleA_complex.begin(), _mTripleA_complex.end());
_mB_complex.setFromTriplets(_mTripleB_complex.begin(), _mTripleB_complex.end());
}
if ((_mPhy->GetNbrPEC() + _mPhy->GetNbrPBCGroups() > 0))
{
this->Assemble_PEC_PBC();
}
else
{
if (_mIsReal)
{
_mP_real.resize(_mDof, _mDof);
_mP_real.setIdentity();
}
else
{
_mP_complex.resize(_mDof, _mDof);
_mP_complex.setIdentity();
}
}
if (_mIsReal)
{
_mA_real = (_mP_real.transpose() * _mA_real) * _mP_real;
_mB_real = (_mP_real.transpose() * _mB_real) * _mP_real;
}
else
{
_mA_complex = (_mP_complex.adjoint() * _mA_complex) * _mP_complex;
_mB_complex = (_mP_complex.adjoint() * _mB_complex) * _mP_complex;
}
std::cout << "[OpticsFEM_3D_EigenFreq] Assemble done: DOF=" << _mDof
<< " nnz(A)=" << (_mIsReal ? _mA_real.nonZeros() : _mA_complex.nonZeros())
<< " nnz(B)=" << (_mIsReal ? _mB_real.nonZeros() : _mB_complex.nonZeros())
<< std::endl;
}
/*
3D_Scatter - 3D first-order Nedelec scattering (assembly_equ + assembly_out + assembly_inc)
*/
void OpticsFEM_3D_Scatter::Assemble()
{
_mFreeDofIndices.clear();
_mPortExtraDof = 0;
_mIsReal = _mMatLib->IsReal();
if (_mIsReal)
{
if (_mPhy->GetNbrPML() > 0)
_mIsReal = false;
}
if (_mIsReal)
{
if (_mPhy->GetNbrSBC() > 0)
_mIsReal = false;
}
if (_mIsReal)
{
for (int i = 0; i < _mPhy->GetNbrPBC(); i++)
{
if (_mPhy->GetPBCPhi(i).imag() != 0.0)
{
_mIsReal = false;
break;
}
}
}
if (_mIsReal && _mPhy->GetNbrElE() > 0)
_mIsReal = false;
if (_mIsReal && (_mPhy->GetNbrBELE() > 0 || _mPhy->GetNbrMAG() > 0
|| _mPhy->GetNbrSCD() > 0 || _mPhy->GetNbrMPD() > 0 || _mPhy->GetNbrEPD() > 0))
_mIsReal = false;
if (_mIsReal && _mPhy->HasNumericPort())
_mIsReal = false;
const int elementOrder = _mPhy->GetElementOrder();
if (elementOrder == 2)
{
if (_mPhy->HasNumericPort())
{
std::cerr << "[OpticsFEM] ElementOrder=2: Port not supported yet; skipped."
<< std::endl;
}
}
_mDof = Nedelec3D::globalDofCount(_mMesh, elementOrder);
this->Assemble_WaveEquation();
if (_mIsReal)
_mB_real = Eigen::VectorXd::Zero(_mDof);
else
_mB_complex = Eigen::VectorXcd::Zero(_mDof);
if (_mPhy->GetNbrSBC() > 0)
this->Assemble_SBC();
if (_mPhy->GetNbrBELE() > 0)
this->Assemble_BELE();
if (_mIsReal)
{
_mA_real = Eigen::SparseMatrix<double, Eigen::RowMajor>(_mDof, _mDof);
_mA_real.setFromTriplets(_mTripleA_real.begin(), _mTripleA_real.end());
_mP_real = Eigen::SparseMatrix<double, Eigen::RowMajor>(_mDof, _mDof);
_mP_real.setIdentity();
}
else
{
_mA_complex = Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor>(_mDof, _mDof);
_mA_complex.setFromTriplets(_mTripleA_complex.begin(), _mTripleA_complex.end());
_mP_complex = Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor>(_mDof, _mDof);
_mP_complex.setIdentity();
}
if (_mExportAsmDir.size() > 0)
this->Test_OutputMatrix(_mExportAsmDir);
if (elementOrder != 2 && _mPhy->HasNumericPort())
this->Assemble_Port();
if (_mPhy->GetNbrMAG() > 0)
this->Assemble_MAG();
if (_mPhy->GetNbrSCD() > 0)
this->Assemble_SCD();
if (_mPhy->GetNbrMPD() > 0)
this->Assemble_MPD();
if (_mPhy->GetNbrEPD() > 0)
this->Assemble_EPD();
if (_mPhy->GetNbrPEC() > 0 || _mPhy->GetNbrElE() > 0)
this->Assemble_PEC_ELE();
if (_mPhy->GetNbrPBCGroups() > 0)
this->Assemble_PBC();
if (_mIsReal)
{
_mA_real = (_mP_real.transpose() * _mA_real) * _mP_real;
_mB_real = _mP_real.transpose() * _mB_real;
}
else
{
// After port PEC elimination A/B are already reduced; skip P projection.
if (_mFreeDofIndices.empty())
{
_mA_complex = (_mP_complex.adjoint() * _mA_complex) * _mP_complex;
_mB_complex = _mP_complex.adjoint() * _mB_complex;
}
}
}
void OpticsFEM_3D_Scatter::Run()
{
if (_mIsReal)
{
_mSolver->GetRealFlag(_mIsReal);
_mSolver->SetParam(&_mA_real, &_mB_real, &_mP_real);
}
else
{
_mSolver->GetRealFlag(_mIsReal);
_mSolver->SetParam(&_mA_complex, &_mB_complex, &_mP_complex);
}
// PEC/ELE: after Dirichlet BC, external complexsolver CSR export fails; use SparseLU here only.
const bool useBuiltInLu = (_mPhy->GetNbrPEC() > 0 || _mPhy->GetNbrElE() > 0 || _mPhy->HasNumericPort());
if (!_mIsReal && useBuiltInLu)
{
_mA_complex.makeCompressed();
Eigen::VectorXcd xReduced;
if (_mPhy->HasNumericPort())
{
std::cout << "[OpticsFEM_3D_Scatter] Reduced system"
<< " size=" << _mA_complex.rows()
<< " nnz=" << _mA_complex.nonZeros() << std::endl;
if (_mFreeDofIndices.size() >= 2)
{
const size_t n = _mFreeDofIndices.size();
std::cout << "[OpticsFEM_3D_Scatter] Port free DOFs:"
<< " e1_idx=" << _mFreeDofIndices[n - 2]
<< " e2_idx=" << _mFreeDofIndices[n - 1] << std::endl;
}
}
bool solved = false;
{
// MATLAB FemMatrixAssembly uses equilibrate(A) before '\'.
// Ruiz infinity-norm scaling + iterative refinement stabilizes short-cavity ports.
const int nA = static_cast<int>(_mA_complex.rows());
Eigen::VectorXd Rsc = Eigen::VectorXd::Ones(nA);
Eigen::VectorXd Csc = Eigen::VectorXd::Ones(nA);
Eigen::SparseMatrix<std::complex<double>> Aeq = _mA_complex;
for (int itEq = 0; itEq < 5; ++itEq)
{
Eigen::VectorXd rowInf = Eigen::VectorXd::Zero(nA);
for (int k = 0; k < Aeq.outerSize(); ++k)
for (Eigen::SparseMatrix<std::complex<double>>::InnerIterator it(Aeq, k); it; ++it)
rowInf(it.row()) = std::max(rowInf(it.row()), std::abs(it.value()));
for (int i = 0; i < nA; ++i)
{
const double s = (rowInf(i) > 0.0) ? (1.0 / std::sqrt(rowInf(i))) : 1.0;
Rsc(i) *= s;
}
std::vector<Eigen::Triplet<std::complex<double>>> trips;
trips.reserve(static_cast<size_t>(Aeq.nonZeros()));
for (int k = 0; k < Aeq.outerSize(); ++k)
for (Eigen::SparseMatrix<std::complex<double>>::InnerIterator it(Aeq, k); it; ++it)
trips.emplace_back(it.row(), it.col(), it.value() * (rowInf(it.row()) > 0.0 ? (1.0 / std::sqrt(rowInf(it.row()))) : 1.0));
Aeq.resize(nA, nA);
Aeq.setFromTriplets(trips.begin(), trips.end());
Eigen::VectorXd colInf = Eigen::VectorXd::Zero(nA);
for (int k = 0; k < Aeq.outerSize(); ++k)
for (Eigen::SparseMatrix<std::complex<double>>::InnerIterator it(Aeq, k); it; ++it)
colInf(it.col()) = std::max(colInf(it.col()), std::abs(it.value()));
for (int i = 0; i < nA; ++i)
{
const double s = (colInf(i) > 0.0) ? (1.0 / std::sqrt(colInf(i))) : 1.0;
Csc(i) *= s;
}
trips.clear();
for (int k = 0; k < Aeq.outerSize(); ++k)
for (Eigen::SparseMatrix<std::complex<double>>::InnerIterator it(Aeq, k); it; ++it)
trips.emplace_back(it.row(), it.col(), it.value() * (colInf(it.col()) > 0.0 ? (1.0 / std::sqrt(colInf(it.col()))) : 1.0));
Aeq.resize(nA, nA);
Aeq.setFromTriplets(trips.begin(), trips.end());
}
const Eigen::VectorXcd beq = Rsc.asDiagonal() * _mB_complex;
Eigen::SparseLU<Eigen::SparseMatrix<std::complex<double>>> lu;
lu.compute(Aeq);
if (lu.info() == Eigen::Success)
{
xReduced = Csc.asDiagonal() * lu.solve(beq);
// Iterative refinement on the original (unscaled) system.
for (int ir = 0; ir < 3; ++ir)
{
const Eigen::VectorXcd r = _mB_complex - _mA_complex * xReduced;
Eigen::VectorXcd dr = Csc.asDiagonal() * lu.solve(Rsc.asDiagonal() * r);
xReduced += dr;
if (r.norm() < 1e-12 * (_mB_complex.norm() + 1e-30))
break;
}
solved = true;
}
}
if (solved)
{
if (_mPhy->HasNumericPort())
{
if (!_mFreeDofIndices.empty())
{
_mX = Eigen::VectorXcd::Zero(_mDof + _mPortExtraDof);
for (size_t i = 0; i < _mFreeDofIndices.size(); i++)
_mX(_mFreeDofIndices[i]) = xReduced(static_cast<int>(i));
}
else
_mX = xReduced;
}
else
{
_mX = Eigen::VectorXcd::Zero(_mP_complex.rows());
_mX = _mP_complex * xReduced;
}
const Eigen::VectorXcd r = _mA_complex * xReduced - _mB_complex;
std::cout << "[OpticsFEM_3D_Scatter] SparseLU (PEC/ELE/Port)"
<< " |x|=" << _mX.norm()
<< " |r|=" << r.norm()
<< " |b|=" << _mB_complex.norm() << std::endl;
}
else
{
std::cout << "[OpticsFEM_3D_Scatter] SparseLU factorization failed" << std::endl;
}
return;
}
_mSolver->Run(&_mX);
if (!_mIsReal && _mB_complex.size() > 0)
{
const double bNorm = _mB_complex.norm();
const double xNorm = _mX.norm();
if (bNorm > 0.0 && (!std::isfinite(_mX(0).real()) || xNorm < 1e-12 * bNorm))
{
Eigen::SparseLU<Eigen::SparseMatrix<std::complex<double>>> lu;
lu.compute(_mA_complex);
if (lu.info() == Eigen::Success)
_mX = lu.solve(_mB_complex);
}
}
}
void OpticsFEM_3D_Scatter::Post(string file)
{
_mPost->GetMesh(_mMesh);
_mPost->GetPhy(_mPhy);
_mPost->GetResult(&_mX);
_mPost->GetElectric();
if (_mPortExtraDof > 0 && _mX.size() >= _mDof + _mPortExtraDof)
{
std::cout << "[OpticsFEM_3D_Scatter] Port amplitudes:";
for (int i = 0; i < _mPortExtraDof; i++)
std::cout << " e" << (i + 1) << "=" << _mX(_mDof + i);
std::cout << std::endl;
std::ofstream sOut(file + "/S_params.txt");
if (sOut.is_open())
{
sOut << std::setprecision(12);
const double pcoef = std::abs(_mPhy->GetNumericPort(0).powerCoef);
for (int i = 0; i < _mPortExtraDof; i++)
{
const std::complex<double> e = _mX(_mDof + i);
sOut << "e" << (i + 1) << " " << e.real() << " " << e.imag();
if (i == 0 && pcoef > 0.0)
sOut << " S11=" << (e.real() / pcoef) << " " << (e.imag() / pcoef);
else if (i == 1 && pcoef > 0.0)
sOut << " S21=" << (e.real() / pcoef) << " " << (e.imag() / pcoef);
sOut << std::endl;
}
}
}
this->Test_OutputMatrix(file);
_mPost->OutputData(file);
std::cout << "[OpticsFEM] post to " << file << std::endl;
}
void OpticsFEM_3D_Scatter::Test_OutputMatrix(const std::string& outDir)
{
EnsureOutputDir(outDir);
const std::string prefix = outDir + "/";
std::ofstream outAi(prefix + "Ai.txt"), outAj(prefix + "Aj.txt"), outAv(prefix + "Av.txt");
std::ofstream outBv_real(prefix + "Bv_real.txt"), outBv_imag(prefix + "Bv_imag.txt");
std::ofstream outX_real(prefix + "X_real.txt"), outX_imag(prefix + "X_imag.txt");
outAv << std::setprecision(12);
if (_mIsReal)
{
for (int col = 0; col < _mA_real.outerSize(); col++)
{
for (Eigen::SparseMatrix<double, Eigen::RowMajor>::InnerIterator it(_mA_real, col); it; ++it)
{
outAi << it.row() << std::endl;
outAj << it.col() << std::endl;
outAv << it.value() << std::endl;
}
}
for (int i = 0; i < _mB_real.size(); i++)
outBv_real << _mB_real(i) << std::endl;
for (int i = 0; i < _mX.size(); i++)
outX_real << _mX(i).real() << std::endl;
}
else
{
// Export reduced system after PBC/PEC projection (matches MATLAB main_export OutFile).
for (int col = 0; col < _mA_complex.outerSize(); col++)
{
for (Eigen::SparseMatrix<std::complex<double>, Eigen::RowMajor>::InnerIterator it(_mA_complex, col); it; ++it)
{
outAi << it.row() << std::endl;
outAj << it.col() << std::endl;
outAv << "(" << it.value().real() << "," << it.value().imag() << ")" << std::endl;
}
}
for (int i = 0; i < _mB_complex.size(); i++)
{
outBv_real << _mB_complex(i).real() << std::endl;
outBv_imag << _mB_complex(i).imag() << std::endl;
}
for (int i = 0; i < _mX.size(); i++)
{
outX_real << _mX(i).real() << std::endl;
outX_imag << _mX(i).imag() << std::endl;
}
}
}
void OpticsFEM_3D_EigenFreq::Test_OutputMatrix(const std::string& outDir)
{
EnsureOutputDir(outDir);
const std::string prefix = outDir + "/";
if (_mIsReal)
{
std::ofstream outAi(prefix + "Ai.txt"), outAj(prefix + "Aj.txt"), outAv(prefix + "Av.txt");
std::ofstream outBi(prefix + "Bi.txt"), outBj(prefix + "Bj.txt"), outBv(prefix + "Bv.txt");
for (const auto& t : _mTripleA_real)
{
outAi << t.row() << '\n';
outAj << t.col() << '\n';
outAv << t.value() << '\n';
}
for (const auto& t : _mTripleB_real)
{
outBi << t.row() << '\n';
outBj << t.col() << '\n';
outBv << t.value() << '\n';
}
std::cout << "[OpticsFEM_3D_EigenFreq] exported A/B COO to " << outDir << std::endl;
}
else
{
std::ofstream outAi(prefix + "Ai.txt"), outAj(prefix + "Aj.txt");
std::ofstream outAv_real(prefix + "Av_real.txt"), outAv_imag(prefix + "Av_imag.txt");
std::ofstream outBi(prefix + "Bi.txt"), outBj(prefix + "Bj.txt");
std::ofstream outBv_real(prefix + "Bv_real.txt"), outBv_imag(prefix + "Bv_imag.txt");
for (const auto& t : _mTripleA_complex)
{
outAi << t.row() << '\n';
outAj << t.col() << '\n';
outAv_real << t.value().real() << '\n';
outAv_imag << t.value().imag() << '\n';
}
for (const auto& t : _mTripleB_complex)
{
outBi << t.row() << '\n';
outBj << t.col() << '\n';
outBv_real << t.value().real() << '\n';
outBv_imag << t.value().imag() << '\n';
}
std::cout << "[OpticsFEM_3D_EigenFreq] exported A/B COO to " << outDir << std::endl;
}
}
void OpticsFEM_3D_EigenFreq::Run()
{
double k0 = 2. * Pi / (c_const / _mSolver->GetSearchValue());
double search = k0 * k0;
if (_mIsReal)
{
_mSolver->GetRealFlag(_mIsReal);
_mSolver->SetParam(&_mA_real, &_mB_real, &_mP_real, search);
}
else
{
_mSolver->GetRealFlag(_mIsReal);
_mSolver->SetParam(&_mA_complex, &_mB_complex, &_mP_complex, search);
}
_mSolver->Run(&_mX, &_mLambda);
}
void OpticsFEM_3D_EigenFreq::Post(string file)
{
_mPost->GetMesh(_mMesh);
_mPost->GetSolver(_mSolver);
_mPost->GetResult(&_mX, &_mLambda);
_mPost->GetElectric();
_mPost->OutputData(file);
std::cout << "[OpticsFEM_3D_EigenFreq] post to " << file << std::endl;
}
/*
3D_Scatter2
prism
*/
void OpticsFEM_3D_Scatter2::Assemble()
{
_mIsReal = _mMatLib->IsReal();
if (_mIsReal)
{
if (_mPhy->GetNbrPML() > 0)
_mIsReal = false;
}
if (_mIsReal)
{
if (_mPhy->GetNbrSBC() > 0)
_mIsReal = false;
}
if (_mIsReal)
{
/*for (int i = 0; i < _mPhy->GetNbrElE(); i++)
{
Eigen::Vector3cd E0;
_mPhy->GetE0(i, E0);
for (int j = 0; j < 3; j++)
{
if (E0(j).imag() != 0)
{
_mIsReal = false;
}
}
}*/
}
this->Assemble_WaveEquation();
//SBC
//this->Assemble_SBC();
//Port (n+m)*(n+m)
//this->Assemble_PortBC();
if (_mIsReal)
_mA_real.setFromTriplets(_mTripleA_real.begin(), _mTripleA_real.end());
else
_mA_complex.setFromTriplets(_mTripleA_complex.begin(), _mTripleA_complex.end());
//PEC+ELE
//this->Assemble_PEC_ELE();
}