22 double matrixNorm(std::unique_ptr<mfem::HypreParMatrix>& K)
24 mfem::HypreParMatrix* H = K.get();
25 hypre_ParCSRMatrix* Hhypre =
static_cast<hypre_ParCSRMatrix*
>(*H);
27 hypre_ParCSRMatrixNormFro(Hhypre, &Hfronorm);
34 auto K_T = std::unique_ptr<mfem::HypreParMatrix>(K->Transpose());
37 mfem::HypreParMatrix* H = K_T.get();
38 hypre_ParCSRMatrix* Hhypre =
static_cast<hypre_ParCSRMatrix*
>(*H);
40 hypre_ParCSRMatrixNormFro(Hhypre, &Hfronorm);
46 std::optional<NonlinearSolverOptions> retained_nonlinear_options,
47 std::optional<LinearSolverOptions> retained_linear_options)
48 : nonlinear_solver_(std::move(s)),
52 retained_nonlinear_options_(std::move(retained_nonlinear_options)),
53 retained_linear_options_(std::move(retained_linear_options))
66 auto solver = std::make_unique<EquationSolver>(nonlinear_opts, linear_opts,
comm_);
67 return std::make_shared<NonlinearBlockSolver>(std::move(solver),
comm_, nonlinear_opts.absolute_tol,
68 nonlinear_opts.relative_tol, nonlinear_opts, linear_opts);
77 SLIC_ERROR_IF(!us[0],
"NonlinearBlockSolver::completeSetup received a null field");
78 const FieldT* best = us[0].get();
79 for (
const auto& u : us) {
80 SLIC_ERROR_IF(!u,
"NonlinearBlockSolver::completeSetup received a null field");
81 if (u->space().GetVDim() > best->
space().GetVDim()) best = u.get();
86 auto* amg_prec =
dynamic_cast<mfem::HypreBoomerAMG*
>(mfem_solver);
88 amg_prec->SetSystemsOptions(best->
space().GetVDim(), smith::ordering == mfem::Ordering::byNODES);
91 #ifdef SMITH_USE_PETSC
92 auto* space_dep_pc =
dynamic_cast<smith::mfem_ext::PetscPreconditionerSpaceDependent*
>(mfem_solver);
96 auto*
space =
const_cast<mfem::ParFiniteElementSpace*
>(&best->
space());
97 space_dep_pc->SetFESpace(
space);
103 const std::vector<mfem::Vector>& residuals,
117 const std::vector<FieldPtr>& u_guesses,
118 std::function<std::vector<mfem::Vector>(
const std::vector<FieldPtr>&)> residual_funcs,
119 std::function<std::vector<std::vector<MatrixPtr>>(
const std::vector<FieldPtr>&)> jacobian_funcs)
const
122 SLIC_ERROR_IF(!
nonlinear_solver_,
"NonlinearBlockSolver requires an EquationSolver instance before solve()");
126 int num_rows =
static_cast<int>(u_guesses.size());
127 SLIC_ERROR_IF(num_rows < 0,
"Number of residual rows must be non-negative");
129 mfem::Array<int> block_offsets;
130 block_offsets.SetSize(num_rows + 1);
131 block_offsets[0] = 0;
132 for (
int row_i = 0; row_i < num_rows; ++row_i) {
133 block_offsets[row_i + 1] = u_guesses[
static_cast<size_t>(row_i)]->
space().TrueVSize();
135 block_offsets.PartialSum();
136 auto block_u = std::make_unique<mfem::BlockVector>(block_offsets);
137 for (
int row_i = 0; row_i < num_rows; ++row_i) {
138 block_u->GetBlock(row_i) = *u_guesses[
static_cast<size_t>(row_i)];
141 auto block_r = std::make_unique<mfem::BlockVector>(block_offsets);
148 auto residual_op_ = std::make_unique<mfem_ext::StdFunctionOperator>(
150 [&residual_funcs, num_rows, &u_guesses, &block_r, &block_offsets](
const mfem::Vector& u_, mfem::Vector& r_) {
151 const mfem::BlockVector* u = dynamic_cast<const mfem::BlockVector*>(&u_);
152 mfem::BlockVector u_block_wrapper;
155 u_block_wrapper.Update(const_cast<double*>(u_.GetData()), block_offsets);
156 u = &u_block_wrapper;
158 for (
int row_i = 0; row_i < num_rows; ++row_i) {
159 *u_guesses[
static_cast<size_t>(row_i)] = u->GetBlock(row_i);
162 auto residuals = residual_funcs(u_guesses);
163 SLIC_ERROR_IF(!block_r,
"Invalid residual block cast to an mfem::BlockVector");
164 for (
int row_i = 0; row_i < num_rows; ++row_i) {
165 auto r = residuals[
static_cast<size_t>(row_i)];
166 block_r->GetBlock(row_i) = r;
170 [
this, &block_offsets, &u_guesses, jacobian_funcs, num_rows](
const mfem::Vector& u_) -> mfem::Operator& {
171 const mfem::BlockVector* u =
dynamic_cast<const mfem::BlockVector*
>(&u_);
172 mfem::BlockVector u_block_wrapper;
174 u_block_wrapper.Update(
const_cast<double*
>(u_.GetData()), block_offsets);
175 u = &u_block_wrapper;
177 for (
int row_i = 0; row_i < num_rows; ++row_i) {
178 *u_guesses[
static_cast<size_t>(row_i)] = u->GetBlock(row_i);
180 matrix_of_jacs_ = jacobian_funcs(u_guesses);
182 auto& J = matrix_of_jacs_[0][0];
183 SLIC_ERROR_IF(!J,
"Jacobian block (0,0) is null in single-block solve");
186 block_jac_ = std::make_unique<mfem::BlockOperator>(block_offsets);
187 for (
int i = 0; i < num_rows; ++i) {
188 for (
int j = 0; j < num_rows; ++j) {
189 auto& J = matrix_of_jacs_[
static_cast<size_t>(i)][
static_cast<size_t>(j)];
191 block_jac_->SetBlock(i, j, J.get());
197 nonlinear_solver_->setConvergenceTolerances(inner_tol_multiplier_ * abs_tol_, inner_tol_multiplier_ * rel_tol_,
199 nonlinear_solver_->setOperator(*residual_op_);
200 nonlinear_solver_->solve(*block_u);
202 for (
int row_i = 0; row_i < num_rows; ++row_i) {
203 *u_guesses[
static_cast<size_t>(row_i)] = block_u->GetBlock(row_i);
209 std::vector<NonlinearBlockSolverBase::FieldPtr> NonlinearBlockSolver::solveAdjoint(
210 const std::vector<DualPtr>& u_bars, std::vector<std::vector<MatrixPtr>>& jacobian_transposed)
const
214 int num_rows =
static_cast<int>(u_bars.size());
215 SLIC_ERROR_IF(num_rows < 0,
"Number of residual rows must be non-negative");
217 std::vector<NonlinearBlockSolverBase::FieldPtr> u_duals(
static_cast<size_t>(num_rows));
218 for (
int row_i = 0; row_i < num_rows; ++row_i) {
219 u_duals[
static_cast<size_t>(row_i)] = std::make_shared<NonlinearBlockSolverBase::FieldT>(
220 u_bars[
static_cast<size_t>(row_i)]->space(),
"u_dual_" + std::to_string(row_i));
223 auto& linear_solver = nonlinear_solver_->linearSolver();
227 linear_solver.SetOperator(*jacobian_transposed[0][0]);
228 linear_solver.Mult(*u_bars[0], *u_duals[0]);
232 mfem::Array<int> block_offsets;
233 block_offsets.SetSize(num_rows + 1);
234 block_offsets[0] = 0;
235 for (
int row_i = 0; row_i < num_rows; ++row_i) {
236 block_offsets[row_i + 1] = u_bars[
static_cast<size_t>(row_i)]->
space().TrueVSize();
238 block_offsets.PartialSum();
240 auto block_ds = std::make_unique<mfem::BlockVector>(block_offsets);
243 auto block_r = std::make_unique<mfem::BlockVector>(block_offsets);
244 for (
int row_i = 0; row_i < num_rows; ++row_i) {
245 block_r->GetBlock(row_i) = *u_bars[
static_cast<size_t>(row_i)];
248 auto block_jac = std::make_unique<mfem::BlockOperator>(block_offsets);
249 for (
int i = 0; i < num_rows; ++i) {
250 for (
int j = 0; j < num_rows; ++j) {
251 auto& J = jacobian_transposed[
static_cast<size_t>(i)][
static_cast<size_t>(j)];
253 block_jac->SetBlock(i, j, J.get());
258 linear_solver.SetOperator(*block_jac);
259 linear_solver.Mult(*block_r, *block_ds);
261 for (
int row_i = 0; row_i < num_rows; ++row_i) {
262 *u_duals[
static_cast<size_t>(row_i)] = block_ds->GetBlock(row_i);
272 auto solid_solver = std::make_unique<EquationSolver>(nonlinear_opts, linear_opts, mesh.
getComm());
273 return std::make_shared<NonlinearBlockSolver>(std::move(solid_solver), mesh.
getComm(), nonlinear_opts.
absolute_tol,
274 nonlinear_opts.
relative_tol, nonlinear_opts, linear_opts);
This file contains the declaration of the boundary condition manager class.
Class for encapsulating the dual vector space of a finite element space (i.e. the space of linear for...
Class for encapsulating the critical MFEM components of a primal finite element field.
mfem::ParFiniteElementSpace & space()
Returns a non-owning reference to the internal FESpace.
Helper class for constructing a mesh consistent with Smith.
MPI_Comm getComm() const
Returns parallel communicator.
bool is_setup_
Records if this block solver has its preconditioner initialized.
double abs_tol_
absolute residual tolerance for convergence check
ConvergenceStatus convergenceStatus(double tolerance_multiplier, const std::vector< mfem::Vector > &residuals) const
Evaluate convergence for this solver's configured tolerance using solver-owned convergence state.
std::unique_ptr< EquationSolver > nonlinear_solver_
the nonlinear equation solver used for the forward pass
std::vector< FieldPtr > solve(const std::vector< FieldPtr > &u_guesses, std::function< std::vector< mfem::Vector >(const std::vector< FieldPtr > &)> residuals, std::function< std::vector< std::vector< MatrixPtr >>(const std::vector< FieldPtr > &)> jacobians) const override
This is an overloaded member function, provided for convenience. It differs from the above function o...
std::optional< LinearSolverOptions > retained_linear_options_
retained linear config
void primeConvergenceContext(const std::vector< mfem::Vector > &residuals, NonlinearConvergenceContext &context) const override
This is an overloaded member function, provided for convenience. It differs from the above function o...
MPI_Comm comm_
MPI communicator for parallel norm computation.
NonlinearBlockSolver(std::unique_ptr< EquationSolver > s, MPI_Comm comm, double abs_tol=1e-12, double rel_tol=1e-8, std::optional< NonlinearSolverOptions > retained_nonlinear_options=std::nullopt, std::optional< LinearSolverOptions > retained_linear_options=std::nullopt)
Construct from a nonlinear equation solver.
std::optional< NonlinearSolverOptions > retained_nonlinear_options_
retained nonlinear config
void completeSetup(const std::vector< FieldPtr > &us) const
Initialize the preconditioner in case of vector problems.
double rel_tol_
relative residual tolerance for convergence check
std::shared_ptr< NonlinearBlockSolver > cloneFresh() const
Build a fresh solver instance from retained config.
This file contains the declaration of an equation solver wrapper.
This contains a class that represents the dual of a finite element vector space, i....
This file contains the declaration of structure that manages the MFEM objects that make up the state ...
Smith mesh class which assists in constructing the appropriate parallel mfem meshes and registering a...
Accelerator functionality.
std::shared_ptr< NonlinearBlockSolver > buildNonlinearBlockSolver(NonlinearSolverOptions nonlinear_opts, LinearSolverOptions linear_opts, const smith::Mesh &mesh)
Create an equation-backed nonlinear block solver.
double skewMatrixNorm(std::unique_ptr< mfem::HypreParMatrix > &K)
Utility to compute 0.5*norm(K-K.T)
double matrixNorm(std::unique_ptr< mfem::HypreParMatrix > &K)
Utility to compute the matrix norm.
std::vector< double > computeResidualBlockNorms(const std::vector< mfem::Vector > &residuals, MPI_Comm comm)
Compute one L2 norm per residual block.
ConvergenceStatus evaluateResidualConvergence(double tolerance_multiplier, double abs_tol, double rel_tol, const std::vector< double > &block_norms, NonlinearConvergenceContext &context)
Evaluate scalar nonlinear residual convergence.
mfem::ParFiniteElementSpace & space(FieldState field)
Get the space from the primal field of a field states.
This file contains nonlinear block solver interfaces and helpers.
#define SMITH_MARK_FUNCTION
Classes for defining an mfem::Operator with std::functions.
Detailed status from evaluating nonlinear residual convergence.
Parameters for an iterative linear solution scheme.
Stores initial residual norms used for relative nonlinear convergence checks.
Nonlinear solution scheme parameters.
double relative_tol
Relative tolerance.
double absolute_tol
Absolute tolerance.