diff --git a/CHANGELOG.md b/CHANGELOG.md index 207c1478a..e1bee333f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,9 @@ The format of this changelog is based on match the mesh on the boundaries specified by the donor and receiver attributes. This is only possible the mesh on the donor and receiver match exactly, non-matching meshes are not supported. + - Exposed configuration linear solver and eigen solver options for the wave port + subproblem. These can now be specified as part of the `config["Boundaries"]["WavePort"]` + configuration. The defaults align with the previously hardcoded values. ## [0.13.0] - 2024-05-20 diff --git a/docs/src/config/boundaries.md b/docs/src/config/boundaries.md index b491347ce..c373e7ce4 100644 --- a/docs/src/config/boundaries.md +++ b/docs/src/config/boundaries.md @@ -349,7 +349,11 @@ corresponding coordinate system. "Active": , "Mode": , "Offset": , - "SolverType": + "SolverType": , + "MaxIts": , + "KSPTol": , + "EigenTol": , + "Verbose": }, ... ] @@ -378,6 +382,16 @@ port boundary, specified in mesh length units. the boundary mode for this wave port. See [`config["Solver"]["Eigenmode"]["Type"]`](solver.md#solver%5B%22Eigenmode%22%5D). +`"MaxIts" [30]` : Specifies the maximum number of iterations to be used in the GMRES +solver. + +`"KSPTol" [1e-8]` : Specifies the tolerance to be used in the linear solver. + +`"EigenTol" [1e-6]` : Specifies the tolerance to be used in the eigenvalue solver. + +`"Verbose" [0]` : Specifies the verbosity level to be used in the linear and eigensolver +for the wave port problem. + ## `boundaries["WavePortPEC"]` ```json diff --git a/examples/cpw/cpw_wave_uniform.json b/examples/cpw/cpw_wave_uniform.json index 79403d907..a9bd7cbb1 100644 --- a/examples/cpw/cpw_wave_uniform.json +++ b/examples/cpw/cpw_wave_uniform.json @@ -69,25 +69,41 @@ "Attributes": [4], "Mode": 1, "Offset": 0.0, - "Excitation": true + "Excitation": true, + "MaxIts": 30, + "KSPTol": 1e-8, + "EigenTol": 1e-6, + "Verbose": 0 }, { "Index": 2, "Attributes": [5], "Mode": 1, - "Offset": 0.0 + "Offset": 0.0, + "MaxIts": 30, + "KSPTol": 1e-8, + "EigenTol": 1e-6, + "Verbose": 0 }, { "Index": 3, "Attributes": [6], "Mode": 1, - "Offset": 0.0 + "Offset": 0.0, + "MaxIts": 30, + "KSPTol": 1e-8, + "EigenTol": 1e-6, + "Verbose": 0 }, { "Index": 4, "Attributes": [7], "Mode": 1, - "Offset": 0.0 + "Offset": 0.0, + "MaxIts": 30, + "KSPTol": 1e-8, + "EigenTol": 1e-6, + "Verbose": 0 } ], "Postprocessing": diff --git a/palace/models/waveportoperator.cpp b/palace/models/waveportoperator.cpp index 250c5dd4d..30ef1ad05 100644 --- a/palace/models/waveportoperator.cpp +++ b/palace/models/waveportoperator.cpp @@ -645,14 +645,11 @@ WavePortData::WavePortData(const config::WavePortData &data, { // Define the linear solver to be used for solving systems associated with the // generalized eigenvalue problem. - constexpr int ksp_print = 0; - constexpr double ksp_tol = 1.0e-8; - constexpr double ksp_max_it = 30; - auto gmres = std::make_unique>(port_comm, ksp_print); + auto gmres = std::make_unique>(port_comm, data.verbose); gmres->SetInitialGuess(false); - gmres->SetRelTol(ksp_tol); - gmres->SetMaxIter(ksp_max_it); - gmres->SetRestartDim(ksp_max_it); + gmres->SetRelTol(data.ksp_tol); + gmres->SetMaxIter(data.ksp_max_its); + gmres->SetRestartDim(data.ksp_max_its); // gmres->SetPrecSide(GmresSolverBase::PrecSide::RIGHT); config::LinearSolverData::Type pc_type = solver.linear.type; @@ -698,7 +695,7 @@ WavePortData::WavePortData(const config::WavePortData &data, #if defined(MFEM_USE_SUPERLU) auto slu = std::make_unique( port_comm, config::LinearSolverData::SymFactType::DEFAULT, false, - ksp_print - 1); + data.verbose - 1); // slu->GetSolver().SetColumnPermutation(mfem::superlu::MMD_AT_PLUS_A); return slu; #endif @@ -708,7 +705,8 @@ WavePortData::WavePortData(const config::WavePortData &data, #if defined(MFEM_USE_STRUMPACK) auto strumpack = std::make_unique( port_comm, config::LinearSolverData::SymFactType::DEFAULT, - config::LinearSolverData::CompressionType::NONE, 0.0, 0, 0, ksp_print - 1); + config::LinearSolverData::CompressionType::NONE, 0.0, 0, 0, + data.verbose - 1); // strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::AMD); return strumpack; #endif @@ -718,7 +716,7 @@ WavePortData::WavePortData(const config::WavePortData &data, #if defined(MFEM_USE_MUMPS) auto mumps = std::make_unique( port_comm, mfem::MUMPSSolver::UNSYMMETRIC, - config::LinearSolverData::SymFactType::DEFAULT, 0.0, ksp_print - 1); + config::LinearSolverData::SymFactType::DEFAULT, 0.0, data.verbose - 1); // mumps->SetReorderingStrategy(mfem::MUMPSSolver::AMD); return mumps; #endif @@ -770,9 +768,8 @@ WavePortData::WavePortData(const config::WavePortData &data, eigen = std::move(slepc); #endif } - constexpr double tol = 1.0e-6; eigen->SetNumModes(mode_idx, std::max(2 * mode_idx + 1, 5)); - eigen->SetTol(tol); + eigen->SetTol(data.eig_tol); eigen->SetLinearSolver(*ksp); // We want to ignore evanescent modes (kₙ with large imaginary component). The diff --git a/palace/utils/configfile.cpp b/palace/utils/configfile.cpp index 05a634efb..b92a8d19c 100644 --- a/palace/utils/configfile.cpp +++ b/palace/utils/configfile.cpp @@ -1114,6 +1114,10 @@ void WavePortBoundaryData::SetUp(json &boundaries) data.eigen_type = it->value("SolverType", data.eigen_type); data.excitation = it->value("Excitation", data.excitation); data.active = it->value("Active", data.active); + data.ksp_max_its = it->value("MaxIts", data.ksp_max_its); + data.ksp_tol = it->value("KSPTol", data.ksp_tol); + data.eig_tol = it->value("EigenTol", data.eig_tol); + data.verbose = it->value("Verbose", data.verbose); // Cleanup it->erase("Index"); @@ -1123,6 +1127,10 @@ void WavePortBoundaryData::SetUp(json &boundaries) it->erase("SolverType"); it->erase("Excitation"); it->erase("Active"); + it->erase("MaxIts"); + it->erase("KSPTol"); + it->erase("EigenTol"); + it->erase("Verbose"); MFEM_VERIFY(it->empty(), "Found an unsupported configuration file keyword under \"WavePort\"!\n" << it->dump(2)); @@ -1137,6 +1145,10 @@ void WavePortBoundaryData::SetUp(json &boundaries) std::cout << "SolverType: " << data.eigen_type << '\n'; std::cout << "Excitation: " << data.excitation << '\n'; std::cout << "Active: " << data.active << '\n'; + std::cout << "MaxIts: " << data.ksp_max_its << '\n'; + std::cout << "KSPTol: " << data.ksp_tol << '\n'; + std::cout << "EigenTol: " << data.eig_tol << '\n'; + std::cout << "Verbose: " << data.verbose << '\n'; } } } diff --git a/palace/utils/configfile.hpp b/palace/utils/configfile.hpp index b1e26eeb4..a9bafe6be 100644 --- a/palace/utils/configfile.hpp +++ b/palace/utils/configfile.hpp @@ -510,6 +510,18 @@ struct WavePortData // List of boundary attributes for this wave port. std::vector attributes = {}; + + // Maximum number of iterations in linear solver. + int ksp_max_its = 45; + + // Tolerance for linear solver. + double ksp_tol = 1e-8; + + // Tolerance for eigenvalue solver. + double eig_tol = 1e-6; + + // Print level for linear and eigenvalue solvers. + int verbose = 0; }; struct WavePortBoundaryData : public internal::DataMap @@ -674,7 +686,7 @@ struct EigenSolverData // Maximum iterations for eigenvalue solver. int max_it = -1; - // Eigensolver subspace dimension or maximum dimension before restart. + // Eigenvalue solver subspace dimension or maximum dimension before restart. int max_size = -1; // Desired number of eigenmodes. diff --git a/scripts/schema/config/boundaries.json b/scripts/schema/config/boundaries.json index 539cd5d48..ec563245c 100644 --- a/scripts/schema/config/boundaries.json +++ b/scripts/schema/config/boundaries.json @@ -134,7 +134,11 @@ "Offset": { "type": "number", "minimum": 0.0 }, "SolverType": { "type": "string" }, "Excitation": { "type": "boolean" }, - "Active": { "type": "boolean" } + "Active": { "type": "boolean" }, + "MaxIts": { "type": "integer", "exclusiveMinimum": 0 }, + "KSPTol": { "type": "number", "exclusiveMinimum": 0.0 }, + "EigenTol": { "type": "number", "exclusiveMinimum": 0.0 }, + "Verbose": { "type": "integer", "minimum": 0.0 } } } },