PRISMS-PF  v2.1
solveIncrement.cc
Go to the documentation of this file.
1 //solveIncrement() method for MatrixFreePDE class
2 
3 #include "../../include/matrixFreePDE.h"
4 #include <deal.II/lac/solver_cg.h>
5 
6 //solve each time increment
7 template <int dim, int degree>
8 void MatrixFreePDE<dim,degree>::solveIncrement(bool skip_time_dependent){
9 
10  //log time
11  computing_timer.enter_section("matrixFreePDE: solveIncrements");
12  Timer time;
13  char buffer[200];
14 
15  // Get the RHS of the explicit equations
16  if (hasExplicitEquation && !skip_time_dependent){
17  computeExplicitRHS();
18  }
19 
20 
21  //solve for each field
22  for(unsigned int fieldIndex=0; fieldIndex<fields.size(); fieldIndex++){
23  currentFieldIndex = fieldIndex; // Used in computeLHS()
24 
25  // Add Neumann BC terms to the residual vector for the current field, if appropriate
26  // Currently commented out because it isn't working yet
27  //applyNeumannBCs();
28 
29  //Parabolic (first order derivatives in time) fields
30  if (fields[fieldIndex].pdetype==EXPLICIT_TIME_DEPENDENT && !skip_time_dependent){
31 
32  // Explicit-time step each DOF
33  // Takes advantage of knowledge that the length of solutionSet and residualSet is an integer multiple of the length of invM for vector variables
34  unsigned int invM_size = invM.local_size();
35  for (unsigned int dof=0; dof<solutionSet[fieldIndex]->local_size(); ++dof){
36  solutionSet[fieldIndex]->local_element(dof)= \
37  invM.local_element(dof%invM_size)*residualSet[fieldIndex]->local_element(dof);
38  }
39  // Set the Dirichelet values (hanging node constraints don't need to be distributed every time step, only at output)
40  if (has_Dirichlet_BCs){
41  constraintsDirichletSet[fieldIndex]->distribute(*solutionSet[fieldIndex]);
42  }
43  //computing_timer.enter_section("matrixFreePDE: updateExplicitGhosts");
44  solutionSet[fieldIndex]->update_ghost_values();
45  //computing_timer.exit_section("matrixFreePDE: updateExplicitGhosts");
46 
47  // Print update to screen and confirm that solution isn't nan
48  if (currentIncrement%userInputs.skip_print_steps==0){
49  double solution_L2_norm = solutionSet[fieldIndex]->l2_norm();
50 
51  sprintf(buffer, "field '%2s' [explicit solve]: current solution: %12.6e, current residual:%12.6e\n", \
52  fields[fieldIndex].name.c_str(), \
53  solution_L2_norm, \
54  residualSet[fieldIndex]->l2_norm());
55  pcout<<buffer;
56 
57  if (!numbers::is_finite(solution_L2_norm)){
58  sprintf(buffer, "ERROR: field '%s' solution is NAN. exiting.\n\n",
59  fields[fieldIndex].name.c_str());
60  pcout<<buffer;
61  exit(-1);
62  }
63 
64  }
65  }
66 
67  }
68 
69  // Now, update the non-explicit variables
70  // For the time being, this is just the elliptic equations, but implicit parabolic and auxilary equations should also be here
71  if (hasNonExplicitEquation){
72 
73  bool nonlinear_it_converged = false;
74  unsigned int nonlinear_it_index = 0;
75 
76  while (!nonlinear_it_converged){
77  nonlinear_it_converged = true; // Set to true here and will be set to false if any variable isn't converged
78 
79  // Update residualSet for the non-explicitly updated variables
80  //compute_nonexplicit_RHS()
81  // Ideally, I'd just do this for the non-explicit variables, but for now I'll do all of them
82  // this is a little redundant, but hopefully not too terrible
83  computeNonexplicitRHS();
84 
85  for(unsigned int fieldIndex=0; fieldIndex<fields.size(); fieldIndex++){
86  currentFieldIndex = fieldIndex; // Used in computeLHS()
87 
88  if ( (fields[fieldIndex].pdetype == IMPLICIT_TIME_DEPENDENT && !skip_time_dependent) || fields[fieldIndex].pdetype == TIME_INDEPENDENT){
89 
90  if (currentIncrement%userInputs.skip_print_steps==0 && userInputs.var_nonlinear[fieldIndex]){
91  sprintf(buffer, "field '%2s' [nonlinear solve]: current solution: %12.6e, current residual:%12.6e\n", \
92  fields[fieldIndex].name.c_str(), \
93  solutionSet[fieldIndex]->l2_norm(), \
94  residualSet[fieldIndex]->l2_norm());
95  pcout<<buffer;
96  }
97 
98  dealii::parallel::distributed::Vector<double> solution_diff = *solutionSet[fieldIndex];
99 
100  //apply Dirichlet BC's
101  // Loops through all DoF to which ones have Dirichlet BCs applied, replace the ones that do with the Dirichlet value
102  // This clears the residual where we want to apply Dirichlet BCs, otherwise the solver sees a positive residual
103  for (std::map<types::global_dof_index, double>::const_iterator it=valuesDirichletSet[fieldIndex]->begin(); it!=valuesDirichletSet[fieldIndex]->end(); ++it){
104  if (residualSet[fieldIndex]->in_local_range(it->first)){
105  (*residualSet[fieldIndex])(it->first) = 0.0;
106  }
107  }
108 
109  //solver controls
110  double tol_value;
111  if (userInputs.linear_solver_parameters.getToleranceType(fieldIndex) == ABSOLUTE_RESIDUAL){
112  tol_value = userInputs.linear_solver_parameters.getToleranceValue(fieldIndex);
113  }
114  else {
115  tol_value = userInputs.linear_solver_parameters.getToleranceValue(fieldIndex)*residualSet[fieldIndex]->l2_norm();
116  }
117 
118  SolverControl solver_control(userInputs.linear_solver_parameters.getMaxIterations(fieldIndex), tol_value);
119 
120  // Currently the only allowed solver is SolverCG, the SolverType input variable is a dummy
121  SolverCG<vectorType> solver(solver_control);
122 
123  //solve
124  try{
125  if (fields[fieldIndex].type == SCALAR){
126  dU_scalar=0.0;
127  solver.solve(*this, dU_scalar, *residualSet[fieldIndex], IdentityMatrix(solutionSet[fieldIndex]->size()));
128  }
129  else {
130  dU_vector=0.0;
131  solver.solve(*this, dU_vector, *residualSet[fieldIndex], IdentityMatrix(solutionSet[fieldIndex]->size()));
132  }
133  }
134  catch (...) {
135  pcout << "\nWarning: implicit solver did not converge as per set tolerances. consider increasing maxSolverIterations or decreasing solverTolerance.\n";
136  }
137 
138  if (userInputs.var_nonlinear[fieldIndex]){
139 
140  // Now that we have the calculated change in the solution, we need to select a damping coefficient
141  double damping_coefficient;
142 
143  if (userInputs.nonlinear_solver_parameters.getBacktrackDampingFlag(fieldIndex)){
144  vectorType solutionSet_old = *solutionSet[fieldIndex];
145  double residual_old = residualSet[fieldIndex]->l2_norm();
146 
147  damping_coefficient = 1.0;
148  bool damping_coefficient_found = false;
149  while (!damping_coefficient_found){
150  if (fields[fieldIndex].type == SCALAR){
151  solutionSet[fieldIndex]->sadd(1.0,damping_coefficient,dU_scalar);
152  }
153  else {
154  solutionSet[fieldIndex]->sadd(1.0,damping_coefficient,dU_vector);
155  }
156 
157  computeNonexplicitRHS();
158 
159  for (std::map<types::global_dof_index, double>::const_iterator it=valuesDirichletSet[fieldIndex]->begin(); it!=valuesDirichletSet[fieldIndex]->end(); ++it){
160  if (residualSet[fieldIndex]->in_local_range(it->first)){
161  (*residualSet[fieldIndex])(it->first) = 0.0;
162  }
163  }
164 
165  double residual_new = residualSet[fieldIndex]->l2_norm();
166 
167  if (currentIncrement%userInputs.skip_print_steps==0){
168  pcout << " Old residual: " << residual_old << " Damping Coeff: " << damping_coefficient << " New Residual: " << residual_new << std::endl;
169  }
170 
171  // An improved approach would use the Armijo–Goldstein condition to ensure a sufficent decrease in the residual. This way is just scales the residual.
172  if ( (residual_new < (residual_old*userInputs.nonlinear_solver_parameters.getBacktrackResidualDecreaseCoeff(fieldIndex))) || damping_coefficient < 1.0e-4){
173  damping_coefficient_found = true;
174  }
175  else{
176  damping_coefficient *= userInputs.nonlinear_solver_parameters.getBacktrackStepModifier(fieldIndex);
177  *solutionSet[fieldIndex] = solutionSet_old;
178  }
179  }
180  }
181  else{
182  damping_coefficient = userInputs.nonlinear_solver_parameters.getDefaultDampingCoefficient(fieldIndex);
183 
184  if (fields[fieldIndex].type == SCALAR){
185  solutionSet[fieldIndex]->sadd(1.0,damping_coefficient,dU_scalar);
186  }
187  else {
188  solutionSet[fieldIndex]->sadd(1.0,damping_coefficient,dU_vector);
189  }
190  }
191 
192  if (currentIncrement%userInputs.skip_print_steps==0){
193  double dU_norm;
194  if (fields[fieldIndex].type == SCALAR){
195  dU_norm = dU_scalar.l2_norm();
196  }
197  else {
198  dU_norm = dU_vector.l2_norm();
199  }
200  sprintf(buffer, "field '%2s' [implicit solve]: initial residual:%12.6e, current residual:%12.6e, nsteps:%u, tolerance criterion:%12.6e, solution: %12.6e, dU: %12.6e\n", \
201  fields[fieldIndex].name.c_str(), \
202  residualSet[fieldIndex]->l2_norm(), \
203  solver_control.last_value(), \
204  solver_control.last_step(), solver_control.tolerance(), solutionSet[fieldIndex]->l2_norm(), dU_norm);
205  pcout<<buffer;
206  }
207 
208  // Check to see if this individual variable has converged
209  if (userInputs.nonlinear_solver_parameters.getToleranceType(fieldIndex) == ABSOLUTE_SOLUTION_CHANGE){
210  double diff;
211 
212  if (fields[fieldIndex].type == SCALAR){
213  diff = dU_scalar.l2_norm();
214  }
215  else {
216  diff = dU_vector.l2_norm();
217  }
218  if (currentIncrement%userInputs.skip_print_steps==0){
219  pcout << "Relative difference between nonlinear iterations: " << diff << " " << nonlinear_it_index << " " << currentIncrement << std::endl;
220  }
221 
222  if (diff > userInputs.nonlinear_solver_parameters.getToleranceValue(fieldIndex) && nonlinear_it_index < userInputs.nonlinear_solver_parameters.getMaxIterations()){
223  nonlinear_it_converged = false;
224  }
225  }
226  else {
227  std::cerr << "PRISMS-PF Error: Nonlinear solver tolerance types other than ABSOLUTE_CHANGE have yet to be implemented." << std::endl;
228  }
229  }
230  else {
231  if (nonlinear_it_index ==0){
232 
233  if (fields[fieldIndex].type == SCALAR){
234  *solutionSet[fieldIndex] += dU_scalar;
235  }
236  else {
237  *solutionSet[fieldIndex] += dU_vector;
238  }
239 
240  if (currentIncrement%userInputs.skip_print_steps==0){
241  double dU_norm;
242  if (fields[fieldIndex].type == SCALAR){
243  dU_norm = dU_scalar.l2_norm();
244  }
245  else {
246  dU_norm = dU_vector.l2_norm();
247  }
248  sprintf(buffer, "field '%2s' [implicit solve]: initial residual:%12.6e, current residual:%12.6e, nsteps:%u, tolerance criterion:%12.6e, solution: %12.6e, dU: %12.6e\n", \
249  fields[fieldIndex].name.c_str(), \
250  residualSet[fieldIndex]->l2_norm(), \
251  solver_control.last_value(), \
252  solver_control.last_step(), solver_control.tolerance(), solutionSet[fieldIndex]->l2_norm(), dU_norm);
253  pcout<<buffer;
254  }
255  }
256 
257  }
258  }
259  else if (fields[fieldIndex].pdetype == AUXILIARY){
260 
261  if (userInputs.var_nonlinear[fieldIndex] || nonlinear_it_index == 0){
262 
263  // If the equation for this field is nonlinear, save the old solution
264  if (userInputs.var_nonlinear[fieldIndex]){
265  if (fields[fieldIndex].type == SCALAR){
266  dU_scalar = *solutionSet[fieldIndex];
267  }
268  else {
269  dU_vector = *solutionSet[fieldIndex];
270  }
271  }
272 
273  // Explicit-time step each DOF
274  // Takes advantage of knowledge that the length of solutionSet and residualSet is an integer multiple of the length of invM for vector variables
275  unsigned int invM_size = invM.local_size();
276  for (unsigned int dof=0; dof<solutionSet[fieldIndex]->local_size(); ++dof){
277  solutionSet[fieldIndex]->local_element(dof)= \
278  invM.local_element(dof%invM_size)*residualSet[fieldIndex]->local_element(dof);
279  }
280 
281  // Set the Dirichelet values (hanging node constraints don't need to be distributed every time step, only at output)
282  constraintsDirichletSet[fieldIndex]->distribute(*solutionSet[fieldIndex]);
283  solutionSet[fieldIndex]->update_ghost_values();
284 
285  // Print update to screen
286  if (currentIncrement%userInputs.skip_print_steps==0){
287  sprintf(buffer, "field '%2s' [auxiliary solve]: current solution: %12.6e, current residual:%12.6e\n", \
288  fields[fieldIndex].name.c_str(), \
289  solutionSet[fieldIndex]->l2_norm(), \
290  residualSet[fieldIndex]->l2_norm());
291  pcout<<buffer;
292  }
293 
294  // Check to see if this individual variable has converged
295  if (userInputs.var_nonlinear[fieldIndex]){
296  if (userInputs.nonlinear_solver_parameters.getToleranceType(fieldIndex) == ABSOLUTE_SOLUTION_CHANGE){
297 
298  double diff;
299 
300  if (fields[fieldIndex].type == SCALAR){
301  dU_scalar -= *solutionSet[fieldIndex];
302  diff = dU_scalar.l2_norm();
303  }
304  else {
305  dU_vector -= *solutionSet[fieldIndex];
306  diff = dU_vector.l2_norm();
307  }
308  if (currentIncrement%userInputs.skip_print_steps==0){
309  pcout << "Relative difference between nonlinear iterations: " << diff << " " << nonlinear_it_index << " " << currentIncrement << std::endl;
310  }
311 
312  if (diff > userInputs.nonlinear_solver_parameters.getToleranceValue(fieldIndex) && nonlinear_it_index < userInputs.nonlinear_solver_parameters.getMaxIterations()){
313  nonlinear_it_converged = false;
314  }
315 
316  }
317  else {
318  std::cerr << "PRISMS-PF Error: Nonlinear solver tolerance types other than ABSOLUTE_CHANGE have yet to be implemented." << std::endl;
319  }
320  }
321  }
322  }
323 
324  //check if solution is nan
325  if (!numbers::is_finite(solutionSet[fieldIndex]->l2_norm())){
326  sprintf(buffer, "ERROR: field '%s' solution is NAN. exiting.\n\n",
327  fields[fieldIndex].name.c_str());
328  pcout<<buffer;
329  exit(-1);
330  }
331 
332  }
333 
334  nonlinear_it_index++;
335  }
336  }
337 
338  if (currentIncrement%userInputs.skip_print_steps==0){
339  pcout << "wall time: " << time.wall_time() << "s\n";
340  }
341  //log time
342  computing_timer.exit_section("matrixFreePDE: solveIncrements");
343 
344 }
345 
346 #include "../../include/matrixFreePDE_template_instantiations.h"
virtual void solveIncrement(bool skip_time_dependent)
dealii::parallel::distributed::Vector< double > vectorType
Definition: FloodFiller.h:15