[P]arallel [Hi]gh-order [Li]brary for [P]DEs  Latest
Parallel High-Order Library for PDEs through hp-adaptive Discontinuous Galerkin methods
ode_solver_base.cpp
1 #include "ode_solver_base.h"
2 #include "limiter/bound_preserving_limiter_factory.hpp"
3 
4 namespace PHiLiP {
5 namespace ODE{
6 
7 template <int dim, typename real, typename MeshType>
9  , std::shared_ptr< ProperOrthogonalDecomposition::PODBase<dim>> pod)
10  : dg(dg_input)
11  , pod(pod)
12  , limiter(BoundPreservingLimiterFactory<dim, 6, real>::create_limiter(dg->all_parameters))
13  , all_parameters(dg->all_parameters)
14  , ode_param(all_parameters->ode_solver_param)
15  , current_time(ode_param.initial_time)
16  , current_iteration(ode_param.initial_iteration)
17  , current_desired_time_for_output_solution_every_dt_time_intervals(ode_param.initial_desired_time_for_output_solution_every_dt_time_intervals)
18  , original_time_step(0.0)
19  , modified_time_step(0.0)
20  , mpi_communicator(MPI_COMM_WORLD)
21  , mpi_rank(dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD))
22  , pcout(std::cout, mpi_rank==0)
23 {}
24 
25 template <int dim, typename real, typename MeshType>
27  : ODESolverBase(dg_input, nullptr)
28 {}
29 
30 template <int dim, typename real, typename MeshType>
32 {
33  (void) pseudotime;
34  return dt;
35 }
36 
37 template <int dim, typename real, typename MeshType>
39 {
40  (void) pseudotime;
41  return dt;
42 }
43 
44 template <int dim, typename real, typename MeshType>
46 {
47  return this->original_time_step;
48 }
49 
50 template <int dim, typename real, typename MeshType>
52 {
53  return this->modified_time_step;
54 }
55 
56 template <int dim, typename real, typename MeshType>
57 void ODESolverBase<dim,real,MeshType>::initialize_steady_polynomial_ramping (const unsigned int global_final_poly_degree)
58 {
59  pcout << " ************************************************************************ " << std::endl;
60  pcout << " Initializing DG with global polynomial degree = " << global_final_poly_degree << " by ramping from degree 0 ... " << std::endl;
61  pcout << " ************************************************************************ " << std::endl;
62 
63  for (unsigned int degree = 0; degree <= global_final_poly_degree; degree++) {
64  pcout << " ************************************************************************ " << std::endl;
65  pcout << " Ramping degree " << degree << " until p=" << global_final_poly_degree << std::endl;
66  pcout << " ************************************************************************ " << std::endl;
67 
68  // Transfer solution to current degree.
69  dealii::LinearAlgebra::distributed::Vector<double> old_solution(dg->solution);
70  old_solution.update_ghost_values();
71  dealii::parallel::distributed::SolutionTransfer<dim, dealii::LinearAlgebra::distributed::Vector<double>, dealii::DoFHandler<dim>> solution_transfer(dg->dof_handler);
72  solution_transfer.prepare_for_coarsening_and_refinement(old_solution);
73  dg->set_all_cells_fe_degree(degree);
74  dg->allocate_system ();
75  dg->solution.zero_out_ghosts();
76  solution_transfer.interpolate(dg->solution);
77  dg->solution.update_ghost_values();
78 
79  // Solve steady state problem.
80  steady_state();
81  }
82 }
83 
84 
85 template <int dim, typename real, typename MeshType>
87 {
88  for (const auto &sol : dg->solution) {
89  if (sol == std::numeric_limits<real>::lowest()) {
90  throw std::invalid_argument(" User forgot to assign valid initial conditions. ");
91  }
92  }
93 }
94 
95 template <int dim, typename real, typename MeshType>
97  const unsigned int current_iteration,
98  const double current_residual,
99  const std::shared_ptr <dealii::TableHandler> data_table) const
100 {
101  if(mpi_rank==0) {
102  // Add iteration to the table
103  std::string iteration_string = "Iteration";
104  data_table->add_value(iteration_string, current_iteration);
105  // Add residual to the table
106  std::string residual_string = "Residual";
107  data_table->add_value(residual_string, current_residual);
108  data_table->set_precision(residual_string, 16);
109  data_table->set_scientific(residual_string, true);
110  // Write to file
111  std::ofstream data_table_file("ode_solver_steady_state_convergence_data_table.txt");
112  data_table->write_text(data_table_file);
113  }
114 }
115 
116 template <int dim, typename real, typename MeshType>
118 {
119  try {
121  }
122  catch( const std::invalid_argument& e ) {
123  std::abort();
124  }
125 
126  pcout << " Performing steady state analysis... " << std::endl;
128 
129  std::shared_ptr<dealii::TableHandler> ode_solver_steady_state_convergence_table = std::make_shared<dealii::TableHandler>();
130 
131  this->residual_norm_decrease = 1; // Always do at least 1 iteration
132  update_norm = 1; // Always do at least 1 iteration
133  this->current_iteration = 0;
134  if (ode_param.output_solution_every_x_steps >= 0) this->dg->output_results_vtk(this->current_iteration);
135 
136  pcout << " Evaluating right-hand side and setting system_matrix to Jacobian before starting iterations... " << std::endl;
137  this->dg->assemble_residual ();
138  initial_residual_norm = this->dg->get_residual_l2norm();
140  pcout << " ********************************************************** "
141  << std::endl
142  << " Initial absolute residual norm: " << this->residual_norm
143  << std::endl;
144 
146  // write initial convergence data
147  write_ode_solver_steady_state_convergence_data_to_table(this->current_iteration, this->residual_norm, ode_solver_steady_state_convergence_table);
148  }
149 
150  // Initial Courant-Friedrichs-Lax number
151  const double initial_CFL = all_parameters->ode_solver_param.initial_time_step;
152  CFL_factor = 1.0;
153 
154  auto initial_solution = dg->solution;
155 
156  double old_residual_norm = this->residual_norm; (void) old_residual_norm;
157 
158  // Output initial solution
159  int convergence_error = this->residual_norm > ode_param.nonlinear_steady_residual_tolerance;
160 
161  while ( convergence_error
162  && this->residual_norm_decrease > ode_param.nonlinear_steady_residual_tolerance
163  //&& update_norm > ode_param.nonlinear_steady_residual_tolerance
164  && this->current_iteration < ode_param.nonlinear_max_iterations
165  && this->residual_norm < 1e5
166  && CFL_factor > 1e-2)
167  {
168  if ((ode_param.ode_output) == Parameters::OutputEnum::verbose
170  && dealii::Utilities::MPI::this_mpi_process(mpi_communicator) == 0 )
171  {
172  pcout.set_condition(true);
173  } else {
174  pcout.set_condition(false);
175  }
176  pcout << " ********************************************************** "
177  << std::endl
178  << " Nonlinear iteration: " << this->current_iteration
179  << " Residual norm (normalized) : " << this->residual_norm
180  << " Residual norm decrease : " << this->residual_norm_decrease
181  << " ( " << this->residual_norm / this->initial_residual_norm << " ) "
182  << std::endl;
183 
185  write_ode_solver_steady_state_convergence_data_to_table(this->current_iteration, this->residual_norm, ode_solver_steady_state_convergence_table);
186  }
187 
188  if ((ode_param.ode_output) == Parameters::OutputEnum::verbose &&
189  (this->current_iteration%ode_param.print_iteration_modulo) == 0 ) {
190  pcout << " Evaluating right-hand side and setting system_matrix to Jacobian... " << std::endl;
191  }
192 
193  double ramped_CFL = initial_CFL * CFL_factor;
194  if (this->residual_norm_decrease < 1.0) {
195  ramped_CFL *= pow((1.0-std::log10(this->residual_norm_decrease)*ode_param.time_step_factor_residual), ode_param.time_step_factor_residual_exp);
196  }
197  ramped_CFL = std::max(ramped_CFL,initial_CFL*CFL_factor);
198  pcout << "Initial CFL = " << initial_CFL << ". Current CFL = " << ramped_CFL << std::endl;
199 
200  if (this->residual_norm < 1e-12) {
201  this->dg->freeze_artificial_dissipation = true;
202  } else {
203  this->dg->freeze_artificial_dissipation = false;
204  }
205 
206  const bool pseudotime = true;
207  step_in_time(ramped_CFL, pseudotime);
208 
209  this->dg->assemble_residual ();
210 
212  const bool is_output_iteration = (this->current_iteration % ode_param.output_solution_every_x_steps == 0);
213  if (is_output_iteration) {
214  const int file_number = this->current_iteration / ode_param.output_solution_every_x_steps;
215  this->dg->output_results_vtk(file_number);
216  }
217  }
218 
219  old_residual_norm = this->residual_norm;
220  this->residual_norm = this->dg->get_residual_l2norm();
221  this->residual_norm_decrease = this->residual_norm / this->initial_residual_norm;
222 
224  && this->residual_norm_decrease > ode_param.nonlinear_steady_residual_tolerance;
225  }
226 
227  if (this->residual_norm > 1e5
228  || std::isnan(this->residual_norm)
229  || CFL_factor <= 1e-2)
230  {
231  this->dg->solution = initial_solution;
232 
233  if(CFL_factor <= 1e-2) this->dg->right_hand_side.add(1.0);
234  }
235 
237  dealii::LinearAlgebra::ReadWriteVector<double> write_dg_solution(this->dg->solution.size());
238  write_dg_solution.import(this->dg->solution, dealii::VectorOperation::values::insert);
239  if(mpi_rank == 0){
240  std::ofstream out_file(ode_param.steady_state_final_solution_filename + ".txt");
241  for(unsigned int i = 0 ; i < write_dg_solution.size() ; i++){
242  out_file << " " << std::setprecision(17) << write_dg_solution(i) << " \n";
243  }
244  out_file.close();
245  }
246  }
247 
248  pcout << " ********************************************************** "
249  << std::endl
250  << " ODESolver steady_state stopped at"
251  << std::endl
252  << " Nonlinear iteration: " << this->current_iteration
253  << " residual norm: " << this->residual_norm
254  << std::endl
255  << " ********************************************************** "
256  << std::endl;
257 
258  return convergence_error;
259 }
260 
261 template <int dim, typename real, typename MeshType>
263 {
264 
265  const unsigned int number_of_time_steps = (!this->all_parameters->use_energy) ? static_cast<int>(ceil(time_advance/ode_param.initial_time_step)) : this->current_iteration+1;
266  const double constant_time_step = time_advance/static_cast<int>(ceil(time_advance/ode_param.initial_time_step));
267 
268  try {
270  }
271  catch( const std::invalid_argument& e ) {
272  std::abort();
273  }
274 
275  pcout << " Advancing solution by " << time_advance << " time units, using "
276  << number_of_time_steps << " iterations of size dt=" << constant_time_step << " ... " << std::endl;
277 
278  if(!this->all_parameters->use_energy) this->current_iteration = 0;
279 
280  if(this->current_iteration == 0) allocate_ode_system ();
281 
283  this->dg->output_results_vtk(this->current_iteration);
285  this->dg->output_results_vtk(this->current_iteration);
287  }
288 
289  while (this->current_iteration < number_of_time_steps)
290  {
291  if ((ode_param.ode_output) == Parameters::OutputEnum::verbose &&
292  (this->current_iteration%ode_param.print_iteration_modulo) == 0 ) {
293  pcout << " ********************************************************** "
294  << std::endl
295  << " Iteration: " << this->current_iteration + 1
296  << " out of: " << number_of_time_steps
297  << std::endl;
298  }
299 
300  if ((ode_param.ode_output) == Parameters::OutputEnum::verbose &&
301  (this->current_iteration%ode_param.print_iteration_modulo) == 0 ) {
302  pcout << " Evaluating right-hand side and setting system_matrix to Jacobian... " << std::endl;
303  }
304 
305  const bool pseudotime = false;
306  step_in_time(constant_time_step, pseudotime);
307 
309  const bool is_output_iteration = (this->current_iteration % ode_param.output_solution_every_x_steps == 0);
310  if (is_output_iteration) {
311  const int file_number = this->current_iteration / ode_param.output_solution_every_x_steps;
312  this->dg->output_results_vtk(file_number);
313  }
315  const bool is_output_time = ((this->current_time <= this->current_desired_time_for_output_solution_every_dt_time_intervals) &&
317  if (is_output_time) {
319  this->dg->output_results_vtk(file_number);
321  }
322  }
323  }
324  return 1;
325 }
326 
329 #if PHILIP_DIM != 1
331 #endif
332 
333 } // ODE namespace
334 } // PHiLiP namespace
OutputEnum ode_output
verbose or quiet.
virtual void step_in_time(real dt, const bool pseudotime)=0
Virtual function to evaluate solution update.
const MPI_Comm mpi_communicator
MPI communicator.
virtual int steady_state()
Evaluate steady state solution.
double CFL_factor
CFL factor for (un)successful linesearches.
const int mpi_rank
MPI rank.
int advance_solution_time(double time_advance)
Function to advance solution to time+dt.
double modified_time_step
Modified time step after calling step_in_time.
void write_ode_solver_steady_state_convergence_data_to_table(const unsigned int current_iteration, const double current_residual, const std::shared_ptr< dealii::TableHandler > data_table) const
Writes the ode solver steady state convergence data to a file.
unsigned int current_iteration
Current iteration.
void initialize_steady_polynomial_ramping(const unsigned int global_final_poly_degree)
Ramps up the solution from p0 all the way up to the given global_final_poly_degree.
int output_solution_every_x_steps
Outputs the solution every x steps to .vtk file.
bool use_energy
Flag to use an energy monotonicity test.
double current_desired_time_for_output_solution_every_dt_time_intervals
Current desired time for output solution every dt time intervals.
double time_step_factor_residual_exp
Scales initial time step by pow(time_step_factor_residual*(-log10(residual_norm_decrease)),time_step_factor_residual_exp)
Files for the baseline physics.
Definition: ADTypes.hpp:10
Base class ODE solver.
This class creates a new BoundPreservingLimiter object based on input parameters. ...
unsigned int print_iteration_modulo
If ode_output==verbose, print every print_iteration_modulo iterations.
double residual_norm_decrease
Current residual norm normalized by initial residual. Only makes sense for steady state...
double initial_residual_norm
Initial residual norm.
double original_time_step
Original time step before calling step_in_time.
double get_modified_time_step() const
Getter for modified_time_step.
double output_solution_every_dt_time_intervals
Outputs the solution every dt time intervals to .vtk file.
ODESolverParam ode_solver_param
Contains parameters for ODE solver.
double initial_time_step
Time step used in ODE solver.
ODESolverBase(std::shared_ptr< DGBase< dim, real, MeshType > > dg_input, std::shared_ptr< ProperOrthogonalDecomposition::PODBase< dim >> pod)
Default constructor that will set the constants.
double current_time
Useful for accurate time-stepping.
double nonlinear_steady_residual_tolerance
Tolerance to determine steady-state convergence.
const Parameters::AllParameters *const all_parameters
Input parameters.
std::string steady_state_final_solution_filename
Filename to write final steady state solution.
double residual_norm
Current residual norm. Only makes sense for steady state.
const Parameters::ODESolverParam ode_param
Input ODE solver parameters.
void valid_initial_conditions() const
Checks whether the DG vector has valid values.
bool output_final_steady_state_solution_to_file
Output final steady state solution to file.
dealii::ConditionalOStream pcout
Parallel std::cout that only outputs on mpi_rank==0.
double time_step_factor_residual
Multiplies initial time-step by time_step_factor_residual*(-log10(residual_norm_decrease)) ...
double get_original_time_step() const
Getter for original_time_step.
virtual double get_automatic_initial_step_size(real dt, const bool pseudotime)
Virtual function to evaluate initial automatic error adaptive time step.
virtual void allocate_ode_system()=0
Virtual function to allocate the ODE system.
virtual double get_automatic_error_adaptive_step_size(real dt, const bool pseudotime)
Virtual function to evaluate automatic error adaptive time step.
double update_norm
Norm of the solution update.
DGBase is independent of the number of state variables.
Definition: dg_base.hpp:82
unsigned int nonlinear_max_iterations
Maximum number of iterations.
std::shared_ptr< DGBase< dim, real, MeshType > > dg
Smart pointer to DGBase.