23 double matrixNorm(std::unique_ptr<mfem::HypreParMatrix>& K)
25 mfem::HypreParMatrix* H = K.get();
26 hypre_ParCSRMatrix* Hhypre =
static_cast<hypre_ParCSRMatrix*
>(*H);
28 hypre_ParCSRMatrixNormFro(Hhypre, &Hfronorm);
35 auto K_T = std::unique_ptr<mfem::HypreParMatrix>(K->Transpose());
38 mfem::HypreParMatrix* H = K_T.get();
39 hypre_ParCSRMatrix* Hhypre =
static_cast<hypre_ParCSRMatrix*
>(*H);
41 hypre_ParCSRMatrixNormFro(Hhypre, &Hfronorm);
50 auto* amg_prec =
dynamic_cast<mfem::HypreBoomerAMG*
>(mfem_solver);
52 amg_prec->SetSystemsOptions(u.
space().GetVDim(), smith::ordering == mfem::Ordering::byNODES);
55 #ifdef SMITH_USE_PETSC
56 auto* space_dep_pc =
dynamic_cast<smith::mfem_ext::PetscPreconditionerSpaceDependent*
>(mfem_solver);
60 mfem::ParFiniteElementSpace*
space =
const_cast<mfem::ParFiniteElementSpace*
>(&u.
space());
61 space_dep_pc->SetFESpace(
space);
67 : mfem_solver(std::move(s)), mfem_preconditioner(std::move(p))
79 std::function<std::unique_ptr<mfem::HypreParMatrix>(
const FiniteElementState&)> jacobian)
const
83 auto du = std::make_shared<FiniteElementState>(u.
space(),
"u");
85 auto Jptr = jacobian(u);
94 const FiniteElementDual& u_bar, std::unique_ptr<mfem::HypreParMatrix> jacobian_transposed)
const
98 auto ds = std::make_shared<FiniteElementState>(u_bar.
space(),
"ds");
105 : nonlinear_solver_(std::move(s))
117 std::function<std::unique_ptr<mfem::HypreParMatrix>(
const FiniteElementState&)> jacobian)
const
121 auto u = std::make_shared<FiniteElementState>(u_guess);
123 auto residual_op_ = std::make_unique<mfem_ext::StdFunctionOperator>(
124 u->
space().TrueVSize(),
126 [&u, &equation](
const mfem::Vector& u_, mfem::Vector& r_) {
127 FiniteElementState uu(u->space(),
"uu");
132 [&u, &jacobian,
this](
const mfem::Vector& u_) -> mfem::Operator& {
133 FiniteElementState uu(u->space(),
"uu");
147 const FiniteElementDual& x_bar, std::unique_ptr<mfem::HypreParMatrix> jacobian_transposed)
const
151 auto ds = std::make_shared<FiniteElementState>(x_bar.
space(),
"ds");
153 linear_solver.SetOperator(*jacobian_transposed);
154 linear_solver.Mult(x_bar, *ds);
162 std::unique_ptr<mfem::Solver> p)
163 : mfem_solver(std::move(s)), mfem_preconditioner(std::move(p))
173 const std::vector<FieldPtr>& u_guesses,
174 std::function<std::vector<mfem::Vector>(
const std::vector<FieldPtr>&)> residual_funcs,
175 std::function<std::vector<std::vector<MatrixPtr>>(
const std::vector<FieldPtr>&)> jacobian_funcs)
const
179 int num_rows =
static_cast<int>(u_guesses.size());
180 SLIC_ERROR_IF(num_rows < 0,
"Number of residual rows must be non-negative");
182 mfem::Array<int> block_offsets;
183 block_offsets.SetSize(num_rows + 1);
184 block_offsets[0] = 0;
185 for (
int row_i = 0; row_i < num_rows; ++row_i) {
186 block_offsets[row_i + 1] = u_guesses[
static_cast<size_t>(row_i)]->
space().TrueVSize();
188 block_offsets.PartialSum();
190 auto block_du = std::make_unique<mfem::BlockVector>(block_offsets);
191 for (
int row_i = 0; row_i < num_rows; ++row_i) {
192 block_du->GetBlock(row_i) = *u_guesses[
static_cast<size_t>(row_i)];
195 auto residuals = residual_funcs(u_guesses);
196 auto block_r = std::make_unique<mfem::BlockVector>(block_offsets);
197 for (
int row_i = 0; row_i < num_rows; ++row_i) {
198 block_r->GetBlock(row_i) = residuals[
static_cast<size_t>(row_i)];
201 auto jacs = jacobian_funcs(u_guesses);
202 auto block_jac = std::make_unique<mfem::BlockOperator>(block_offsets);
203 for (
int i = 0; i < num_rows; ++i) {
204 for (
int j = 0; j < num_rows; ++j) {
205 block_jac->SetBlock(i, j, jacs[
static_cast<size_t>(i)][
static_cast<size_t>(j)].
get());
213 for (
int row_i = 0; row_i < num_rows; ++row_i) {
214 *u_guesses[
static_cast<size_t>(row_i)] -= block_du->GetBlock(row_i);
222 const std::vector<DualPtr>& u_bars, std::vector<std::vector<MatrixPtr>>& jacobian_transposed)
const
226 int num_rows =
static_cast<int>(u_bars.size());
227 SLIC_ERROR_IF(num_rows < 0,
"Number of residual rows must be non-negative");
229 std::vector<DifferentiableBlockSolver::FieldPtr> u_duals(
static_cast<size_t>(num_rows));
230 for (
int row_i = 0; row_i < num_rows; ++row_i) {
231 u_duals[
static_cast<size_t>(row_i)] = std::make_shared<DifferentiableBlockSolver::FieldT>(
232 u_bars[
static_cast<size_t>(row_i)]->space(),
"u_dual_" + std::to_string(row_i));
235 mfem::Array<int> block_offsets;
236 block_offsets.SetSize(num_rows + 1);
237 block_offsets[0] = 0;
238 for (
int row_i = 0; row_i < num_rows; ++row_i) {
239 block_offsets[row_i + 1] = u_bars[
static_cast<size_t>(row_i)]->
space().TrueVSize();
241 block_offsets.PartialSum();
243 auto block_ds = std::make_unique<mfem::BlockVector>(block_offsets);
246 auto block_r = std::make_unique<mfem::BlockVector>(block_offsets);
247 for (
int row_i = 0; row_i < num_rows; ++row_i) {
248 block_r->GetBlock(row_i) = *u_bars[
static_cast<size_t>(row_i)];
251 auto block_jac = std::make_unique<mfem::BlockOperator>(block_offsets);
252 for (
int i = 0; i < num_rows; ++i) {
253 for (
int j = 0; j < num_rows; ++j) {
254 block_jac->SetBlock(i, j, jacobian_transposed[
static_cast<size_t>(i)][
static_cast<size_t>(j)].
get());
262 for (
int row_i = 0; row_i < num_rows; ++row_i) {
263 *u_duals[
static_cast<size_t>(row_i)] = block_ds->GetBlock(row_i);
270 : nonlinear_solver_(std::move(s))
280 const std::vector<FieldPtr>& u_guesses,
281 std::function<std::vector<mfem::Vector>(
const std::vector<FieldPtr>&)> residual_funcs,
282 std::function<std::vector<std::vector<MatrixPtr>>(
const std::vector<FieldPtr>&)> jacobian_funcs)
const
286 int num_rows =
static_cast<int>(u_guesses.size());
287 SLIC_ERROR_IF(num_rows < 0,
"Number of residual rows must be non-negative");
289 mfem::Array<int> block_offsets;
290 block_offsets.SetSize(num_rows + 1);
291 block_offsets[0] = 0;
292 for (
int row_i = 0; row_i < num_rows; ++row_i) {
293 block_offsets[row_i + 1] = u_guesses[
static_cast<size_t>(row_i)]->
space().TrueVSize();
295 block_offsets.PartialSum();
297 auto block_u = std::make_unique<mfem::BlockVector>(block_offsets);
298 for (
int row_i = 0; row_i < num_rows; ++row_i) {
299 block_u->GetBlock(row_i) = *u_guesses[
static_cast<size_t>(row_i)];
302 auto block_r = std::make_unique<mfem::BlockVector>(block_offsets);
304 auto residual_op_ = std::make_unique<mfem_ext::StdFunctionOperator>(
306 [&residual_funcs, num_rows, &u_guesses, &block_r](
const mfem::Vector& u_, mfem::Vector& r_) {
307 const mfem::BlockVector* u = dynamic_cast<const mfem::BlockVector*>(&u_);
308 SLIC_ERROR_IF(!u,
"Invalid u cast in block differentiable solver to a block vector");
309 for (int row_i = 0; row_i < num_rows; ++row_i) {
310 *u_guesses[static_cast<size_t>(row_i)] = u->GetBlock(row_i);
312 auto residuals = residual_funcs(u_guesses);
313 SLIC_ERROR_IF(!block_r,
"Invalid r cast in block differentiable solver to a block vector");
314 for (
int row_i = 0; row_i < num_rows; ++row_i) {
315 auto r = residuals[
static_cast<size_t>(row_i)];
316 block_r->GetBlock(row_i) = r;
320 [
this, &block_offsets, &u_guesses, jacobian_funcs, num_rows](
const mfem::Vector& u_) -> mfem::Operator& {
321 const mfem::BlockVector* u =
dynamic_cast<const mfem::BlockVector*
>(&u_);
322 SLIC_ERROR_IF(!u,
"Invalid u cast in block differentiable solver to a block vector");
323 for (
int row_i = 0; row_i < num_rows; ++row_i) {
324 *u_guesses[
static_cast<size_t>(row_i)] = u->GetBlock(row_i);
326 block_jac_ = std::make_unique<mfem::BlockOperator>(block_offsets);
327 matrix_of_jacs_ = jacobian_funcs(u_guesses);
328 for (
int i = 0; i < num_rows; ++i) {
329 for (
int j = 0; j < num_rows; ++j) {
330 auto& J = matrix_of_jacs_[
static_cast<size_t>(i)][
static_cast<size_t>(j)];
332 block_jac_->SetBlock(i, j, J.get());
338 nonlinear_solver_->setOperator(*residual_op_);
339 nonlinear_solver_->solve(*block_u);
341 for (
int row_i = 0; row_i < num_rows; ++row_i) {
342 *u_guesses[
static_cast<size_t>(row_i)] = block_u->GetBlock(row_i);
348 std::vector<DifferentiableBlockSolver::FieldPtr> NonlinearDifferentiableBlockSolver::solveAdjoint(
349 const std::vector<DualPtr>& u_bars, std::vector<std::vector<MatrixPtr>>& jacobian_transposed)
const
353 int num_rows =
static_cast<int>(u_bars.size());
354 SLIC_ERROR_IF(num_rows < 0,
"Number of residual rows must be non-negative");
356 std::vector<DifferentiableBlockSolver::FieldPtr> u_duals(
static_cast<size_t>(num_rows));
357 for (
int row_i = 0; row_i < num_rows; ++row_i) {
358 u_duals[
static_cast<size_t>(row_i)] = std::make_shared<DifferentiableBlockSolver::FieldT>(
359 u_bars[
static_cast<size_t>(row_i)]->space(),
"u_dual_" + std::to_string(row_i));
362 mfem::Array<int> block_offsets;
363 block_offsets.SetSize(num_rows + 1);
364 block_offsets[0] = 0;
365 for (
int row_i = 0; row_i < num_rows; ++row_i) {
366 block_offsets[row_i + 1] = u_bars[
static_cast<size_t>(row_i)]->
space().TrueVSize();
368 block_offsets.PartialSum();
370 auto block_ds = std::make_unique<mfem::BlockVector>(block_offsets);
373 auto block_r = std::make_unique<mfem::BlockVector>(block_offsets);
374 for (
int row_i = 0; row_i < num_rows; ++row_i) {
375 block_r->GetBlock(row_i) = *u_bars[
static_cast<size_t>(row_i)];
378 auto block_jac = std::make_unique<mfem::BlockOperator>(block_offsets);
379 for (
int i = 0; i < num_rows; ++i) {
380 for (
int j = 0; j < num_rows; ++j) {
381 block_jac->SetBlock(i, j, jacobian_transposed[
static_cast<size_t>(i)][
static_cast<size_t>(j)].
get());
385 auto& linear_solver = nonlinear_solver_->linearSolver();
386 linear_solver.SetOperator(*block_jac);
387 linear_solver.Mult(*block_r, *block_ds);
389 for (
int row_i = 0; row_i < num_rows; ++row_i) {
390 *u_duals[
static_cast<size_t>(row_i)] = block_ds->GetBlock(row_i);
400 return std::make_shared<smith::LinearDifferentiableSolver>(std::move(linear_solver), std::move(precond));
407 auto solid_solver = std::make_unique<EquationSolver>(nonlinear_opts, linear_opts, mesh.
getComm());
408 return std::make_shared<NonlinearDifferentiableSolver>(std::move(solid_solver));
414 auto solid_solver = std::make_unique<EquationSolver>(nonlinear_opts, linear_opts, mesh.
getComm());
415 return std::make_shared<NonlinearDifferentiableBlockSolver>(std::move(solid_solver));
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.
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::unique_ptr< mfem::Solver > mfem_preconditioner
stored mfem block preconditioner
std::vector< FieldPtr > solveAdjoint(const std::vector< DualPtr > &u_bars, std::vector< std::vector< MatrixPtr >> &jacobian_transposed) const override
This is an overloaded member function, provided for convenience. It differs from the above function o...
std::unique_ptr< mfem::Solver > mfem_solver
stored mfem block solver
LinearDifferentiableBlockSolver(std::unique_ptr< mfem::Solver > s, std::unique_ptr< mfem::Solver > p)
Construct from a linear solver and linear block precondition which may be used by the linear solver.
void completeSetup(const std::vector< FieldT > &us) override
This is an overloaded member function, provided for convenience. It differs from the above function o...
std::unique_ptr< mfem::Solver > mfem_preconditioner
optionally used preconditioner
std::shared_ptr< smith::FiniteElementState > solve(const smith::FiniteElementState &u_guess, std::function< mfem::Vector(const smith::FiniteElementState &)> equation, std::function< std::unique_ptr< mfem::HypreParMatrix >(const smith::FiniteElementState &)> jacobian) const override
This is an overloaded member function, provided for convenience. It differs from the above function o...
void completeSetup(const smith::FiniteElementState &u) override
This is an overloaded member function, provided for convenience. It differs from the above function o...
std::unique_ptr< mfem::Solver > mfem_solver
linear solver
LinearDifferentiableSolver(std::unique_ptr< mfem::Solver > s, std::unique_ptr< mfem::Solver > p)
Construct from a linear solver and linear precondition which may also be used by a nonlinear solver.
std::shared_ptr< smith::FiniteElementState > solveAdjoint(const smith::FiniteElementDual &u_bar, std::unique_ptr< mfem::HypreParMatrix > jacobian_transposed) const override
This is an overloaded member function, provided for convenience. It differs from the above function o...
Helper class for constructing a mesh consistent with Smith.
MPI_Comm getComm() const
Returns parallel communicator.
void completeSetup(const std::vector< FieldT > &us) override
This is an overloaded member function, provided for convenience. It differs from the above function o...
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...
NonlinearDifferentiableBlockSolver(std::unique_ptr< EquationSolver > s)
Construct from a linear solver and linear block precondition which may be used by the linear solver.
std::shared_ptr< smith::FiniteElementState > solveAdjoint(const smith::FiniteElementDual &u_bar, std::unique_ptr< mfem::HypreParMatrix > jacobian_transposed) const override
This is an overloaded member function, provided for convenience. It differs from the above function o...
void completeSetup(const smith::FiniteElementState &u) override
This is an overloaded member function, provided for convenience. It differs from the above function o...
std::unique_ptr< mfem::HypreParMatrix > J_
stored linearized Jacobian matrix for memory reuse
void clearMemory() const override
This is an overloaded member function, provided for convenience. It differs from the above function o...
NonlinearDifferentiableSolver(std::unique_ptr< EquationSolver > s)
Consruct from a smith nonlinear EquationSolver.
std::unique_ptr< EquationSolver > nonlinear_solver_
the nonlinear equation solver used for the forward pass
std::shared_ptr< smith::FiniteElementState > solve(const smith::FiniteElementState &u_guess, std::function< mfem::Vector(const smith::FiniteElementState &)> equation, std::function< std::unique_ptr< mfem::HypreParMatrix >(const smith::FiniteElementState &)> jacobian) const override
This is an overloaded member function, provided for convenience. It differs from the above function o...
This file contains the declaration of the DifferentiableSolver interface.
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< NonlinearDifferentiableSolver > buildDifferentiableNonlinearSolver(NonlinearSolverOptions nonlinear_opts, LinearSolverOptions linear_opts, const smith::Mesh &mesh)
Create a differentiable nonlinear solver.
std::pair< std::unique_ptr< mfem::Solver >, std::unique_ptr< mfem::Solver > > buildLinearSolverAndPreconditioner(LinearSolverOptions linear_opts, MPI_Comm comm)
Build the linear solver and its associated preconditioner given a linear options struct.
constexpr T & get(variant< T0, T1 > &v)
Returns the variant member of specified type.
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.
mfem::ParFiniteElementSpace & space(FieldState field)
Get the space from the primal field of a field states.
void initializeSolver(mfem::Solver *mfem_solver, const smith::FiniteElementState &u)
Initialize mfem solver if near-nullspace is needed.
std::shared_ptr< LinearDifferentiableSolver > buildDifferentiableLinearSolver(LinearSolverOptions linear_opts, const smith::Mesh &mesh)
Create a differentiable linear solver.
std::shared_ptr< NonlinearDifferentiableBlockSolver > buildDifferentiableNonlinearBlockSolver(NonlinearSolverOptions nonlinear_opts, LinearSolverOptions linear_opts, const smith::Mesh &mesh)
Create a differentiable nonlinear block solver.
#define SMITH_MARK_FUNCTION
This file contains enumerations and record types for physics solver configuration.
Classes for defining an mfem::Operator with std::functions.
Parameters for an iterative linear solution scheme.
Nonlinear solution scheme parameters.