two_d_unsteady_heat_t_adapt.cc
Go to the documentation of this file.
1 //LIC// ====================================================================
2 //LIC// This file forms part of oomph-lib, the object-oriented,
3 //LIC// multi-physics finite-element library, available
4 //LIC// at http://www.oomph-lib.org.
5 //LIC//
6 //LIC// Copyright (C) 2006-2023 Matthias Heil and Andrew Hazel
7 //LIC//
8 //LIC// This library is free software; you can redistribute it and/or
9 //LIC// modify it under the terms of the GNU Lesser General Public
10 //LIC// License as published by the Free Software Foundation; either
11 //LIC// version 2.1 of the License, or (at your option) any later version.
12 //LIC//
13 //LIC// This library is distributed in the hope that it will be useful,
14 //LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 //LIC// Lesser General Public License for more details.
17 //LIC//
18 //LIC// You should have received a copy of the GNU Lesser General Public
19 //LIC// License along with this library; if not, write to the Free Software
20 //LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 //LIC// 02110-1301 USA.
22 //LIC//
23 //LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
24 //LIC//
25 //LIC//====================================================================
26 // Driver for 2D unsteady heat problem with temporal adaptivity and
27 // optional restart from disk
28 
29 //Generic routines
30 #include "generic.h"
31 
32 // The unsteady heat equations
33 #include "unsteady_heat.h"
34 
35 // Mesh
36 #include "meshes/rectangular_quadmesh.h"
37 
38 using namespace std;
39 
40 using namespace oomph;
41 
42 using namespace MathematicalConstants;
43 
44 
45 /// ////////////////////////////////////////////////////////////////////
46 /// ////////////////////////////////////////////////////////////////////
47 /// ////////////////////////////////////////////////////////////////////
48 
49 //======start_of_ExactSolnForUnsteadyHeat=====================
50 /// Namespace for forced exact solution for UnsteadyHeat equation
51 //====================================================================
53 {
54 
55  /// Factor controlling the rate of change
56  double Gamma=10.0;
57 
58  /// Wavenumber
59  double K=3.0;
60 
61  /// Angle of bump
62  double Phi=1.0;
63 
64  /// Exact solution as a Vector
65  void get_exact_u(const double& time, const Vector<double>& x,
66  Vector<double>& u)
67  {
68  double zeta=cos(Phi)*x[0]+sin(Phi)*x[1];
69  u[0]=sin(K*zeta)*
70  0.5*(1.0+tanh(Gamma*cos(2.0*MathematicalConstants::Pi*time)));
71  }
72 
73  /// Exact solution as a scalar
74  void get_exact_u(const double& time, const Vector<double>& x, double& u)
75  {
76  double zeta=cos(Phi)*x[0]+sin(Phi)*x[1];
77  u=sin(K*zeta)*
78  0.5*(1.0+tanh(Gamma*cos(2.0*MathematicalConstants::Pi*time)));
79  }
80 
81  /// Source function to make it an exact solution
82  void get_source(const double& time, const Vector<double>& x, double& source)
83  {
84  source=
85  -0.5*sin(K*(cos(Phi)*x[0]+sin(Phi)*x[1]))*K*K*pow(cos(Phi),2.0)*(
86  0.1E1+tanh(Gamma*cos(0.2E1*0.3141592653589793E1*time)))-
87  0.5*sin(K*(cos(Phi)*x[0]+sin(Phi)*x[1]))*K*K*pow(sin(Phi),2.0)*
88  (0.1E1+tanh(Gamma*cos(0.2E1*0.3141592653589793E1*time)))+
89  0.1E1*sin(K*(cos(Phi)*x[0]+sin(Phi)*x[1]))*
90  (1.0-pow(tanh(Gamma*cos(0.2E1*0.3141592653589793E1*time)),2.0))*
91  Gamma*sin(0.2E1*0.3141592653589793E1*time)*0.3141592653589793E1;
92  }
93 
94 } // end of ExactSolnForUnsteadyHeat
95 
96 /// /////////////////////////////////////////////////////////////////////
97 /// /////////////////////////////////////////////////////////////////////
98 /// /////////////////////////////////////////////////////////////////////
99 
100 //=====start_of_problem_class=========================================
101 /// UnsteadyHeat problem
102 //====================================================================
103 template<class ELEMENT>
104 class UnsteadyHeatProblem : public Problem
105 {
106 
107 public:
108 
109  /// Constructor
110  UnsteadyHeatProblem(UnsteadyHeatEquations<2>::UnsteadyHeatSourceFctPt
111  source_fct_pt);
112 
113  /// Destructor (empty)
115 
116  /// Update the problem specs after solve (empty)
118 
119  /// Update the problem specs before solve (empty)
121 
122  /// Update the problem specs after solve (empty)
124 
125  /// Update the problem specs before next timestep:
126  /// Set Dirchlet boundary conditions from exact solution.
127  void actions_before_implicit_timestep();
128 
129  /// Set initial condition (incl previous timesteps) according
130  /// to specified function.
131  void set_initial_condition();
132 
133  /// Doc the solution
134  void doc_solution(DocInfo& doc_info, ofstream& trace_file);
135 
136  /// Dump problem to disk to allow for restart.
137  void dump_it(ofstream& dump_file);
138 
139  /// Read problem for restart from specified restart file.
140  void restart(ifstream& restart_file);
141 
142  /// Global error norm for adaptive time-stepping
143  double global_temporal_error_norm();
144 
145 private:
146 
147  /// Pointer to source function
148  UnsteadyHeatEquations<2>::UnsteadyHeatSourceFctPt Source_fct_pt;
149 
150  /// Pointer to control node at which the solution is documented
152 
153 }; // end of problem class
154 
155 
156 
157 
158 //========start_of_constructor============================================
159 /// Constructor for UnsteadyHeat problem in square domain
160 //========================================================================
161 template<class ELEMENT>
163  UnsteadyHeatEquations<2>::UnsteadyHeatSourceFctPt source_fct_pt) :
164  Source_fct_pt(source_fct_pt)
165 {
166 
167  // Allocate the timestepper -- this constructs the Problem's
168  // time object with a sufficient amount of storage to store the
169  // previous timsteps.
170  add_time_stepper_pt(new BDF<2>(true));
171 
172  // Setup parameters for exact solution
173  // -----------------------------------
174 
175  // Parameter controlling the rate of change
177 
178  // Wavenumber
180 
181  // Setup mesh
182  //-----------
183 
184  // Number of elements in x and y directions
185  unsigned nx=5;
186  unsigned ny=5;
187 
188  // Lengths in x and y directions
189  double lx=1.0;
190  double ly=1.0;
191 
192  // Build mesh
193  mesh_pt() = new RectangularQuadMesh<ELEMENT>(nx,ny,lx,ly,time_stepper_pt());
194 
195  // Choose a control node at which the solution is documented
196  //----------------------------------------------------------
197  // Total number of elements
198  unsigned n_el=mesh_pt()->nelement();
199 
200  // Choose an element in the middle
201  unsigned control_el=unsigned(n_el/2);
202 
203  // Choose its first node as the control node
204  Control_node_pt=mesh_pt()->finite_element_pt(control_el)->node_pt(0);
205 
206  cout << "Recording trace of the solution at: "
207  << Control_node_pt->x(0) << " "
208  << Control_node_pt->x(1) << std::endl;
209 
210 
211  // Set the boundary conditions for this problem:
212  // ---------------------------------------------
213  // All nodes are free by default -- just pin the ones that have
214  // Dirichlet conditions here.
215  unsigned n_bound = mesh_pt()->nboundary();
216  for(unsigned b=0;b<n_bound;b++)
217  {
218  unsigned n_node = mesh_pt()->nboundary_node(b);
219  for (unsigned n=0;n<n_node;n++)
220  {
221  mesh_pt()->boundary_node_pt(b,n)->pin(0);
222  }
223  } // end of set boundary conditions
224 
225 
226  // Complete the build of all elements so they are fully functional
227  //----------------------------------------------------------------
228 
229  // Find number of elements in mesh
230  unsigned n_element = mesh_pt()->nelement();
231 
232  // Loop over the elements to set up element-specific
233  // things that cannot be handled by constructor
234  for(unsigned i=0;i<n_element;i++)
235  {
236  // Upcast from FiniteElement to the present element
237  ELEMENT *el_pt = dynamic_cast<ELEMENT*>(mesh_pt()->element_pt(i));
238 
239  //Set the source function pointer
240  el_pt->source_fct_pt() = Source_fct_pt;
241  }
242 
243  // Do equation numbering
244  cout <<"Number of equations: " << assign_eqn_numbers() << std::endl;
245 
246 } // end of constructor
247 
248 
249 
250 //=========start of actions_before_implicit_timestep===============================
251 /// Actions before timestep: update the domain, then reset the
252 /// boundary conditions for the current time.
253 //========================================================================
254 template<class ELEMENT>
256 {
257  // Get current time
258  double time=time_pt()->time();
259 
260  //Loop over the boundaries
261  unsigned num_bound = mesh_pt()->nboundary();
262  for(unsigned ibound=0;ibound<num_bound;ibound++)
263  {
264  // Loop over the nodes on boundary
265  unsigned num_nod=mesh_pt()->nboundary_node(ibound);
266  for (unsigned inod=0;inod<num_nod;inod++)
267  {
268  Node* nod_pt=mesh_pt()->boundary_node_pt(ibound,inod);
269  double u;
270  Vector<double> x(2);
271  x[0]=nod_pt->x(0);
272  x[1]=nod_pt->x(1);
273  // Get current values of the boundary conditions from the
274  // exact solution
276  nod_pt->set_value(0,u);
277  }
278  }
279 } // end of actions_before_implicit_timestep
280 
281 
282 
283 //======================start_of_set_initial_condition====================
284 /// Set initial condition: Assign previous and current values
285 /// from exact solution or from restart file.
286 //========================================================================
287 template<class ELEMENT>
289 {
290 
291  // Pointer to restart file
292  ifstream* restart_file_pt=0;
293 
294  // Restart?
295  //---------
296  // Restart file specified via command line [all programs have at least
297  // a single command line argument: their name. Ignore this here.]
298  if (CommandLineArgs::Argc==1)
299  {
300  cout << "No restart -- setting IC from exact solution" << std::endl;
301  }
302  else if (CommandLineArgs::Argc==2)
303  {
304  // Open restart file
305  restart_file_pt= new ifstream(CommandLineArgs::Argv[1],ios_base::in);
306  if (restart_file_pt!=0)
307  {
308  cout << "Have opened " << CommandLineArgs::Argv[1] <<
309  " for restart. " << std::endl;
310  }
311  else
312  {
313  std::ostringstream error_stream;
314  error_stream
315  << "ERROR while trying to open " << CommandLineArgs::Argv[1] <<
316  " for restart." << std::endl;
317 
318  throw OomphLibError(
319  error_stream.str(),
320  OOMPH_CURRENT_FUNCTION,
321  OOMPH_EXCEPTION_LOCATION);
322  }
323  }
324  // More than one command line argument?
325  else
326  {
327  std::ostringstream error_stream;
328  error_stream << "Can only specify one input file\n"
329  << "You specified the following command line arguments:\n";
330  //Fix this
331  CommandLineArgs::output();
332 
333  throw OomphLibError(
334  error_stream.str(),
335  OOMPH_CURRENT_FUNCTION,
336  OOMPH_EXCEPTION_LOCATION);
337  }
338 
339 
340  // Read restart data:
341  //-------------------
342  if (restart_file_pt!=0)
343  {
344  // Read the data from restart file. This also initialises
345  // the previous timesteps and sets up the weights for the timestepper(s)
346  // in the problem
347  restart(*restart_file_pt);
348  }
349  // Assign initial condition from exact solution
350  //---------------------------------------------
351  else
352  {
353 
354  // Choose initial timestep
355  double dt0=0.05;
356 
357  // Initialise timestep -- also sets the weights for all timesteppers
358  // in the problem.
359  initialise_dt(dt0);
360 
361  // Backup time in global Time object
362  double backed_up_time=time_pt()->time();
363 
364  // Past history fo needs to be established for t=time0-deltat, ...
365  // Then provide current values (at t=time0) which will also form
366  // the initial guess for the first solve at t=time0+deltat
367 
368  // Vector of exact solution value
369  Vector<double> soln(1);
370  Vector<double> x(2);
371 
372  //Find number of nodes in mesh
373  unsigned num_nod = mesh_pt()->nnode();
374 
375  // Set continuous times at previous timesteps
376  int nprev_steps=time_stepper_pt()->nprev_values();
377  Vector<double> prev_time(nprev_steps+1);
378  for (int itime=nprev_steps;itime>=0;itime--)
379  {
380  prev_time[itime]=time_pt()->time(unsigned(itime));
381  }
382 
383  // Loop over current & previous timesteps
384  for (int itime=nprev_steps;itime>=0;itime--)
385  {
386  // Continuous time
387  double time=prev_time[itime];
388  cout << "setting IC at time =" << time << std::endl;
389 
390  // Loop over the nodes to set initial guess everywhere
391  for (unsigned n=0;n<num_nod;n++)
392  {
393  // Get nodal coordinates
394  x[0]=mesh_pt()->node_pt(n)->x(0);
395  x[1]=mesh_pt()->node_pt(n)->x(1);
396 
397  // Get intial solution
399 
400  // Assign solution
401  mesh_pt()->node_pt(n)->set_value(itime,0,soln[0]);
402 
403  // Loop over coordinate directions: Previous position = present position
404  for (unsigned i=0;i<2;i++)
405  {
406  mesh_pt()->node_pt(n)->x(itime,i)=x[i];
407  }
408  }
409  }
410 
411  // Reset backed up time for global timestepper
412  time_pt()->time()=backed_up_time;
413 
414  }
415 
416 
417 } // end of set_initial_condition
418 
419 
420 
421 //=======start_of_doc_solution============================================
422 /// Doc the solution
423 //========================================================================
424 template<class ELEMENT>
426 doc_solution(DocInfo& doc_info,ofstream& trace_file)
427 {
428  ofstream some_file;
429  char filename[100];
430 
431  // Number of plot points
432  unsigned npts;
433  npts=5;
434 
435 
436  cout << std::endl;
437  cout << "=================================================" << std::endl;
438  cout << "Docing solution for t=" << time_pt()->time() << std::endl;
439  cout << "=================================================" << std::endl;
440 
441 
442  cout << " Timestep: " << doc_info.number() << std::endl;
443 
444  // Output solution
445  //-----------------
446  sprintf(filename,"%s/soln%i.dat",doc_info.directory().c_str(),
447  doc_info.number());
448  some_file.open(filename);
449  mesh_pt()->output(some_file,npts);
450 
451  // Write file as a tecplot text object
452  some_file << "TEXT X=2.5,Y=93.6,F=HELV,HU=POINT,C=BLUE,H=26,T=\"time = "
453  << time_pt()->time() << "\"";
454  // ...and draw a horizontal line whose length is proportional
455  // to the elapsed time
456  some_file << "GEOMETRY X=2.5,Y=98,T=LINE,C=BLUE,LT=0.4" << std::endl;
457  some_file << "1" << std::endl;
458  some_file << "2" << std::endl;
459  some_file << " 0 0" << std::endl;
460  some_file << time_pt()->time()*20.0 << " 0" << std::endl;
461 
462  // Write dummy zones
463  some_file << "ZONE I=2,J=2" << std::endl;
464  some_file << "0.0 0.0 0.0" << std::endl;
465  some_file << "1.0 0.0 0.0" << std::endl;
466  some_file << "0.0 1.0 0.0" << std::endl;
467  some_file << "1.0 1.0 0.0" << std::endl;
468  some_file << "ZONE I=2,J=2" << std::endl;
469  some_file << "0.0 0.0 1.0" << std::endl;
470  some_file << "1.0 0.0 1.0" << std::endl;
471  some_file << "0.0 1.0 1.0" << std::endl;
472  some_file << "1.0 1.0 1.0" << std::endl;
473  some_file.close();
474 
475 
476  // Output exact solution
477  //----------------------
478  sprintf(filename,"%s/exact_soln%i.dat",doc_info.directory().c_str(),
479  doc_info.number());
480  some_file.open(filename);
481  mesh_pt()->output_fct(some_file,npts,time_pt()->time(),
483  // Write dummy zones
484  some_file << "ZONE I=2,J=2" << std::endl;
485  some_file << "0.0 0.0 0.0" << std::endl;
486  some_file << "1.0 0.0 0.0" << std::endl;
487  some_file << "0.0 1.0 0.0" << std::endl;
488  some_file << "1.0 1.0 0.0" << std::endl;
489  some_file << "ZONE I=2,J=2" << std::endl;
490  some_file << "0.0 0.0 1.0" << std::endl;
491  some_file << "1.0 0.0 1.0" << std::endl;
492  some_file << "0.0 1.0 1.0" << std::endl;
493  some_file << "1.0 1.0 1.0" << std::endl;
494  some_file.close();
495 
496  // Doc error
497  //----------
498  double error,norm;
499  sprintf(filename,"%s/error%i.dat",doc_info.directory().c_str(),
500  doc_info.number());
501  some_file.open(filename);
502  mesh_pt()->compute_error(some_file,
504  time_pt()->time(),
505  error,norm);
506  some_file.close();
507 
508  // Doc solution and error
509  //-----------------------
510  cout << "error: " << error << std::endl;
511  cout << "norm : " << norm << std::endl << std::endl;
512 
513  // Get exact solution at control node
514  Vector<double> x_ctrl(2);
515  x_ctrl[0]=Control_node_pt->x(0);
516  x_ctrl[1]=Control_node_pt->x(1);
517  double u_exact;
518  ExactSolnForUnsteadyHeat::get_exact_u(time_pt()->time(),x_ctrl,u_exact);
519  trace_file << time_pt()->time() << " "
520  << time_pt()->dt() << " "
521  << Control_node_pt->value(0) << " "
522  << u_exact << " "
523  << error << " "
524  << norm << std::endl;
525 
526 
527  // Write restart file
528  sprintf(filename,"%s/restart%i.dat",doc_info.directory().c_str(),
529  doc_info.number());
530  some_file.open(filename);
531  dump_it(some_file);
532  some_file.close();
533 
534 } // end of doc_solution
535 
536 
537 
538 
539 //=====start_of_dump_it===================================================
540 /// Dump the solution to disk to allow for restart
541 //========================================================================
542 template<class ELEMENT>
543 void UnsteadyHeatProblem<ELEMENT>::dump_it(ofstream& dump_file)
544 {
545 
546  // Call generic dump()
547  Problem::dump(dump_file);
548 
549 } // end of dump_it
550 
551 
552 
553 //=================start_of_restart=======================================
554 /// Read solution from disk for restart
555 //========================================================================
556 template<class ELEMENT>
557 void UnsteadyHeatProblem<ELEMENT>::restart(ifstream& restart_file)
558 {
559 
560  // Read the generic problem data from restart file
561  Problem::read(restart_file);
562 
563 } // end of restart
564 
565 
566 
567 
568 //========start_of_global_temporal_error_norm==============================
569 /// Global error norm for adaptive timestepping: RMS error, based on
570 /// difference between predicted and actual value at all nodes.
571 //=========================================================================
572 template<class ELEMENT>
574 {
575  double global_error = 0.0;
576 
577  //Find out how many nodes there are in the problem
578  unsigned n_node = mesh_pt()->nnode();
579 
580  //Loop over the nodes and calculate the estimated error in the values
581  for(unsigned i=0;i<n_node;i++)
582  {
583  // Get error in solution: Difference between predicted and actual
584  // value for nodal value 0
585  double error = mesh_pt()->node_pt(i)->time_stepper_pt()->
586  temporal_error_in_value(mesh_pt()->node_pt(i),0);
587 
588  //Add the square of the individual error to the global error
589  global_error += error*error;
590  }
591 
592  // Divide by the number of nodes
593  global_error /= double(n_node);
594 
595  // Return square root...
596  return sqrt(global_error);
597 
598 } // end of global_temporal_error_norm
599 
600 /// /////////////////////////////////////////////////////////////////////
601 /// /////////////////////////////////////////////////////////////////////
602 /// /////////////////////////////////////////////////////////////////////
603 
604 
605 
606 //=======start_of_main====================================================
607 /// Driver code for the adaptive solution of an
608 /// unsteady heat equation with option for restart from disk:
609 /// Only a single command line argument is allowed.
610 /// If specified it is interpreted as the name of the restart file.
611 //========================================================================
612 int main(int argc, char* argv[])
613 {
614 
615  // Store command line arguments
616  CommandLineArgs::setup(argc,argv);
617 
618  // Build problem
621 
622  // Setup labels for output
623  DocInfo doc_info;
624 
625  // Output directory
626  doc_info.set_directory("RESLT");
627 
628  // Output number
629  doc_info.number()=0;
630 
631  // Open a trace file
632  ofstream trace_file;
633  char filename[100];
634  sprintf(filename,"%s/trace.dat",doc_info.directory().c_str());
635  trace_file.open(filename);
636  trace_file << "VARIABLES=\"time\",\"dt\",\"u<SUB>FE</SUB>\","
637  << "\"u<SUB>exact</SUB>\",\"norm of error\",\"norm of solution\""
638  << std::endl;
639 
640  // Choose simulation interval
641  double t_max=0.18; // 2.0;
642 
643  // Set IC
644  problem.set_initial_condition();
645 
646 
647  // Initial timestep: Use the one used when setting up the initial
648  // condition
649  double dt=problem.time_pt()->dt();
650 
651  //Output initial condition
652  problem.doc_solution(doc_info,trace_file);
653 
654  //Increment counter for solutions
655  doc_info.number()++;
656 
657  // Target error for adaptive timestepping
658  double epsilon_t=1.0e-4;
659 
660  // Timestepping loop: Don't know how many steps we're going to take
661  // in advance
662  while (problem.time_pt()->time()<t_max)
663  {
664 
665  // Take an adaptive timestep -- the input dt is the suggested timestep.
666  // The adaptive timestepper will adjust dt until the required error
667  // tolerance is satisfied. The function returns a suggestion
668  // for the timestep that should be taken for the next step. This
669  // is either the actual timestep taken this time or a larger
670  // value if the solution was found to be "too accurate".
671  double dt_next=problem.adaptive_unsteady_newton_solve(dt,epsilon_t);
672 
673  // Use dt_next as suggestion for the next timestep
674  dt=dt_next;
675 
676  //Output solution
677  problem.doc_solution(doc_info,trace_file);
678 
679  //Increment counter for solutions
680  doc_info.number()++;
681 
682  } // end of timestepping loop
683 
684  // Close trace file
685  trace_file.close();
686 
687 
688 }; // end of main
///////////////////////////////////////////////////////////////////// ///////////////////////////////...
UnsteadyHeatEquations< 2 >::UnsteadyHeatSourceFctPt Source_fct_pt
Pointer to source function.
void doc_solution(DocInfo &doc_info, ofstream &trace_file)
Doc the solution.
Node * Control_node_pt
Pointer to control node at which the solution is documented.
void actions_before_implicit_timestep()
Update the problem specs before next timestep: Set Dirchlet boundary conditions from exact solution.
double global_temporal_error_norm()
Global error norm for adaptive time-stepping.
void actions_after_newton_solve()
Update the problem specs after solve (empty)
void set_initial_condition()
Set initial condition (incl previous timesteps) according to specified function.
void dump_it(ofstream &dump_file)
Dump problem to disk to allow for restart.
void actions_before_newton_solve()
Update the problem specs before solve (empty)
UnsteadyHeatProblem(UnsteadyHeatEquations< 2 >::UnsteadyHeatSourceFctPt source_fct_pt)
Constructor.
~UnsteadyHeatProblem()
Destructor (empty)
void restart(ifstream &restart_file)
Read problem for restart from specified restart file.
void actions_after_implicit_timestep()
Update the problem specs after solve (empty)
//////////////////////////////////////////////////////////////////// ////////////////////////////////...
double Gamma
Factor controlling the rate of change.
void get_exact_u(const double &time, const Vector< double > &x, Vector< double > &u)
Exact solution as a Vector.
void get_exact_u(const double &time, const Vector< double > &x, double &u)
Exact solution as a scalar.
void get_source(const double &time, const Vector< double > &x, double &source)
Source function to make it an exact solution.
int main(int argc, char *argv[])
///////////////////////////////////////////////////////////////////// ///////////////////////////////...