XIAN-FEM-2026June/3D opticsfem-master/complex/complexsolver.cpp

200 lines
6.2 KiB
C++

// Drop-in replacement for OpticsFEM external complex linear solver.
// Reads CSR matrix + RHS from binary .dat files (same protocol as solver/interface.cpp)
// and writes x_real_0.dat / x_complex_0.dat for the main program.
#include <Eigen/Sparse>
#include <Eigen/SparseLU>
#include <Eigen/IterativeLinearSolvers>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
using SpMat = Eigen::SparseMatrix<std::complex<double>>;
using VecX = Eigen::VectorXcd;
static std::vector<int> readIntFile(const char* path)
{
std::ifstream in(path, std::ios::binary);
if (!in)
throw std::runtime_error(std::string("cannot open ") + path);
in.seekg(0, std::ios::end);
const std::streamsize bytes = in.tellg();
in.seekg(0, std::ios::beg);
std::vector<int> data(static_cast<size_t>(bytes / sizeof(int)));
if (!data.empty())
in.read(reinterpret_cast<char*>(data.data()), bytes);
return data;
}
static std::vector<double> readDoubleFile(const char* path)
{
std::ifstream in(path, std::ios::binary);
if (!in)
throw std::runtime_error(std::string("cannot open ") + path);
in.seekg(0, std::ios::end);
const std::streamsize bytes = in.tellg();
in.seekg(0, std::ios::beg);
std::vector<double> data(static_cast<size_t>(bytes / sizeof(double)));
if (!data.empty())
in.read(reinterpret_cast<char*>(data.data()), bytes);
return data;
}
static void writeDoubleFile(const char* path, const double* data, int n)
{
std::ofstream out(path, std::ios::binary | std::ios::trunc);
if (!out)
throw std::runtime_error(std::string("cannot write ") + path);
out.write(reinterpret_cast<const char*>(data), static_cast<std::streamsize>(n) * sizeof(double));
}
static SpMat loadCsrMatrix(const std::vector<int>& roffsets,
const std::vector<int>& cindices,
const std::vector<double>& aReal,
const std::vector<double>& aImag)
{
if (roffsets.size() < 2)
throw std::runtime_error("invalid Roffsets.dat");
const int n = static_cast<int>(roffsets.size()) - 1;
const int nnz = roffsets.back();
if (static_cast<int>(cindices.size()) != nnz
|| static_cast<int>(aReal.size()) != nnz
|| static_cast<int>(aImag.size()) != nnz)
throw std::runtime_error("CSR dimensions mismatch");
std::vector<Eigen::Triplet<std::complex<double>>> triplets;
triplets.reserve(static_cast<size_t>(nnz));
for (int i = 0; i < n; ++i)
{
for (int k = roffsets[static_cast<size_t>(i)]; k < roffsets[static_cast<size_t>(i + 1)]; ++k)
{
triplets.emplace_back(i, cindices[static_cast<size_t>(k)],
std::complex<double>(aReal[static_cast<size_t>(k)], aImag[static_cast<size_t>(k)]));
}
}
SpMat A(n, n);
A.setFromTriplets(triplets.begin(), triplets.end());
A.makeCompressed();
return A;
}
static VecX loadRhs(const std::vector<double>& bReal, const std::vector<double>& bImag, int n)
{
if (static_cast<int>(bReal.size()) != n || static_cast<int>(bImag.size()) != n)
throw std::runtime_error("RHS dimensions mismatch");
VecX b(n);
for (int i = 0; i < n; ++i)
b(i) = std::complex<double>(bReal[static_cast<size_t>(i)], bImag[static_cast<size_t>(i)]);
return b;
}
static VecX solveDirect(const SpMat& A, const VecX& b)
{
Eigen::SparseLU<SpMat> lu;
lu.compute(A);
if (lu.info() != Eigen::Success)
throw std::runtime_error("SparseLU factorization failed");
VecX x = lu.solve(b);
if (lu.info() != Eigen::Success)
throw std::runtime_error("SparseLU solve failed");
return x;
}
static VecX solveIterative(const SpMat& A, const VecX& b, double relTol, double absTol, int& iterations)
{
Eigen::BiCGSTAB<SpMat, Eigen::IncompleteLUT<std::complex<double>>> solver;
solver.preconditioner().setDroptol(1e-4);
solver.setMaxIterations(2000);
solver.setTolerance(relTol > 0.0 ? relTol : 1e-8);
solver.compute(A);
if (solver.info() != Eigen::Success)
throw std::runtime_error("BiCGSTAB setup failed");
VecX x = solver.solve(b);
iterations = static_cast<int>(solver.iterations());
const double bn = b.norm();
const double rn = (A * x - b).norm();
if (solver.info() != Eigen::Success && rn > absTol && rn > relTol * bn)
throw std::runtime_error("BiCGSTAB did not converge");
return x;
}
int main()
{
try
{
const std::vector<int> roffsets = readIntFile("Roffsets.dat");
const std::vector<int> cindices = readIntFile("Cindices.dat");
const std::vector<double> aReal = readDoubleFile("A_real.dat");
const std::vector<double> aImag = readDoubleFile("A_imag.dat");
const std::vector<double> bReal = readDoubleFile("B_real.dat");
const std::vector<double> bImag = readDoubleFile("B_imag.dat");
const std::vector<double> opt = readDoubleFile("opt.dat");
const int n = static_cast<int>(roffsets.size()) - 1;
const double relTol = opt.size() > 3 ? opt[3] : 1e-3;
const double absTol = opt.size() > 4 ? opt[4] : 1e-6;
std::cerr << "[complexsolver] n=" << n << " nnz=" << roffsets.back()
<< " relTol=" << relTol << std::endl;
const SpMat A = loadCsrMatrix(roffsets, cindices, aReal, aImag);
const VecX b = loadRhs(bReal, bImag, n);
VecX x;
std::string method;
int iterations = 0;
const char* mode = std::getenv("OPTICSFEM_COMPLEXSOLVER");
const bool useIterative = mode && std::strcmp(mode, "iter") == 0;
if (useIterative && n >= 5000)
{
x = solveIterative(A, b, relTol, absTol, iterations);
method = "BiCGSTAB+ILUT";
}
else
{
x = solveDirect(A, b);
method = "SparseLU";
}
std::vector<double> xr(static_cast<size_t>(n)), xi(static_cast<size_t>(n));
for (int i = 0; i < n; ++i)
{
xr[static_cast<size_t>(i)] = x(i).real();
xi[static_cast<size_t>(i)] = x(i).imag();
}
writeDoubleFile("x_real_0.dat", xr.data(), n);
writeDoubleFile("x_complex_0.dat", xi.data(), n);
const double residual = (A * x - b).norm();
const double bnorm = b.norm();
writeDoubleFile("res.dat", &residual, 1);
std::ofstream log("log.dat", std::ios::trunc);
log << "method=" << method << "\n";
log << "n=" << n << " nnz=" << roffsets.back() << "\n";
log << "|b|=" << bnorm << " |r|=" << residual << "\n";
if (iterations > 0)
log << "iterations=" << iterations << "\n";
std::cerr << "[complexsolver] done " << method << " |x|=" << x.norm()
<< " |r|=" << residual << std::endl;
return 0;
}
catch (const std::exception& ex)
{
std::cerr << "[complexsolver] ERROR: " << ex.what() << std::endl;
return 1;
}
}