// 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 #include #include #include #include #include #include #include #include #include #include using SpMat = Eigen::SparseMatrix>; using VecX = Eigen::VectorXcd; static std::vector 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 data(static_cast(bytes / sizeof(int))); if (!data.empty()) in.read(reinterpret_cast(data.data()), bytes); return data; } static std::vector 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 data(static_cast(bytes / sizeof(double))); if (!data.empty()) in.read(reinterpret_cast(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(data), static_cast(n) * sizeof(double)); } static SpMat loadCsrMatrix(const std::vector& roffsets, const std::vector& cindices, const std::vector& aReal, const std::vector& aImag) { if (roffsets.size() < 2) throw std::runtime_error("invalid Roffsets.dat"); const int n = static_cast(roffsets.size()) - 1; const int nnz = roffsets.back(); if (static_cast(cindices.size()) != nnz || static_cast(aReal.size()) != nnz || static_cast(aImag.size()) != nnz) throw std::runtime_error("CSR dimensions mismatch"); std::vector>> triplets; triplets.reserve(static_cast(nnz)); for (int i = 0; i < n; ++i) { for (int k = roffsets[static_cast(i)]; k < roffsets[static_cast(i + 1)]; ++k) { triplets.emplace_back(i, cindices[static_cast(k)], std::complex(aReal[static_cast(k)], aImag[static_cast(k)])); } } SpMat A(n, n); A.setFromTriplets(triplets.begin(), triplets.end()); A.makeCompressed(); return A; } static VecX loadRhs(const std::vector& bReal, const std::vector& bImag, int n) { if (static_cast(bReal.size()) != n || static_cast(bImag.size()) != n) throw std::runtime_error("RHS dimensions mismatch"); VecX b(n); for (int i = 0; i < n; ++i) b(i) = std::complex(bReal[static_cast(i)], bImag[static_cast(i)]); return b; } static VecX solveDirect(const SpMat& A, const VecX& b) { Eigen::SparseLU 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>> 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(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 roffsets = readIntFile("Roffsets.dat"); const std::vector cindices = readIntFile("Cindices.dat"); const std::vector aReal = readDoubleFile("A_real.dat"); const std::vector aImag = readDoubleFile("A_imag.dat"); const std::vector bReal = readDoubleFile("B_real.dat"); const std::vector bImag = readDoubleFile("B_imag.dat"); const std::vector opt = readDoubleFile("opt.dat"); const int n = static_cast(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 xr(static_cast(n)), xi(static_cast(n)); for (int i = 0; i < n; ++i) { xr[static_cast(i)] = x(i).real(); xi[static_cast(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; } }