1 /*******************************************************************************
2  *
3  * ALBERTA:  an Adaptive multi Level finite element toolbox  using
4  *           Bisectioning refinement and Error control by Residual
5  *           Techniques
6  *
7  * www.alberta-fem.de
8  *
9  * file:     heat.c
10  *
11  * description:  solver for parabolic model problem
12  *
13  *                   u,t - \Delta u = f  in \Omega
14  *                                u = g  on \partial \Omega
15  *
16  * This version demonstrates the use of EL_SYS_INFO_INSTAT together with
17  * update_system_instat()
18  *
19  ******************************************************************************
20  *
21  *  authors:   Alfred Schmidt
22  *             Zentrum fuer Technomathematik
23  *             Fachbereich 3 Mathematik/Informatik
24  *             Universitaet Bremen
25  *             Bibliothekstr. 2
26  *             D-28359 Bremen, Germany
27  *
28  *             Kunibert G. Siebert
29  *             Institut fuer Mathematik
30  *             Universitaet Augsburg
31  *             Universitaetsstr. 14
32  *             D-86159 Augsburg, Germany
33  *
34  *  (c) by A. Schmidt and K.G. Siebert (1996-2003)
35  *
36  ******************************************************************************/
37 
38 
39 #include <alberta/alberta.h>
40 
41 #include "alberta-demo.h"
42 
43 
44 static bool do_graphics = true; /* global graphics "kill-switch" */
45 
46 /*******************************************************************************
47  * global variables: finite element space, discrete solution, discrete
48  *                   solution from previous time step, load vector,
49  *                   system matrix, and structure for adaptive procedure
50  *
51  * These variables are kept global because they are shared across build(),
52  * solve() and estimate().
53  ******************************************************************************/
54 
55 static const FE_SPACE *fe_space;      /* initialized by main() */
56 static DOF_REAL_VEC   *u_h = NULL;    /* initialized by main() */
57 static DOF_REAL_VEC   *u_old = NULL;  /* initialized by main() */
58 static DOF_REAL_VEC   *f_h = NULL;    /* initialized by main() */
59 static DOF_MATRIX     *matrix = NULL; /* initialized by main() */
60 static ADAPT_INSTAT   *adapt_instat;  /* initialized by main() */
61 static BNDRY_FLAGS    dirichlet_mask; /* initialized by main() */
62 
63 static REAL theta = 0.5;   /* parameter of the time discretization */
64 static REAL err_L2  = 0.0; /* spatial error in a single time step */
65 
66 
67 /*******************************************************************************
68  * struct heat_leaf_data: structure for storing one REAL value on each
69  *                          leaf element as LEAF_DATA
70  * rw_el_est():  return a pointer to the memory for storing the element
71  *               estimate (stored as LEAF_DATA), called by heat_est()
72  * get_el_est(): return the value of the element estimates (from LEAF_DATA),
73  *               called by adapt_method_stat() and graphics()
74  ******************************************************************************/
75 
76 struct heat_leaf_data
77 {
78   REAL estimate;            /*  one real for the estimate                   */
79   REAL est_c;               /*  one real for the coarsening estimate        */
80 };
81 
rw_el_est(EL * el)82 static REAL *rw_el_est(EL *el)
83 {
84   if (IS_LEAF_EL(el))
85     return &((struct heat_leaf_data *)LEAF_DATA(el))->estimate;
86   else
87     return NULL;
88 }
89 
get_el_est(EL * el)90 static REAL get_el_est(EL *el)
91 {
92   if (IS_LEAF_EL(el))
93     return ((struct heat_leaf_data *)LEAF_DATA(el))->estimate;
94   else
95     return 0.0;
96 }
97 
rw_el_estc(EL * el)98 static REAL *rw_el_estc(EL *el)
99 {
100   if (IS_LEAF_EL(el))
101     return &((struct heat_leaf_data *)LEAF_DATA(el))->est_c;
102   else
103     return NULL;
104 }
105 
get_el_estc(EL * el)106 static REAL get_el_estc(EL *el)
107 {
108   if (IS_LEAF_EL(el))
109     return ((struct heat_leaf_data *)LEAF_DATA(el))->est_c;
110   else
111     return 0.0;
112 }
113 
114 static REAL time_est = 0.0;
115 
get_time_est(MESH * mesh,ADAPT_INSTAT * adapt)116 static REAL get_time_est(MESH *mesh, ADAPT_INSTAT *adapt)
117 {
118   return time_est;
119 }
120 
121 /*******************************************************************************
122  * For test purposes: exact solution and its gradient (optional)
123  ******************************************************************************/
124 
125 static REAL eval_time_u = 0.0;
u(const REAL_D x)126 static REAL u(const REAL_D x)
127 {
128   return sin(M_PI*eval_time_u)*exp(-10.0*SCP_DOW(x,x));
129 }
130 
131 static REAL eval_time_u0 = 0.0;
u0(const REAL_D x)132 static REAL u0(const REAL_D x)
133 {
134   eval_time_u = eval_time_u0;
135   return u(x);
136 }
137 
138 /*******************************************************************************
139  * problem data: right hand side, boundary values
140  ******************************************************************************/
141 
142 static REAL eval_time_g = 0.0;
g(const REAL_D x)143 static REAL g(const REAL_D x)  /* boundary values, not optional */
144 {
145   eval_time_u = eval_time_g;
146   return u(x);
147 }
148 
149 static REAL eval_time_f = 0.0;
f(const REAL_D x)150 static REAL f(const REAL_D x)              /* -Delta u, not optional        */
151 {
152   REAL  r2 = SCP_DOW(x,x), ux  = sin(M_PI*eval_time_f)*exp(-10.0*r2);
153   REAL  ut = M_PI*cos(M_PI*eval_time_f)*exp(-10.0*r2);
154   return ut - (400.0*r2 - 20.0*DIM_OF_WORLD)*ux;
155 }
156 
157 
158 /*******************************************************************************
159  * write error and estimator data to files
160  ******************************************************************************/
161 
write_statistics(const char * path,ADAPT_INSTAT * adapt,int n_dof,REAL space_est,REAL time_est,REAL err_L2)162 static void write_statistics(const char *path, ADAPT_INSTAT *adapt, int n_dof,
163 			     REAL space_est, REAL time_est, REAL err_L2)
164 {
165   static FILE *file_ndof = NULL, *file_tau = NULL;
166   static FILE *file_space_est = NULL, *file_time_est = NULL, *file_L2_err = NULL;
167   const char  *name = fe_space->bas_fcts->name;
168   REAL        time = adapt->time;
169   char        filename[1024];
170 
171   if (!file_ndof)
172   {
173     sprintf(filename, "%s/n_dof-%s.agr", path ? path : ".", name);
174     file_ndof = fopen(filename, "w");
175   }
176 
177   if (!file_tau)
178   {
179     sprintf(filename, "%s/tau-%s.agr", path ? path : ".", name);
180     file_tau       = fopen(filename, "w");
181   }
182 
183   if (!file_space_est)
184   {
185     sprintf(filename, "%s/space_est-%s.agr", path ? path : ".", name);
186     file_space_est = fopen(filename, "w");
187   }
188 
189   if (!file_time_est)
190   {
191     sprintf(filename, "%s/time_est-%s.agr", path ? path : ".", name);
192     file_time_est  = fopen(filename, "w");
193   }
194 
195   if (!file_L2_err)
196   {
197     sprintf(filename, "%s/L2_err-%s.agr", path ? path : ".", name);
198     file_L2_err    = fopen(filename, "w");
199   }
200 
201   if (file_ndof)
202     fprintf(file_ndof, "%.6le %d\n", time, n_dof);
203 
204 /*---  don't print zeros, zeros do not allow log display of estimate ---*/
205   if (file_space_est)
206     fprintf(file_space_est, "%.6le %.6le\n", time, MAX(space_est,1.e-20));
207 
208   if (time > adapt->start_time)
209   {
210     if (file_tau)
211     {
212       fprintf(file_tau, "%.6le %.6le\n", time, adapt->timestep);
213     }
214 /*---  don't print zeros, zeros do not allow log display of estimate ---*/
215     if (file_time_est)
216       fprintf(file_time_est, "%.6le %.6le\n", time, MAX(time_est,1.e-20));
217   }
218 
219 /*---  don't print zeros, zeros do not allow log display of error    ---*/
220   if (file_L2_err)
221     fprintf(file_L2_err, "%.6le %.6le\n", time, MAX(err_L2,1.e-20));
222 
223   if (time >= adapt->end_time)
224   {
225     if (file_ndof)      fclose(file_ndof);
226     if (file_tau)       fclose(file_tau);
227     if (file_space_est) fclose(file_space_est);
228     if (file_time_est)  fclose(file_time_est);
229     if (file_L2_err)    fclose(file_L2_err);
230   }
231   else
232   {
233     fflush(NULL);
234   }
235 
236   return;
237 }
238 
239 /*******************************************************************************
240  * interpolation is solve on the initial grid
241  ******************************************************************************/
242 
interpol_u0(MESH * mesh)243 static void interpol_u0(MESH *mesh)
244 {
245   dof_compress(mesh);
246   interpol(u0, u_h);
247 
248   return;
249 }
250 
init_timestep(MESH * mesh,ADAPT_INSTAT * adapt)251 static void init_timestep(MESH *mesh, ADAPT_INSTAT *adapt)
252 {
253   FUNCNAME("init_timestep");
254 
255   INFO(adapt_instat->info,1,
256     "---8<---------------------------------------------------\n");
257   INFO(adapt_instat->info, 1,"starting new timestep\n");
258 
259   dof_copy(u_h, u_old);
260   return;
261 }
262 
set_time(MESH * mesh,ADAPT_INSTAT * adapt)263 static void set_time(MESH *mesh, ADAPT_INSTAT *adapt)
264 {
265   FUNCNAME("set_time");
266 
267   INFO(adapt->info,1,
268     "---8<---------------------------------------------------\n");
269   if (adapt->time == adapt->start_time)
270   {
271     INFO(adapt->info, 1,"start time: %.4le\n", adapt->time);
272   }
273   else
274   {
275     INFO(adapt->info, 1,"timestep for (%.4le %.4le), tau = %.4le\n",
276 			 adapt->time-adapt->timestep, adapt->time,
277 			 adapt->timestep);
278   }
279 
280   eval_time_f = adapt->time - (1 - theta)*adapt->timestep;
281   eval_time_g = adapt->time;
282 
283   return;
284 }
285 
close_timestep(MESH * mesh,ADAPT_INSTAT * adapt)286 static void close_timestep(MESH *mesh, ADAPT_INSTAT *adapt)
287 {
288   FUNCNAME("close_timestep");
289   static REAL err_max = 0.0;                     /* max space-time error    */
290   static REAL est_max = 0.0;                     /* max space-time estimate */
291   static int  write_fe_data = 0, write_stat_data = 0;
292   static int  step = 0;
293   static char path[256] = "./";
294 
295   REAL        space_est = adapt->adapt_space->err_sum;
296   REAL        tolerance = adapt->rel_time_error*adapt->tolerance;
297 
298   err_max = MAX(err_max, err_L2);
299   est_max = MAX(est_max, space_est + time_est);
300 
301   INFO(adapt->info,1,
302     "---8<---------------------------------------------------\n");
303 
304   if (adapt->time == adapt->start_time) {
305     tolerance = adapt->adapt_initial->tolerance;
306     INFO(adapt->info,1,"start time: %.4le\n", adapt->time);
307   } else {
308     tolerance += adapt->adapt_space->tolerance;
309     INFO(adapt->info,1,"timestep for (%.4le %.4le), tau = %.4le\n",
310 			adapt->time-adapt->timestep, adapt->time,
311 			adapt->timestep);
312   }
313   INFO(adapt->info,2,"max. est.  = %.4le, tolerance = %.4le\n",
314 		      est_max, tolerance);
315   INFO(adapt->info,2,"max. error = %.4le, ratio = %.2lf\n",
316 		      err_max, err_max/MAX(est_max,1.0e-20));
317 
318   if (!step) {
319     GET_PARAMETER(1, "write finite element data", "%d", &write_fe_data);
320     GET_PARAMETER(1, "write statistical data", "%d", &write_stat_data);
321     GET_PARAMETER(1, "data path", "%s", path);
322   }
323 
324   /*****************************************************************************
325    * write mesh and discrete solution to file for post-processing
326    ****************************************************************************/
327 
328   if (write_fe_data) {
329     const char *fn;
330 
331     fn= generate_filename(path, "mesh", step);
332     write_mesh_xdr(mesh, fn, adapt->time);
333     fn= generate_filename(path, "u_h", step);
334     write_dof_real_vec_xdr(u_h, fn);
335   }
336 
337   step++;
338 
339   /*****************************************************************************
340    * write data about estimate, error, time step size, etc.
341    ****************************************************************************/
342 
343   if (write_stat_data) {
344     int n_dof = fe_space->admin->size_used;
345     write_statistics(path, adapt, n_dof, space_est, time_est, err_L2);
346   }
347 
348   if (do_graphics) {
349     graphics(mesh, u_h, get_el_est, u, adapt->time);
350   }
351 
352   return;
353 }
354 
355 
356 /*--------------------------------------------------------------------------*/
357 /* build(): assemblage of the linear system: matrix, load vector,           */
358 /*          boundary values, called by adapt_method_stat()                  */
359 /*          on the first call initialize u_h, f_h, matrix and information   */
360 /*          for assembling the system matrix                                */
361 /*                                                                          */
362 /* struct op_data: structure for passing information from init_element() to*/
363 /*                 LALt()                                                   */
364 /* init_element(): initialization on the element; calculates the            */
365 /*                 coordinates and |det DF_S| used by LALt; passes these    */
366 /*                 values to LALt via user_data,                            */
367 /*                 called on each element by update_matrix()                */
368 /* LALt():         implementation of -Lambda id Lambda^t for -Delta u,      */
369 /*                 called by update_matrix()                                */
370 /* c():            implementation of 1/tau*m(,)                             */
371 /*--------------------------------------------------------------------------*/
372 
373 struct op_data /* application data (AKA "user_data") */
374 {
375   REAL_BD Lambda; /*  the gradient of the barycentric coordinates */
376   REAL    det;    /*  |det D F_S|                                 */
377 
378   REAL    tau_1;
379 };
380 
LALt(const EL_INFO * el_info,const QUAD * quad,int iq,void * ud)381 static const REAL_B *LALt(const EL_INFO *el_info, const QUAD *quad,
382 			  int iq, void *ud)
383 {
384   struct op_data *info = (struct op_data *)ud;
385   int i, j, k, dim = el_info->mesh->dim;
386   static REAL_BB LALt;
387 
388   for (i = 0; i <= dim; i++)
389     for (j = i; j <= dim; j++)
390     {
391       for (LALt[i][j] = k = 0; k < DIM_OF_WORLD; k++)
392 	LALt[i][j] += info->Lambda[i][k]*info->Lambda[j][k];
393       LALt[i][j] *= info->det;
394       LALt[j][i] = LALt[i][j];
395     }
396   return (const REAL_B *)LALt;
397 }
398 
c(const EL_INFO * el_info,const QUAD * quad,int iq,void * ud)399 static REAL c(const EL_INFO *el_info, const QUAD *quad, int iq, void *ud)
400 {
401   struct op_data *info = (struct op_data *)ud;
402 
403   return info->tau_1*info->det;
404 }
405 
assemble(DOF_MATRIX * matrix,DOF_REAL_VEC * fh,DOF_REAL_VEC * uh,const DOF_REAL_VEC * u_old,REAL theta,REAL tau,REAL (* f)(const REAL_D),REAL (* g)(const REAL_D),const BNDRY_FLAGS dirichlet_mask)406 static void assemble(DOF_MATRIX *matrix, DOF_REAL_VEC *fh, DOF_REAL_VEC *uh,
407 		     const DOF_REAL_VEC *u_old,
408 		     REAL theta, REAL tau,
409 		     REAL (*f)(const REAL_D), REAL (*g)(const REAL_D),
410 		     const BNDRY_FLAGS dirichlet_mask)
411 {
412   /* Some quantities remembered across calls. Think of this routine
413    * like being a "library function" ... The stuff is re-initialized
414    * whenever the finite element space changes. We use fe_space->admin
415    * to check for changes in the finite element space because
416    * DOF_ADMIN's are persisitent within ALBERTA, while fe-space are
417    * not.
418    */
419   static EL_MATRIX_INFO stiff_elmi, mass_elmi;
420   static const DOF_ADMIN *admin = NULL;
421   static const QUAD *quad = NULL;
422   static struct op_data op_data[1]; /* storage for det and Lambda */
423 
424   /* Remaining (non-static) variables. */
425   const BAS_FCTS *bas_fcts;
426   FLAGS          fill_flag;
427   EL_SCHAR_VEC   *bound;
428 
429   /* Initialize persistent variables. */
430   if (admin != uh->fe_space->admin) {
431     OPERATOR_INFO stiff_opi = { NULL, }, mass_opi = { NULL, };
432 
433     admin    = uh->fe_space->admin;
434 
435     stiff_opi.row_fe_space   = uh->fe_space;
436     stiff_opi.quad[2]        = quad;
437     stiff_opi.LALt.real      = LALt;
438     stiff_opi.LALt_pw_const  = true;
439     stiff_opi.LALt_symmetric = true;
440     stiff_opi.user_data      = op_data;
441 
442     fill_matrix_info(&stiff_opi, &stiff_elmi);
443 
444     mass_opi.row_fe_space = uh->fe_space;
445     mass_opi.quad[0]      = quad;
446     mass_opi.c.real       = c;
447     mass_opi.c_pw_const   = true;
448     mass_opi.user_data    = op_data;
449 
450     fill_matrix_info(&mass_opi, &mass_elmi);
451 
452     quad = get_quadrature(uh->fe_space->bas_fcts->dim,
453 			  2*uh->fe_space->bas_fcts->degree);
454   }
455 
456   op_data->tau_1 = 1.0/tau;
457 
458   /* Assemble the matrix and the right hand side. The idea is to
459    * assemble the local mass and stiffness matrices only once, and to
460    * use it to update both, the system matrix and the contribution of
461    * the time discretisation to the RHS.
462    */
463   clear_dof_matrix(matrix);
464   dof_set(0.0, fh);
465 
466   bas_fcts = uh->fe_space->bas_fcts;
467 
468   bound = get_el_schar_vec(bas_fcts);
469 
470   fill_flag = CALL_LEAF_EL|FILL_COORDS|FILL_BOUND;
471   TRAVERSE_FIRST(uh->fe_space->mesh, -1, fill_flag) {
472     const EL_REAL_VEC  *u_old_loc;
473     const EL_DOF_VEC   *dof;
474     const EL_BNDRY_VEC *bndry_bits;
475     const EL_MATRIX *stiff_loc, *mass_loc;
476 
477     /* Get the local coefficients of u_old, boundary info, dof-mapping */
478     u_old_loc  = fill_el_real_vec(NULL, el_info->el, u_old);
479     dof        = get_dof_indices(NULL, uh->fe_space, el_info->el);
480     bndry_bits = get_bound(NULL, bas_fcts, el_info);
481 
482     /* Initialization of values used by LALt and c. It is not
483      * necessary to introduce an extra "init_element()" hook for our
484      * OPERATOR_INFO structures; the line below is just what would be
485      * contained in that function (compare with ellipt.c).
486      *
487      * Beware to replace the "..._0cd()" for co-dimension 0 by its
488      * proper ..._dim() variant if ever "porting" this stuff to
489      * parametric meshes on manifolds.
490      */
491     op_data->det = el_grd_lambda_0cd(el_info, op_data->Lambda);
492 
493     /* Obtain the local (i.e. per-element) matrices. */
494     stiff_loc = stiff_elmi.el_matrix_fct(el_info, stiff_elmi.fill_info);
495     mass_loc  = mass_elmi.el_matrix_fct(el_info, mass_elmi.fill_info);
496 
497     /* Translate the geometric boundary classification into
498      * Dirichlet/Neumann/Interior boundary condition
499      * interpretation. Inside the loop over the mesh-elements we need
500      * only to care about Dirichlet boundary conditions.
501      */
502     dirichlet_map(bound, bndry_bits, dirichlet_mask);
503 
504     /* add theta*a(psi_i,psi_j) + 1/tau*m(4*u^3*psi_i,psi_j) */
505     if (theta) {
506       add_element_matrix(matrix,
507 			 theta, stiff_loc, NoTranspose, dof, dof, bound);
508     }
509     add_element_matrix(matrix, 1.0, mass_loc, NoTranspose, dof, dof, bound);
510 
511       /* compute the contributions from the old time-step:
512        *
513        * f += -(1-theta)*a(u_old,psi_i) + 1/tau*m(u_old,psi_i)
514        */
515     bi_mat_el_vec(-(1.0 - theta), stiff_loc,
516 		  1.0, mass_loc, u_old_loc,
517 		  1.0, fh, dof, bound);
518 
519   } TRAVERSE_NEXT();
520 
521   free_el_schar_vec(bound);
522 
523   /* Indicate that the boundary conditions are built into the matrix,
524    * needed e.g. by the hierarchical preconditioners.
525    */
526   BNDRY_FLAGS_CPY(matrix->dirichlet_bndry, dirichlet_mask);
527 
528   /* Add the "force-term" to the right hand side (L2scp_...() is additive) */
529   L2scp_fct_bas(f, quad, fh);
530 
531   /* Close the system by imposing suitable boundary conditions. Have a
532    * look at ellipt.c for how to impose more complicated stuff; here
533    * we only use Dirichlet b.c.
534    */
535   dirichlet_bound(fh, uh, NULL, dirichlet_mask, g);
536 }
537 
build(MESH * mesh,U_CHAR flag)538 static void build(MESH *mesh, U_CHAR flag)
539 {
540   FUNCNAME("build");
541 
542   dof_compress(mesh);
543 
544   INFO(adapt_instat->adapt_space->info, 2,
545     "%d DOFs for %s\n", fe_space->admin->size_used, fe_space->name);
546 
547   assemble(matrix, f_h, u_h, u_old, theta, adapt_instat->timestep,
548 	   f, g, dirichlet_mask);
549 }
550 
551 /*--------------------------------------------------------------------------*/
552 /* solve(): solve the linear system, called by adapt_method_stat()          */
553 /*--------------------------------------------------------------------------*/
554 
solve(MESH * mesh)555 static void solve(MESH *mesh)
556 {
557   FUNCNAME("solve");
558   static REAL tol = 1.e-8, ssor_omega = 1.0;
559   static int  max_iter = 1000, info = 2, restart = 0, ssor_iter = 1, ilu_k = 8;
560   static OEM_PRECON icon = DiagPrecon;
561   static OEM_SOLVER solver = NoSolver;
562   const PRECON *precon;
563 
564   if (solver == NoSolver) {
565     GET_PARAMETER(1, "solver", "%d", &solver);
566     GET_PARAMETER(1, "solver tolerance", "%f", &tol);
567     GET_PARAMETER(1, "solver precon", "%d", &icon);
568     GET_PARAMETER(1, "solver max iteration", "%d", &max_iter);
569     GET_PARAMETER(1, "solver info", "%d", &info);
570     if (icon == __SSORPrecon) {
571     	GET_PARAMETER(1, "precon ssor omega", "%f", &ssor_omega);
572     	GET_PARAMETER(1, "precon ssor iter", "%d", &ssor_iter);
573     }
574     if (icon == ILUkPrecon)
575         GET_PARAMETER(1, "precon ilu(k)", "%d", &ilu_k);
576     if (solver == GMRes) {
577       GET_PARAMETER(1, "solver restart", "%d", &restart);
578     }
579   }
580 
581   if (icon == ILUkPrecon)
582 	  precon = init_oem_precon(matrix, NULL, info, ILUkPrecon, ilu_k);
583   else
584 	  precon = init_oem_precon(matrix, NULL, info, icon, ssor_omega, ssor_iter);
585   oem_solve_s(matrix, NULL, f_h, u_h,
586 	      solver, tol, precon, restart, max_iter, info);
587 }
588 
589 /*--------------------------------------------------------------------------*/
590 /* Functions for error estimate:                                            */
591 /* estimate():   calculates error estimate via heat_est()                   */
592 /*               calculates exact error also (only for test purpose),       */
593 /*               called by adapt_method_stat()                              */
594 /*--------------------------------------------------------------------------*/
595 
r(const EL_INFO * el_info,const QUAD * quad,int iq,REAL uh_at_qp,const REAL_D grd_uh_at_qp,REAL t)596 static REAL r(const EL_INFO *el_info,
597 	      const QUAD *quad, int iq,
598 	      REAL uh_at_qp, const REAL_D grd_uh_at_qp,
599 	      REAL t)
600 {
601   REAL_D      x;
602 
603   coord_to_world(el_info, quad->lambda[iq], x);
604   eval_time_f = t;
605 
606   return -f(x);
607 }
608 
609 
estimate(MESH * mesh,ADAPT_STAT * adapt)610 static REAL estimate(MESH *mesh, ADAPT_STAT *adapt)
611 {
612   FUNCNAME("estimate");
613   static REAL    C[4] = {-1.0, 1.0, 1.0, 1.0};
614   static REAL_DD A = {{0.0}};
615   FLAGS          r_flag = 0;  /* = (INIT_UH|INIT_GRD_UH), if needed by r()  */
616   int            n;
617   REAL           space_est;
618 
619   eval_time_u = adapt_instat->time;
620 
621   if (C[0] < 0) {
622     C[0] = 1.0;
623     GET_PARAMETER(1, "estimator C0", "%f", &C[0]);
624     GET_PARAMETER(1, "estimator C1", "%f", &C[1]);
625     GET_PARAMETER(1, "estimator C2", "%f", &C[2]);
626     GET_PARAMETER(1, "estimator C3", "%f", &C[3]);
627 
628     for (n = 0; n < DIM_OF_WORLD; n++) {
629       A[n][n] = 1.0;   /* set diogonal of A; all other elements are zero */
630     }
631   }
632 
633   time_est = heat_est(u_h, u_old, adapt_instat, rw_el_est, rw_el_estc,
634 		      -1 /* quad_degree */,
635 		      C, (const REAL_D *)A, dirichlet_mask,
636 		      r, r_flag, NULL /* gn() */, 0 /* gn_flag */);
637 
638   space_est = adapt_instat->adapt_space->err_sum;
639   err_L2 = L2_err(u, u_h, NULL, false, false, NULL, NULL);
640 
641   INFO(adapt_instat->info,2,
642     "---8<---------------------------------------------------\n");
643   INFO(adapt_instat->info, 2,"time = %.4le with timestep = %.4le\n",
644 			      adapt_instat->time, adapt_instat->timestep);
645   INFO(adapt_instat->info, 2,"estimate   = %.4le, max = %.4le\n", space_est,
646 			      sqrt(adapt_instat->adapt_space->err_max));
647   INFO(adapt_instat->info, 2,"||u-uh||L2 = %.4le, ratio = %.2lf\n", err_L2,
648 			      err_L2/MAX(space_est,1.e-20));
649 
650   return adapt_instat->adapt_space->err_sum;
651 }
652 
est_initial(MESH * mesh,ADAPT_STAT * adapt)653 static REAL est_initial(MESH *mesh, ADAPT_STAT *adapt)
654 {
655   err_L2 = adapt->err_sum =
656     L2_err(u0, u_h, NULL, false, false, rw_el_est, &adapt->err_max);
657   return adapt->err_sum;
658 }
659 
660 /*--------------------------------------------------------------------------*/
661 /* main program                                                             */
662 /*--------------------------------------------------------------------------*/
663 
main(int argc,char ** argv)664 int main(int argc, char **argv)
665 {
666   FUNCNAME("main");
667   MACRO_DATA     *data;
668   MESH           *mesh;
669   const BAS_FCTS *lagrange;
670   int             n_refine = 0, p = 1, dim;
671   char            filename[PATH_MAX];
672   REAL            fac = 1.0;
673 
674   /*****************************************************************************
675    * first of all, initialize the access to parameters of the init file
676    ****************************************************************************/
677   parse_parameters(argc, argv, "INIT/heat.dat");
678 
679   GET_PARAMETER(1, "mesh dimension", "%d", &dim);
680   GET_PARAMETER(1, "macro file name", "%s", filename);
681   GET_PARAMETER(1, "global refinements", "%d", &n_refine);
682   GET_PARAMETER(1, "parameter theta", "%e", &theta);
683   GET_PARAMETER(1, "polynomial degree", "%d", &p);
684   GET_PARAMETER(1, "online graphics", "%d", &do_graphics);
685 
686   /*****************************************************************************
687   *  get a mesh, and read the macro triangulation from file
688   ****************************************************************************/
689   data = read_macro(filename);
690   mesh = GET_MESH(dim, "ALBERTA mesh", data,
691 		  NULL /* init_node_projection() */,
692 		  NULL /* init_wall_trafos() */);
693   free_macro_data(data);
694 
695   init_leaf_data(mesh, sizeof(struct heat_leaf_data),
696 		 NULL /* refine_leaf_data() */,
697 		 NULL /* coarsen_leaf_data() */);
698 
699   /* Finite element spaces can be added at any time, but it is more
700    * efficient to do so before refining the mesh a lot.
701    */
702   lagrange = get_lagrange(mesh->dim, p);
703   TEST_EXIT(lagrange, "no lagrange BAS_FCTS\n");
704 
705   fe_space = get_fe_space(mesh, lagrange->name, lagrange, 1, ADM_FLAGS_DFLT);
706 
707   global_refine(mesh, n_refine * dim, FILL_NOTHING);
708 
709   /*****************************************************************************
710    *  initialize the global variables shared across build(), solve()
711    *  and estimate().
712    ****************************************************************************/
713   matrix = get_dof_matrix("A", fe_space, NULL);
714   f_h    = get_dof_real_vec("f_h", fe_space);
715   u_h    = get_dof_real_vec("u_h", fe_space);
716   u_h->refine_interpol = fe_space->bas_fcts->real_refine_inter;
717   u_h->coarse_restrict = fe_space->bas_fcts->real_coarse_inter;
718   u_old  = get_dof_real_vec("u_old", fe_space);
719   u_old->refine_interpol = fe_space->bas_fcts->real_refine_inter;
720   u_old->coarse_restrict = fe_space->bas_fcts->real_coarse_inter;
721   dof_set(0.0, u_h);      /*  initialize u_h  !                          */
722 
723   BNDRY_FLAGS_ALL(dirichlet_mask); /* Only Dirichlet b.c. supported here */
724 
725   /*****************************************************************************
726    *  init adapt_instat structure
727    ****************************************************************************/
728   adapt_instat = get_adapt_instat(dim, "heat", "adapt", 2, adapt_instat);
729 
730   /* Some animation in between ... */
731   if (do_graphics) {
732     graphics(mesh, NULL, NULL, NULL, adapt_instat->start_time);
733   }
734 
735   /*****************************************************************************
736    *  adapt time step size to refinement level and polynomial degree,
737    *  based on the known L2-error error estimates.
738    ****************************************************************************/
739   if (theta < 0.5) {
740     WARNING("You are using the explicit Euler scheme\n");
741     WARNING("Use a sufficiently small time step size!!!\n");
742     fac = 1.0e-3;
743   }
744 
745   if (theta == 0.5) {
746     adapt_instat->timestep *= fac*pow(2, -(REAL)(p*(n_refine))/2.0);
747   } else {
748     adapt_instat->timestep *= fac*pow(2, -(REAL)(p*(n_refine)));
749   }
750   MSG("using initial timestep size = %.4le\n", adapt_instat->timestep);
751 
752   eval_time_u0 = adapt_instat->start_time;
753 
754   adapt_instat->adapt_initial->get_el_est = get_el_est;
755   adapt_instat->adapt_initial->estimate = est_initial;
756   adapt_instat->adapt_initial->solve = interpol_u0;
757 
758   adapt_instat->adapt_space->get_el_est   = get_el_est;
759   adapt_instat->adapt_space->get_el_estc  = get_el_estc;
760   adapt_instat->adapt_space->estimate = estimate;
761   adapt_instat->adapt_space->build_after_coarsen = build;
762   adapt_instat->adapt_space->solve = solve;
763 
764   adapt_instat->init_timestep  = init_timestep;
765   adapt_instat->set_time       = set_time;
766   adapt_instat->get_time_est   = get_time_est;
767   adapt_instat->close_timestep = close_timestep;
768 
769   /*****************************************************************************
770    * ... off we go ...
771    ****************************************************************************/
772   adapt_method_instat(mesh, adapt_instat);
773 
774   WAIT_REALLY;
775 
776   return 0;
777 }
778