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