https://mooseframework.inl.gov
Loading...
Searching...
No Matches
AdjointSolve.C
Go to the documentation of this file.
1//* This file is part of the MOOSE framework
2//* https://mooseframework.inl.gov
3//*
4//* All rights reserved, see COPYRIGHT for full restrictions
5//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6//*
7//* Licensed under LGPL 2.1, please see LICENSE for details
8//* https://www.gnu.org/licenses/lgpl-2.1.html
9
10#include "AdjointSolve.h"
12
13#include "FEProblem.h"
14#include "NonlinearSystemBase.h"
15#include "NonlinearSystem.h"
16#include "NodalBCBase.h"
17#include "Executioner.h"
18
19#include "libmesh/dof_map.h"
20#include "libmesh/fuzzy_equals.h"
21#include "libmesh/petsc_matrix.h"
22#include "libmesh/petsc_vector.h"
23#include "petscmat.h"
24
27{
29 params.addRequiredParam<std::vector<SolverSystemName>>(
30 "forward_system",
31 "Name of the nonlinear system representing the forward problem. Multiple and linear solver "
32 "systems are not currently supported.");
33 params.addRequiredParam<NonlinearSystemName>(
34 "adjoint_system", "Name of the system representing the adjoint problem.");
35 return params;
36}
37
39 : SolveObject(ex),
40 _forward_sys_num(
41 _problem.nlSysNum(getParam<std::vector<SolverSystemName>>("forward_system")[0])),
42 _adjoint_sys_num(_problem.nlSysNum(getParam<NonlinearSystemName>("adjoint_system"))),
43 _nl_forward(_problem.getNonlinearSystemBase(_forward_sys_num)),
44 _nl_adjoint(_problem.getNonlinearSystemBase(_adjoint_sys_num))
45{
46 // Disallow vectors of systems
47 if (getParam<std::vector<SolverSystemName>>("forward_system").size() != 1)
48 paramError("forward_system",
49 "Multiple nonlinear forward systems is not supported at the moment");
50
51 // These should never be hit, but just in case
52 if (!dynamic_cast<NonlinearSystem *>(&_nl_forward))
53 paramError("forward_system", "Forward system does not appear to be a 'NonlinearSystem'.");
54 if (!dynamic_cast<NonlinearSystem *>(&_nl_adjoint))
55 paramError("adjoint_system", "Adjoint system does not appear to be a 'NonlinearSystem'.");
56 // Adjoint system should never perform its own automatic scaling. Scaling factors from the forward
57 // system are applied.
59
60 // We need to force the forward system to have a scaling vector. This is
61 // in case a user provides scaling for an individual variables but doesn't have any
62 // AD objects.
64
65 // Set the solver options for the adjoint system
66 mooseAssert(_problem.numSolverSystems() > 1,
67 "We should have forward and adjoint systems as evidenced by our initialization list");
68 const auto prefix = _nl_adjoint.prefix();
71 // Set solver parameter prefix
72 auto & solver_params = _problem.solverParams(_nl_adjoint.number());
73 solver_params._prefix = prefix;
74 solver_params._solver_sys_num = _nl_adjoint.number();
75}
76
77bool
79{
80 TIME_SECTION("execute", 1, "Executing adjoint problem", false);
81
82 mooseAssert(!_inner_solve,
83 "I don't see any code path in which this class winds up with an inner solve");
84
85 // enforce PETSc options are passed to the adjoint solve as well, as set in the user file
86 auto & petsc_options = _problem.getPetscOptions();
87 auto & pars = _problem.solverParams(_nl_adjoint.number());
88 Moose::PetscSupport::petscSetOptions(petsc_options, pars);
89
91
93 {
94 _console << "MultiApps failed to converge on ADJOINT_TIMESTEP_BEGIN!" << std::endl;
95 return false;
96 }
97 // Output results between the forward and adjoint solve.
99
101
102 // Convenient references
103 // Adjoint matrix, solution, and right-hand-side
104 auto & matrix = cast_ref<ImplicitSystem &>(_nl_forward.system()).get_system_matrix();
105 auto & solution = _nl_adjoint.solution();
107 // Linear solver parameters
108 auto & es = _problem.es();
109 const auto tol = es.parameters.get<Real>("linear solver tolerance");
110 const auto maxits = es.parameters.get<unsigned int>("linear solver maximum iterations");
111 // Linear solver for adjoint system
112 auto & solver = *cast_ref<ImplicitSystem &>(_nl_adjoint.system()).get_linear_solver();
113
114 // Assemble adjoint system by evaluating the forward Jacobian, computing the adjoint
115 // residual/source, and homogenizing nodal BCs
116 assembleAdjointSystem(matrix, solution, rhs);
117 applyNodalBCs(matrix, solution, rhs);
118
119 // Solve the adjoint system
120 solver.adjoint_solve(matrix, solution, rhs, tol, maxits);
121
122 // For scaling of the forward problem we need to apply correction factor
123 solution *= _nl_forward.getVector("scaling_factors");
124
125 // Hanging-node (and other DofMap) constraints are not applied by the raw transpose solve above,
126 // which leaves constrained dofs at zero, so back-substitute them here. The vector being
127 // constrained belongs to the adjoint system, not the forward system that owns the matrix.
129
131 if (solver.get_converged_reason() < 0)
132 {
133 _console << "Adjoint solve failed to converge with reason: "
134 << Utility::enum_to_string(solver.get_converged_reason()) << std::endl;
135 return false;
136 }
137
140 {
141 _console << "MultiApps failed to converge on ADJOINT_TIMESTEP_END!" << std::endl;
142 return false;
143 }
144
145 return true;
146}
147
148void
149AdjointSolve::assembleAdjointSystem(SparseMatrix<Number> & matrix,
150 const NumericVector<Number> & /*solution*/,
151 NumericVector<Number> & rhs)
152{
153
155
158 rhs.close();
159 rhs.scale(-1.0);
160}
161
162void
163AdjointSolve::applyNodalBCs(SparseMatrix<Number> & matrix,
164 const NumericVector<Number> & solution,
165 NumericVector<Number> & rhs)
166{
167 std::vector<dof_id_type> nbc_dofs;
168 auto & nbc_warehouse = _nl_forward.getNodalBCWarehouse();
169 if (nbc_warehouse.hasActiveObjects())
170 {
171 for (const auto & bnode : *_mesh.getBoundaryNodeRange())
172 {
173 BoundaryID boundary_id = bnode->_bnd_id;
174 Node * node = bnode->_node;
175
176 if (!nbc_warehouse.hasActiveBoundaryObjects(boundary_id) ||
177 node->processor_id() != processor_id())
178 continue;
179
180 for (const auto & bc : nbc_warehouse.getActiveBoundaryObjects(boundary_id))
181 if (bc->shouldApply())
182 for (unsigned int c = 0; c < bc->variable().count(); ++c)
183 nbc_dofs.push_back(node->dof_number(_forward_sys_num, bc->variable().number() + c, 0));
184 }
185
186 // Petsc has a nice interface for zeroing rows and columns, so we'll use it
187 auto petsc_matrix = dynamic_cast<PetscMatrix<Number> *>(&matrix);
188 auto petsc_solution = dynamic_cast<const PetscVector<Number> *>(&solution);
189 auto petsc_rhs = dynamic_cast<PetscVector<Number> *>(&rhs);
190 if (petsc_matrix && petsc_solution && petsc_rhs)
191 LibmeshPetscCall(MatZeroRowsColumns(petsc_matrix->mat(),
192 cast_int<PetscInt>(nbc_dofs.size()),
193 libMesh::numeric_petsc_cast(nbc_dofs.data()),
194 1.0,
195 petsc_solution->vec(),
196 petsc_rhs->vec()));
197 else
198 mooseError("Using PETSc matrices and vectors is required for applying homogenized boundary "
199 "conditions.");
200 }
201}
202
203void
205{
206 const auto adj_vars = _nl_adjoint.getVariables(0);
207 for (const auto & adj_var : adj_vars)
208 // If the user supplies any scaling factors for individual variables the
209 // adjoint system won't be consistent.
210 if (!libMesh::absolute_fuzzy_equals(adj_var->scalingFactor(), 1.0))
212 "User cannot supply scaling factors for adjoint variables. Adjoint system is scaled "
213 "automatically by the forward system.");
214
215 // This is to prevent automatic scaling of the adjoint system. Scaling is
216 // taken from the forward system
217 if (_nl_adjoint.hasVector("scaling_factors"))
218 _nl_adjoint.removeVector("scaling_factors");
219
220 // Main thing is that the number of dofs in each system is the same
223 "The forward and adjoint systems do not seem to be the same size. This could be due to (1) "
224 "the number of variables added to each system is not the same, (2) variables do not have "
225 "consistent family/order, (3) variables do not have the same block restriction.");
226}
boundary_id_type BoundaryID
const double tol
InputParameters emptyInputParameters()
void applyNodalBCs(SparseMatrix< Number > &matrix, const NumericVector< Number > &solution, NumericVector< Number > &rhs)
Helper function for applying nodal BCs to the adjoint matrix and RHS.
virtual bool solve() override
Solve the adjoint system with the following procedure:
const unsigned int _forward_sys_num
The number of the nonlinear system representing the forward model.
NonlinearSystemBase & _nl_forward
The nonlinear system representing the forward model.
const unsigned int _adjoint_sys_num
The number of the nonlinear system representing the adjoint model.
AdjointSolve(Executioner &ex)
virtual void assembleAdjointSystem(SparseMatrix< Number > &matrix, const NumericVector< Number > &solution, NumericVector< Number > &rhs)
Assembles adjoint system.
NonlinearSystemBase & _nl_adjoint
The nonlinear system representing the adjoint model.
void checkIntegrity()
Checks whether the forward and adjoint systems are consistent.
static InputParameters validParams()
const ConsoleStream _console
virtual void computeJacobian(const NumericVector< libMesh::Number > &soln, libMesh::SparseMatrix< libMesh::Number > &jacobian, const unsigned int nl_sys_num)
virtual libMesh::EquationSystems & es() override
virtual std::size_t numSolverSystems() const override
void setCurrentNonlinearSystem(const unsigned int nl_sys_num)
SolverParams & solverParams(unsigned int solver_sys_num=0)
bool execMultiApps(ExecFlagType type, bool auto_advance=true)
virtual void computeResidualTag(const NumericVector< libMesh::Number > &soln, NumericVector< libMesh::Number > &residual, TagID tag)
virtual void execute(const ExecFlagType &exec_type)
Moose::PetscSupport::PetscOptions & getPetscOptions()
virtual void outputStep(ExecFlagType type)
void addRequiredParam(const std::string &name, const std::string &doc_string)
void paramError(const std::string &param, Args... args) const
void mooseError(Args &&... args) const
const T & getParam(const std::string &name) const
libMesh::StoredRange< MooseMesh::const_bnd_node_iterator, const BndNode * > * getBoundaryNodeRange()
NumericVector< Number > & getResidualNonTimeVector()
const MooseObjectTagWarehouse< NodalBCBase > & getNodalBCWarehouse() const
TagID nonTimeVectorTag() const override
virtual libMesh::System & system() override
FEProblemBase & _problem
SolveObject * _inner_solve
MooseMesh & _mesh
std::string _prefix
virtual const NumericVector< Number > *const & currentSolution() const override final
bool automaticScaling() const
std::string prefix() const
bool hasVector(const std::string &tag_name) const
unsigned int number() const
void removeVector(const std::string &name)
virtual NumericVector< Number > & getVector(const std::string &name)
const std::vector< MooseVariableFieldBase * > & getVariables(THREAD_ID tid)
NumericVector< Number > & solution()
void enforce_constraints_exactly(const System &system, NumericVector< Number > *v=nullptr, bool homogeneous=false) const
processor_id_type processor_id() const
const T & get(std::string_view) const
dof_id_type n_dofs() const
const DofMap & get_dof_map() const
void petscSetOptions(const PetscOptions &po, const SolverParams &solver_params, FEProblemBase *const problem=nullptr)
void setConvergedReasonFlags(FEProblemBase &fe_problem, std::string prefix)
void storePetscOptions(FEProblemBase &fe_problem, const std::string &prefix, const ParallelParamObject &param_object)
const ExecFlagType EXEC_ADJOINT_TIMESTEP_END
const ExecFlagType EXEC_ADJOINT_TIMESTEP_BEGIN
bool absolute_fuzzy_equals(const T &var1, const T2 &var2, const Real tol=TOLERANCE *TOLERANCE)
PetscInt * numeric_petsc_cast(const numeric_index_type *p)