$extrastylesheet
00001 // The libMesh Finite Element Library. 00002 // Copyright (C) 2002-2014 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner 00003 00004 // This library is free software; you can redistribute it and/or 00005 // modify it under the terms of the GNU Lesser General Public 00006 // License as published by the Free Software Foundation; either 00007 // version 2.1 of the License, or (at your option) any later version. 00008 00009 // This library is distributed in the hope that it will be useful, 00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 // Lesser General Public License for more details. 00013 00014 // You should have received a copy of the GNU Lesser General Public 00015 // License along with this library; if not, write to the Free Software 00016 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00017 00018 00019 #include "libmesh/twostep_time_solver.h" 00020 #include "libmesh/diff_system.h" 00021 #include "libmesh/euler_solver.h" 00022 #include "libmesh/numeric_vector.h" 00023 00024 namespace libMesh 00025 { 00026 00027 00028 00029 TwostepTimeSolver::TwostepTimeSolver (sys_type& s) 00030 : AdaptiveTimeSolver(s) 00031 00032 { 00033 // We start with a reasonable time solver: implicit Euler 00034 core_time_solver.reset(new EulerSolver(s)); 00035 } 00036 00037 00038 00039 TwostepTimeSolver::~TwostepTimeSolver () 00040 { 00041 } 00042 00043 00044 00045 void TwostepTimeSolver::solve() 00046 { 00047 libmesh_assert(core_time_solver.get()); 00048 00049 // The core_time_solver will handle any first_solve actions 00050 first_solve = false; 00051 00052 // We may have to repeat timesteps entirely if our error is bad 00053 // enough 00054 bool max_tolerance_met = false; 00055 00056 // Calculating error values each time 00057 Real single_norm(0.), double_norm(0.), error_norm(0.), 00058 relative_error(0.); 00059 00060 while (!max_tolerance_met) 00061 { 00062 // If we've been asked to reduce deltat if necessary, make sure 00063 // the core timesolver does so 00064 core_time_solver->reduce_deltat_on_diffsolver_failure = 00065 this->reduce_deltat_on_diffsolver_failure; 00066 00067 if (!quiet) 00068 { 00069 libMesh::out << "\n === Computing adaptive timestep === " 00070 << std::endl; 00071 } 00072 00073 // Use the double-length timestep first (so the 00074 // old_nonlinear_solution won't have to change) 00075 core_time_solver->solve(); 00076 00077 // Save a copy of the double-length nonlinear solution 00078 // and the old nonlinear solution 00079 UniquePtr<NumericVector<Number> > double_solution = 00080 _system.solution->clone(); 00081 UniquePtr<NumericVector<Number> > old_solution = 00082 _system.get_vector("_old_nonlinear_solution").clone(); 00083 00084 double_norm = calculate_norm(_system, *double_solution); 00085 if (!quiet) 00086 { 00087 libMesh::out << "Double norm = " << double_norm << std::endl; 00088 } 00089 00090 // Then reset the initial guess for our single-length calcs 00091 *(_system.solution) = _system.get_vector("_old_nonlinear_solution"); 00092 00093 // Call two single-length timesteps 00094 // Be sure that the core_time_solver does not change the 00095 // timestep here. (This is unlikely because it just succeeded 00096 // with a timestep twice as large!) 00097 // FIXME: even if diffsolver failure is unlikely, we ought to 00098 // do *something* if it happens 00099 core_time_solver->reduce_deltat_on_diffsolver_failure = 0; 00100 00101 Real old_time = _system.time; 00102 Real old_deltat = _system.deltat; 00103 _system.deltat *= 0.5; 00104 core_time_solver->solve(); 00105 core_time_solver->advance_timestep(); 00106 core_time_solver->solve(); 00107 00108 single_norm = calculate_norm(_system, *_system.solution); 00109 if (!quiet) 00110 { 00111 libMesh::out << "Single norm = " << single_norm << std::endl; 00112 } 00113 00114 // Reset the core_time_solver's reduce_deltat... value. 00115 core_time_solver->reduce_deltat_on_diffsolver_failure = 00116 this->reduce_deltat_on_diffsolver_failure; 00117 00118 // But then back off just in case our advance_timestep() isn't 00119 // called. 00120 // FIXME: this probably doesn't work with multistep methods 00121 _system.get_vector("_old_nonlinear_solution") = *old_solution; 00122 _system.time = old_time; 00123 _system.deltat = old_deltat; 00124 00125 // Find the relative error 00126 *double_solution -= *(_system.solution); 00127 error_norm = calculate_norm(_system, *double_solution); 00128 relative_error = error_norm / _system.deltat / 00129 std::max(double_norm, single_norm); 00130 00131 // If the relative error makes no sense, we're done 00132 if (!double_norm && !single_norm) 00133 return; 00134 00135 if (!quiet) 00136 { 00137 libMesh::out << "Error norm = " << error_norm << std::endl; 00138 libMesh::out << "Local relative error = " 00139 << (error_norm / 00140 std::max(double_norm, single_norm)) 00141 << std::endl; 00142 libMesh::out << "Global relative error = " 00143 << (error_norm / _system.deltat / 00144 std::max(double_norm, single_norm)) 00145 << std::endl; 00146 libMesh::out << "old delta t = " << _system.deltat << std::endl; 00147 } 00148 00149 // If our upper tolerance is negative, that means we want to set 00150 // it based on the first successful time step 00151 if (this->upper_tolerance < 0) 00152 this->upper_tolerance = -this->upper_tolerance * relative_error; 00153 00154 // If we haven't met our upper error tolerance, we'll have to 00155 // repeat this timestep entirely 00156 if (this->upper_tolerance && relative_error > this->upper_tolerance) 00157 { 00158 // Reset the initial guess for our next try 00159 *(_system.solution) = 00160 _system.get_vector("_old_nonlinear_solution"); 00161 00162 // Chop delta t in half 00163 _system.deltat /= 2.; 00164 00165 if (!quiet) 00166 { 00167 libMesh::out << "Failed to meet upper error tolerance" 00168 << std::endl; 00169 libMesh::out << "Retrying with delta t = " 00170 << _system.deltat << std::endl; 00171 } 00172 } 00173 else 00174 max_tolerance_met = true; 00175 } 00176 00177 00178 // Otherwise, compare the relative error to the tolerance 00179 // and adjust deltat 00180 last_deltat = _system.deltat; 00181 00182 // If our target tolerance is negative, that means we want to set 00183 // it based on the first successful time step 00184 if (this->target_tolerance < 0) 00185 this->target_tolerance = -this->target_tolerance * relative_error; 00186 00187 const Real global_shrink_or_growth_factor = 00188 std::pow(this->target_tolerance / relative_error, 00189 static_cast<Real>(1. / core_time_solver->error_order())); 00190 00191 const Real local_shrink_or_growth_factor = 00192 std::pow(this->target_tolerance / 00193 (error_norm/std::max(double_norm, single_norm)), 00194 static_cast<Real>(1. / (core_time_solver->error_order()+1.))); 00195 00196 if (!quiet) 00197 { 00198 libMesh::out << "The global growth/shrink factor is: " 00199 << global_shrink_or_growth_factor << std::endl; 00200 libMesh::out << "The local growth/shrink factor is: " 00201 << local_shrink_or_growth_factor << std::endl; 00202 } 00203 00204 // The local s.o.g. factor is based on the expected **local** 00205 // truncation error for the timestepping method, the global 00206 // s.o.g. factor is based on the method's **global** truncation 00207 // error. You can shrink/grow the timestep to attempt to satisfy 00208 // either a global or local time-discretization error tolerance. 00209 00210 Real shrink_or_growth_factor = 00211 this->global_tolerance ? global_shrink_or_growth_factor : 00212 local_shrink_or_growth_factor; 00213 00214 if (this->max_growth && this->max_growth < shrink_or_growth_factor) 00215 { 00216 if (!quiet && this->global_tolerance) 00217 { 00218 libMesh::out << "delta t is constrained by max_growth" << std::endl; 00219 } 00220 shrink_or_growth_factor = this->max_growth; 00221 } 00222 00223 _system.deltat *= shrink_or_growth_factor; 00224 00225 // Restrict deltat to max-allowable value if necessary 00226 if ((this->max_deltat != 0.0) && (_system.deltat > this->max_deltat)) 00227 { 00228 if (!quiet) 00229 { 00230 libMesh::out << "delta t is constrained by maximum-allowable delta t." 00231 << std::endl; 00232 } 00233 _system.deltat = this->max_deltat; 00234 } 00235 00236 // Restrict deltat to min-allowable value if necessary 00237 if ((this->min_deltat != 0.0) && (_system.deltat < this->min_deltat)) 00238 { 00239 if (!quiet) 00240 { 00241 libMesh::out << "delta t is constrained by minimum-allowable delta t." 00242 << std::endl; 00243 } 00244 _system.deltat = this->min_deltat; 00245 } 00246 00247 if (!quiet) 00248 { 00249 libMesh::out << "new delta t = " << _system.deltat << std::endl; 00250 } 00251 } 00252 00253 } // namespace libMesh