https://mooseframework.inl.gov
PetscContactLineSearch.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 "PetscContactLineSearch.h"
11 #include "FEProblem.h"
12 #include "NonlinearSystem.h"
13 #include "libmesh/petsc_nonlinear_solver.h"
14 #include "libmesh/petsc_solver_exception.h"
15 #include <petscdm.h>
16 
18 
21 {
23 }
24 
26  : ContactLineSearchBase(parameters)
27 {
28  _solver = dynamic_cast<PetscNonlinearSolver<Real> *>(
29  _fe_problem.getNonlinearSystem(/*nl_sys_num=*/0).nonlinearSolver());
30  if (!_solver)
31  mooseError(
32  "This line search operates only with Petsc, so Petsc must be your nonlinear solver.");
33 }
34 
35 void
37 {
38  PetscBool changed_y = PETSC_FALSE, changed_w = PETSC_FALSE;
39  Vec X, F, Y, W, G, W1;
40  SNESLineSearch line_search;
41  PetscReal fnorm, xnorm, ynorm, gnorm;
42  PetscReal ksp_rtol, ksp_abstol, ksp_dtol;
43  PetscInt ksp_maxits;
44  KSP ksp;
45  SNES snes = _solver->snes();
46 
47  LibmeshPetscCall(SNESGetLineSearch(snes, &line_search));
48  LibmeshPetscCall(SNESLineSearchGetVecs(line_search, &X, &F, &Y, &W, &G));
49  LibmeshPetscCall(SNESLineSearchGetNorms(line_search, &xnorm, &fnorm, &ynorm));
50  LibmeshPetscCall(SNESLineSearchGetSNES(line_search, &snes));
51  LibmeshPetscCall(SNESLineSearchSetReason(line_search, SNES_LINESEARCH_SUCCEEDED));
52  LibmeshPetscCall(SNESGetKSP(snes, &ksp));
53  LibmeshPetscCall(KSPGetTolerances(ksp, &ksp_rtol, &ksp_abstol, &ksp_dtol, &ksp_maxits));
54  LibmeshPetscCall(VecDuplicate(W, &W1));
55 
56  if (!_user_ksp_rtol_set)
57  {
58  _user_ksp_rtol = ksp_rtol;
59  _user_ksp_rtol_set = true;
60  }
61 
62  ++_nl_its;
63 
64  /* precheck */
65  LibmeshPetscCall(SNESLineSearchPreCheck(line_search, X, Y, &changed_y));
66 
67  /* temporary update */
68  _contact_lambda = 1.;
69  LibmeshPetscCall(VecWAXPY(W, -_contact_lambda, Y, X));
70 
71  /* compute residual to determine whether contact state has changed since the last non-linear
72  * residual evaluation */
73  _current_contact_state.clear();
74  LibmeshPetscCall(SNESComputeFunction(snes, W, F));
75 #if PETSC_VERSION_LESS_THAN(3, 25, 0)
76  PetscBool domainerror;
77  LibmeshPetscCall(SNESGetFunctionDomainError(snes, &domainerror));
78  if (domainerror)
79  LibmeshPetscCall(SNESLineSearchSetReason(line_search, SNES_LINESEARCH_FAILED_DOMAIN));
80 #else
81  SNESConvergedReason reason;
82  LibmeshPetscCall(SNESGetConvergedReason(snes, &reason));
83  if (reason == SNES_DIVERGED_FUNCTION_DOMAIN)
84  LibmeshPetscCall(SNESLineSearchSetReason(line_search, SNES_LINESEARCH_FAILED_FUNCTION_DOMAIN));
85 #endif
86 
87  LibmeshPetscCall(VecNorm(F, NORM_2, &fnorm));
88  std::set<dof_id_type> contact_state_stored = _current_contact_state;
89  _current_contact_state.clear();
90  printContactInfo(contact_state_stored);
91 
92  if (_affect_ltol)
93  {
94  if (contact_state_stored != _old_contact_state)
95  {
96  LibmeshPetscCall(KSPSetTolerances(ksp, _contact_ltol, ksp_abstol, ksp_dtol, ksp_maxits));
97  _console << "Contact set changed since previous non-linear iteration!" << std::endl;
98  }
99  else
100  LibmeshPetscCall(KSPSetTolerances(ksp, _user_ksp_rtol, ksp_abstol, ksp_dtol, ksp_maxits));
101  }
102 
103  size_t ls_its = 0;
104  while (ls_its < _allowed_lambda_cuts)
105  {
106  _contact_lambda *= 0.5;
107  /* update */
108  LibmeshPetscCall(VecWAXPY(W1, -_contact_lambda, Y, X));
109 
110  LibmeshPetscCall(SNESComputeFunction(snes, W1, G));
111 #if PETSC_VERSION_LESS_THAN(3, 25, 0)
112  LibmeshPetscCall(SNESGetFunctionDomainError(snes, &domainerror));
113  if (domainerror)
114  LibmeshPetscCall(SNESLineSearchSetReason(line_search, SNES_LINESEARCH_FAILED_DOMAIN));
115 #else
116  LibmeshPetscCall(SNESGetConvergedReason(snes, &reason));
117  if (reason == SNES_DIVERGED_FUNCTION_DOMAIN)
118  LibmeshPetscCall(
119  SNESLineSearchSetReason(line_search, SNES_LINESEARCH_FAILED_FUNCTION_DOMAIN));
120 #endif
121 
122  LibmeshPetscCall(VecNorm(G, NORM_2, &gnorm));
123  if (gnorm < fnorm)
124  {
125  LibmeshPetscCall(VecCopy(G, F));
126  LibmeshPetscCall(VecCopy(W1, W));
127  fnorm = gnorm;
128  contact_state_stored.swap(_current_contact_state);
129  _current_contact_state.clear();
130  printContactInfo(contact_state_stored);
131  ++ls_its;
132  }
133  else
134  break;
135  }
136 
137  LibmeshPetscCall(VecScale(Y, _contact_lambda));
138  /* postcheck */
139  LibmeshPetscCall(SNESLineSearchPostCheck(line_search, X, Y, W, &changed_y, &changed_w));
140 
141  if (changed_y)
142  LibmeshPetscCall(VecWAXPY(W, -1., Y, X));
143 
144  if (changed_w || changed_y)
145  {
146  LibmeshPetscCall(SNESComputeFunction(snes, W, F));
147 #if PETSC_VERSION_LESS_THAN(3, 25, 0)
148  LibmeshPetscCall(SNESGetFunctionDomainError(snes, &domainerror));
149  if (domainerror)
150  LibmeshPetscCall(SNESLineSearchSetReason(line_search, SNES_LINESEARCH_FAILED_DOMAIN));
151 #else
152  LibmeshPetscCall(SNESGetConvergedReason(snes, &reason));
153  if (reason == SNES_DIVERGED_FUNCTION_DOMAIN)
154  LibmeshPetscCall(
155  SNESLineSearchSetReason(line_search, SNES_LINESEARCH_FAILED_FUNCTION_DOMAIN));
156 #endif
157  contact_state_stored.swap(_current_contact_state);
158  _current_contact_state.clear();
159  printContactInfo(contact_state_stored);
160  }
161 
162  /* copy the solution over */
163  LibmeshPetscCall(VecCopy(W, X));
164 
165  LibmeshPetscCall(SNESLineSearchComputeNorms(line_search));
166 
167  LibmeshPetscCall(VecDestroy(&W1));
168 
169  _old_contact_state = std::move(contact_state_stored);
170 }
bool _affect_ltol
Whether to modify the linear tolerance.
SNES snes(const char *name=nullptr)
virtual libMesh::NonlinearSolver< Number > * nonlinearSolver() override
std::set< dof_id_type > _old_contact_state
The old contact set.
Real _contact_ltol
What the linear tolerance should be while the contact state is changing.
std::set< dof_id_type > _current_contact_state
The current contact set.
virtual void lineSearch() override
FEProblem & _fe_problem
static const std::string F
Definition: NS.h:169
static const std::string G
Definition: NS.h:170
registerMooseObject("ContactApp", PetscContactLineSearch)
This class implements a custom line search for use with mechanical contact.
Petsc implementation of the contact line search (based on the Petsc LineSearchShell) ...
Real _contact_lambda
The multiplier of the newton step.
static InputParameters validParams()
PetscContactLineSearch(const InputParameters &parameters)
unsigned _allowed_lambda_cuts
How many times the linsearch is allowed to cut lambda.
void mooseError(Args &&... args) const
void printContactInfo(const std::set< dof_id_type > &contact_set)
Method for printing the contact information.
const ConsoleStream _console
Real _user_ksp_rtol
the linear tolerance set by the user in the input file
PetscNonlinearSolver< Real > * _solver
size_t _nl_its
static InputParameters validParams()
virtual NonlinearSystem & getNonlinearSystem(const unsigned int nl_sys_num) override
bool _user_ksp_rtol_set
Whether the user linear tolerance has been set yet in this object.