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"
18#include "MooseVariableScalar.h"
19#include "Executioner.h"
20
21#include "libmesh/dof_map.h"
22#include "libmesh/fuzzy_equals.h"
23#include "libmesh/petsc_matrix.h"
24#include "libmesh/petsc_vector.h"
25#include "petscmat.h"
26
29{
31 params.addRequiredParam<std::vector<SolverSystemName>>(
32 "forward_system",
33 "Name of the nonlinear system representing the forward problem. Multiple and linear solver "
34 "systems are not currently supported.");
35 params.addRequiredParam<NonlinearSystemName>(
36 "adjoint_system", "Name of the system representing the adjoint problem.");
37 return params;
38}
39
41 : SolveObject(ex),
42 _forward_sys_num(
43 _problem.nlSysNum(getParam<std::vector<SolverSystemName>>("forward_system")[0])),
44 _adjoint_sys_num(_problem.nlSysNum(getParam<NonlinearSystemName>("adjoint_system"))),
45 _nl_forward(_problem.getNonlinearSystemBase(_forward_sys_num)),
46 _nl_adjoint(_problem.getNonlinearSystemBase(_adjoint_sys_num))
47{
48 // Disallow vectors of systems
49 if (getParam<std::vector<SolverSystemName>>("forward_system").size() != 1)
50 paramError("forward_system",
51 "Multiple nonlinear forward systems is not supported at the moment");
52
53 // These should never be hit, but just in case
54 if (!dynamic_cast<NonlinearSystem *>(&_nl_forward))
55 paramError("forward_system", "Forward system does not appear to be a 'NonlinearSystem'.");
56 if (!dynamic_cast<NonlinearSystem *>(&_nl_adjoint))
57 paramError("adjoint_system", "Adjoint system does not appear to be a 'NonlinearSystem'.");
58 // Adjoint system should never perform its own automatic scaling. Scaling factors from the forward
59 // system are applied.
61
62 // Set the solver options for the adjoint system
63 mooseAssert(_problem.numSolverSystems() > 1,
64 "We should have forward and adjoint systems as evidenced by our initialization list");
65 const auto prefix = _nl_adjoint.prefix();
68 // Set solver parameter prefix
69 auto & solver_params = _problem.solverParams(_nl_adjoint.number());
70 solver_params._prefix = prefix;
71 solver_params._solver_sys_num = _nl_adjoint.number();
72}
73
74bool
76{
77 TIME_SECTION("execute", 1, "Executing adjoint problem", false);
78
79 mooseAssert(!_inner_solve,
80 "I don't see any code path in which this class winds up with an inner solve");
81
82 // enforce PETSc options are passed to the adjoint solve as well, as set in the user file
83 auto & petsc_options = _problem.getPetscOptions();
84 auto & pars = _problem.solverParams(_nl_adjoint.number());
85 Moose::PetscSupport::petscSetOptions(petsc_options, pars);
86
88
90 {
91 _console << "MultiApps failed to converge on ADJOINT_TIMESTEP_BEGIN!" << std::endl;
92 return false;
93 }
94 // Output results between the forward and adjoint solve.
96
98
99 // Convenient references
100 // Adjoint matrix, solution, and right-hand-side
101 auto & matrix = cast_ref<ImplicitSystem &>(_nl_forward.system()).get_system_matrix();
102 auto & solution = _nl_adjoint.solution();
104 // Linear solver parameters
105 auto & es = _problem.es();
106 const auto tol = es.parameters.get<Real>("linear solver tolerance");
107 const auto maxits = es.parameters.get<unsigned int>("linear solver maximum iterations");
108 // Linear solver for adjoint system
109 auto & solver = *cast_ref<ImplicitSystem &>(_nl_adjoint.system()).get_linear_solver();
110
111 // Assemble adjoint system by evaluating the forward Jacobian, computing the adjoint
112 // residual/source, and homogenizing nodal BCs
113 assembleAdjointSystem(matrix, solution, rhs);
114 applyNodalBCs(matrix, solution, rhs);
115
116 // Solve the adjoint system
117 solver.adjoint_solve(matrix, solution, rhs, tol, maxits);
118
119 // The forward Jacobian has its rows scaled by the variable scaling factors S, so the transposed
120 // solve returns mu with (S J)^T mu = -g. The adjoint is therefore lambda = S mu.
121 const auto & dof_map = _nl_forward.dofMap();
122 auto scaling = solution.zero_clone();
123 std::vector<dof_id_type> dof_indices;
124 const auto fill_scaling = [&](const MooseVariableBase & var)
125 {
126 const auto & factors = var.arrayScalingFactor();
127 for (const auto i : make_range(var.count()))
128 {
129 dof_map.local_variable_indices(dof_indices, _mesh.getMesh(), var.number() + i);
130 for (const auto dof : dof_indices)
131 scaling->set(dof, factors[i]);
132 }
133 };
134 for (const auto * const var : _nl_forward.getVariables(0))
135 fill_scaling(*var);
136 for (const auto * const var : _nl_forward.getScalarVariables(0))
137 fill_scaling(*var);
138 scaling->close();
139 solution *= *scaling;
140
141 // Hanging-node (and other DofMap) constraints are not applied by the raw transpose solve above,
142 // which leaves constrained dofs at zero, so back-substitute them here. The vector being
143 // constrained belongs to the adjoint system, not the forward system that owns the matrix.
145
147 if (solver.get_converged_reason() < 0)
148 {
149 _console << "Adjoint solve failed to converge with reason: "
150 << Utility::enum_to_string(solver.get_converged_reason()) << std::endl;
151 return false;
152 }
153
156 {
157 _console << "MultiApps failed to converge on ADJOINT_TIMESTEP_END!" << std::endl;
158 return false;
159 }
160
161 return true;
162}
163
164void
165AdjointSolve::assembleAdjointSystem(SparseMatrix<Number> & matrix,
166 const NumericVector<Number> & /*solution*/,
167 NumericVector<Number> & rhs)
168{
169
171
174 rhs.close();
175 rhs.scale(-1.0);
176}
177
178void
179AdjointSolve::applyNodalBCs(SparseMatrix<Number> & matrix,
180 const NumericVector<Number> & solution,
181 NumericVector<Number> & rhs)
182{
183 std::vector<dof_id_type> nbc_dofs;
184 auto & nbc_warehouse = _nl_forward.getNodalBCWarehouse();
185 if (nbc_warehouse.hasActiveObjects())
186 {
187 for (const auto & bnode : *_mesh.getBoundaryNodeRange())
188 {
189 BoundaryID boundary_id = bnode->_bnd_id;
190 Node * node = bnode->_node;
191
192 if (!nbc_warehouse.hasActiveBoundaryObjects(boundary_id) ||
193 node->processor_id() != processor_id())
194 continue;
195
196 for (const auto & bc : nbc_warehouse.getActiveBoundaryObjects(boundary_id))
197 if (bc->shouldApply())
198 for (unsigned int c = 0; c < bc->variable().count(); ++c)
199 nbc_dofs.push_back(node->dof_number(_forward_sys_num, bc->variable().number() + c, 0));
200 }
201
202 // Petsc has a nice interface for zeroing rows and columns, so we'll use it
203 auto petsc_matrix = dynamic_cast<PetscMatrix<Number> *>(&matrix);
204 auto petsc_solution = dynamic_cast<const PetscVector<Number> *>(&solution);
205 auto petsc_rhs = dynamic_cast<PetscVector<Number> *>(&rhs);
206 if (petsc_matrix && petsc_solution && petsc_rhs)
207 LibmeshPetscCall(MatZeroRowsColumns(petsc_matrix->mat(),
208 cast_int<PetscInt>(nbc_dofs.size()),
209 libMesh::numeric_petsc_cast(nbc_dofs.data()),
210 1.0,
211 petsc_solution->vec(),
212 petsc_rhs->vec()));
213 else
214 mooseError("Using PETSc matrices and vectors is required for applying homogenized boundary "
215 "conditions.");
216 }
217}
218
219void
221{
222 const auto adj_vars = _nl_adjoint.getVariables(0);
223 for (const auto & adj_var : adj_vars)
224 // If the user supplies any scaling factors for individual variables the
225 // adjoint system won't be consistent.
226 if (!libMesh::absolute_fuzzy_equals(adj_var->scalingFactor(), 1.0))
228 "User cannot supply scaling factors for adjoint variables. Adjoint system is scaled "
229 "automatically by the forward system.");
230
231 // Main thing is that the number of dofs in each system is the same
234 "The forward and adjoint systems do not seem to be the same size. This could be due to (1) "
235 "the number of variables added to each system is not the same, (2) variables do not have "
236 "consistent family/order, (3) variables do not have the same block restriction.");
237}
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
MeshBase & getMesh()
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
unsigned int number() const
const std::vector< MooseVariableFieldBase * > & getVariables(THREAD_ID tid)
virtual libMesh::DofMap & dofMap()
NumericVector< Number > & solution()
const std::vector< MooseVariableScalar * > & getScalarVariables(THREAD_ID tid)
void enforce_constraints_exactly(const System &system, NumericVector< Number > *v=nullptr, bool homogeneous=false) const
void local_variable_indices(T &idx, const MeshBase &mesh, unsigned int var_num) 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)