libMesh
adjoints_ex7.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 
18 
19 
20 // Adjoints Example 7 - Unsteady Adjoint Refinement Error Estimator for Model Error Estimation.
21 // \author Vikram Garg
22 // \date 2020
23 //
24 // This example illustrates the use of the AdjointRefinementErrorEstimator in an unsteady setting. It also
25 // illustrates adjoint estimation of model error.
26 // The PDE we are interested in is the simple 2-d heat
27 // equation:
28 // partial(T)/partial(t) - K(x) Laplacian(T) = 1.0
29 // with initial condition:
30 // T(x,y;0) = 1.0 and boundary conditions:
31 // T(boundary;t) = 1.0
32 // with K(x) = 0.001 + x for the true model, and K = 0.01 for the coarse model.
33 // We are interested in estimating the effect of using K = 0.01 on 2 QoIs.
34 
35 // We specify our Quantity of Interest (QoI) as
36 // Q0(u) = int_{domain} u(x,y;tf) 1.0 dx dy (A temporally non-smooth QoI evaluated at the final time)
37 // Q1(u) = int_{0}^{tf} int_{domain} u(x,y;t) 1.0 dx dy dt (A temporally smooth QoI)
38 
39 // We verify the adjoint identity,
40 // Q(u_true) - Q(u_coarse) = R(u_true, z)
41 
42 // Local includes
43 #include "initial.h"
44 #include "adjoint_initial.h"
45 #include "femparameters.h"
46 #include "heatsystem.h"
47 #include "sigma_physics.h"
48 
49 // Libmesh includes
50 #include "libmesh/equation_systems.h"
51 #include "libmesh/dirichlet_boundaries.h"
52 #include "libmesh/system_norm.h"
53 #include "libmesh/numeric_vector.h"
54 #include "libmesh/enum_solver_package.h"
55 
56 #include "libmesh/mesh.h"
57 #include "libmesh/mesh_generation.h"
58 #include "libmesh/mesh_refinement.h"
59 
60 #include "libmesh/petsc_diff_solver.h"
61 #include "libmesh/steady_solver.h"
62 #include "libmesh/euler_solver.h"
63 #include "libmesh/euler2_solver.h"
64 #include "libmesh/newton_solver.h"
65 #include "libmesh/petsc_diff_solver.h"
66 #include "libmesh/twostep_time_solver.h"
67 
68 #include "libmesh/getpot.h"
69 #include "libmesh/tecplot_io.h"
70 #include "libmesh/gmv_io.h"
71 #include "libmesh/exodusII_io.h"
72 
73 #include "libmesh/adjoint_refinement_estimator.h"
74 #include "libmesh/error_vector.h"
75 
76 // SolutionHistory Includes
77 #include "libmesh/solution_history.h"
78 #include "libmesh/memory_solution_history.h"
79 #include "libmesh/file_solution_history.h"
80 
81 // C++ includes
82 #include <iostream>
83 #include <sys/time.h>
84 #include <iomanip>
85 #include <memory>
86 
87 void write_output(EquationSystems & es,
88  unsigned int t_step, // The current time step count
89  std::string solution_type, // primal or adjoint solve
90  FEMParameters & param)
91 {
92  // Ignore parameters when there are no output formats available.
93  libmesh_ignore(es, t_step, solution_type, param);
94 
95 #ifdef LIBMESH_HAVE_GMV
96  if (param.output_gmv)
97  {
98  MeshBase & mesh = es.get_mesh();
99 
100  std::ostringstream file_name_gmv;
101  file_name_gmv << solution_type
102  << ".out.gmv."
103  << std::setw(2)
104  << std::setfill('0')
105  << std::right
106  << t_step;
107 
108  GMVIO(mesh).write_equation_systems(file_name_gmv.str(), es);
109  }
110 #endif
111 
112 #ifdef LIBMESH_HAVE_EXODUS_API
113  if (param.output_exodus)
114  {
115  MeshBase & mesh = es.get_mesh();
116 
117  // We write out one file per timestep. The files are named in
118  // the following way:
119  // foo.e
120  // foo.e-s002
121  // foo.e-s003
122  // ...
123  // so that, if you open the first one with Paraview, it actually
124  // opens the entire sequence of adapted files.
125  std::ostringstream file_name_exodus;
126 
127  file_name_exodus << solution_type << ".e";
128  if (t_step > 0)
129  file_name_exodus << "-s"
130  << std::setw(3)
131  << std::setfill('0')
132  << std::right
133  << t_step + 1;
134 
135  // TODO: Get the current time from the System...
136  ExodusII_IO(mesh).write_timestep(file_name_exodus.str(),
137  es,
138  1,
139  /*time=*/t_step + 1);
140  }
141 #endif
142 }
143 
145 {
146  // Use analytical jacobians?
147  system.analytic_jacobians() = param.analytic_jacobians;
148 
149  // Verify analytic jacobians against numerical ones?
152 
153  // More desperate debugging options
155  system.print_solutions = param.print_solutions;
157  system.print_residuals = param.print_residuals;
159  system.print_jacobians = param.print_jacobians;
162 
163  // Solve this as a time-dependent or steady system
164  if (param.transient)
165  {
166  std::unique_ptr<UnsteadySolver> innersolver;
167  if (param.timesolver_core == "euler2")
168  {
169  auto euler2solver = std::make_unique<Euler2Solver>(system);
170  euler2solver->theta = param.timesolver_theta;
171  innersolver = std::move(euler2solver);
172  }
173  else if (param.timesolver_core == "euler")
174  {
175  auto eulersolver = std::make_unique<EulerSolver>(system);
176  eulersolver->theta = param.timesolver_theta;
177  innersolver = std::move(eulersolver);
178  }
179  else
180  libmesh_error_msg("This example (and unsteady adjoints in libMesh) only support Backward Euler and explicit methods.");
181 
182  if (param.timesolver_tolerance)
183  {
184  system.time_solver = std::make_unique<TwostepTimeSolver>(system);
185  auto timesolver = cast_ptr<TwostepTimeSolver *>(system.time_solver.get());
186 
187  timesolver->max_growth = param.timesolver_maxgrowth;
188  timesolver->target_tolerance = param.timesolver_tolerance;
189  timesolver->upper_tolerance = param.timesolver_upper_tolerance;
190  timesolver->component_norm = SystemNorm(param.timesolver_norm);
191  timesolver->core_time_solver = std::move(innersolver);
192 
193  // The Memory/File Solution History object we will set the system SolutionHistory object to
194  if(param.solution_history_type == "memory")
195  {
196  MemorySolutionHistory heatsystem_solution_history(system);
197  system.time_solver->set_solution_history(heatsystem_solution_history);
198  }
199  else if (param.solution_history_type == "file")
200  {
201  FileSolutionHistory heatsystem_solution_history(system);
202  system.time_solver->set_solution_history(heatsystem_solution_history);
203  }
204  else
205  libmesh_error_msg("Unrecognized solution history type: " << param.solution_history_type);
206  }
207  else
208  {
209  system.time_solver = std::move(innersolver);
210 
211  // The Memory/File Solution History object we will set the system SolutionHistory object to
212  if(param.solution_history_type == "memory")
213  {
214  MemorySolutionHistory heatsystem_solution_history(system);
215  system.time_solver->set_solution_history(heatsystem_solution_history);
216  }
217  else if (param.solution_history_type == "file")
218  {
219  FileSolutionHistory heatsystem_solution_history(system);
220  system.time_solver->set_solution_history(heatsystem_solution_history);
221  }
222  else
223  libmesh_error_msg("Unrecognized solution history type: " << param.solution_history_type);
224  }
225  }
226  else
227  system.time_solver = std::make_unique<SteadySolver>(system);
228 
229  system.time_solver->reduce_deltat_on_diffsolver_failure = param.deltat_reductions;
230  system.time_solver->quiet = param.time_solver_quiet;
231 
232  #ifdef LIBMESH_ENABLE_DIRICHLET
233  // Create any Dirichlet boundary conditions
234  typedef
235  std::map<boundary_id_type, FunctionBase<Number> *>::
236  const_iterator Iter;
237 
238  for (Iter i = param.dirichlet_conditions.begin();
239  i != param.dirichlet_conditions.end(); ++i)
240  {
241  boundary_id_type b = i->first;
242  FunctionBase<Number> *f = i->second;
243 
244  system.get_dof_map().add_dirichlet_boundary(DirichletBoundary({b},
246  f));
247 
248  libMesh::out << "Added Dirichlet boundary " << b << " for variables ";
249  for (std::size_t vi=0; vi != param.dirichlet_condition_variables[b].size(); ++vi)
251  libMesh::out << std::endl;
252  }
253  #endif // LIBMESH_ENABLE_DIRICHLET
254 
255  // Set the time stepping options
256  system.deltat = param.deltat;
257 
258  // And the integration options
260 
261  // And the nonlinear solver options
262  if (param.use_petsc_snes)
263  {
264 #ifdef LIBMESH_HAVE_PETSC
265  system.time_solver->diff_solver() = std::make_unique<PetscDiffSolver>(system);
266 #else
267  libmesh_error_msg("This example requires libMesh to be compiled with PETSc support.");
268 #endif
269  }
270  else
271  {
272  system.time_solver->diff_solver() = std::make_unique<NewtonSolver>(system);
273  auto solver = cast_ptr<NewtonSolver*>(system.time_solver->diff_solver().get());
274 
275  solver->quiet = param.solver_quiet;
276  solver->verbose = param.solver_verbose;
277  solver->max_nonlinear_iterations = param.max_nonlinear_iterations;
278  solver->minsteplength = param.min_step_length;
279  solver->relative_step_tolerance = param.relative_step_tolerance;
280  solver->relative_residual_tolerance = param.relative_residual_tolerance;
281  solver->require_residual_reduction = param.require_residual_reduction;
282  solver->linear_tolerance_multiplier = param.linear_tolerance_multiplier;
283  if (system.time_solver->reduce_deltat_on_diffsolver_failure)
284  {
285  solver->continue_after_max_iterations = true;
286  solver->continue_after_backtrack_failure = true;
287  }
289 
290  // And the linear solver options
291  solver->max_linear_iterations = param.max_linear_iterations;
292  solver->initial_linear_tolerance = param.initial_linear_tolerance;
293  solver->minimum_linear_tolerance = param.minimum_linear_tolerance;
294  }
295 }
296 
297 #ifdef LIBMESH_ENABLE_AMR
298 std::unique_ptr<AdjointRefinementEstimator>
299 build_adjoint_refinement_error_estimator(QoISet &qois, FEMPhysics* supplied_physics, FEMParameters &/*param*/)
300 {
301  std::cout<<"Computing the error estimate using the Adjoint Refinement Error Estimator"<<std::endl<<std::endl;
302 
303  auto adjoint_refinement_estimator = std::make_unique<AdjointRefinementEstimator>();
304 
305  adjoint_refinement_estimator->qoi_set() = qois;
306 
307  // Set the residual evaluation physics pointer that belongs to the Adjoint Refinement
308  // Error Estimator class to the residual_physics pointer passed by the user
309  adjoint_refinement_estimator->set_residual_evaluation_physics(supplied_physics);
310 
311  // Also set the adjoint evaluation physics pointer to the same pointer
312  adjoint_refinement_estimator->set_adjoint_evaluation_physics(supplied_physics);
313 
314  // We enrich the FE space for the dual problem by doing a uniform h refinements
315  // Since we are dealing with model error, 0 h refinements are legal.
316  adjoint_refinement_estimator->number_h_refinements = 0;
317 
318  return adjoint_refinement_estimator;
319 }
320 #endif // LIBMESH_ENABLE_AMR
321 
322 // The main program.
323 int main (int argc, char ** argv)
324 {
325  // Skip adaptive examples on a non-adaptive libMesh build
326 #ifndef LIBMESH_ENABLE_AMR
327  libmesh_ignore(argc, argv);
328  libmesh_example_requires(false, "--enable-amr");
329 #else
330  // Skip this 2D example if libMesh was compiled as 1D-only.
331  libmesh_example_requires(2 <= LIBMESH_DIM, "2D support");
332 
333  // We use Dirichlet boundary conditions here
334 #ifndef LIBMESH_ENABLE_DIRICHLET
335  libmesh_example_requires(false, "--enable-dirichlet");
336 #endif
337 
338  // Initialize libMesh.
339  LibMeshInit init (argc, argv);
340 
341  // This doesn't converge with Trilinos for some reason...
342  libmesh_example_requires(libMesh::default_solver_package() == PETSC_SOLVERS, "--enable-petsc");
343 
344  libMesh::out << "Started " << argv[0] << std::endl;
345 
346  // Make sure the general input file exists, and parse it
347  {
348  std::ifstream i("general.in");
349  libmesh_error_msg_if(!i, '[' << init.comm().rank() << "] Can't find general.in; exiting early.");
350  }
351  GetPot infile("general.in");
352 
353  // But allow the command line to override it.
354  infile.parse_command_line(argc, argv);
355 
356  // Read in parameters from the input file
357  FEMParameters param(init.comm());
358  param.read(infile);
359 
360  // Create a mesh with the given dimension, distributed
361  // across the default MPI communicator.
362  Mesh mesh(init.comm(), cast_int<unsigned char>(param.dimension));
363 
364  // And an object to refine it
365  auto mesh_refinement = std::make_unique<MeshRefinement>(mesh);
366 
367  // And an EquationSystems to run on it
368  EquationSystems equation_systems (mesh);
369 
370  libMesh::out << "Building mesh" << std::endl;
371 
372  // Build a unit square
373  ElemType elemtype;
374 
375  if (param.elementtype == "tri" ||
376  param.elementtype == "unstructured")
377  elemtype = TRI3;
378  else
379  elemtype = QUAD4;
380 
381  MeshTools::Generation::build_square (mesh, param.coarsegridx, param.coarsegridy,
382  param.domain_xmin, param.domain_xmin + param.domain_edge_width,
383  param.domain_ymin, param.domain_ymin + param.domain_edge_length,
384  elemtype);
385 
386  libMesh::out << "Building system" << std::endl;
387 
388  HeatSystem & system = equation_systems.add_system<HeatSystem> ("HeatSystem");
389 
390  set_system_parameters(system, param);
391 
392  // Also build an FEMPhysics to provide the residual definition for the adjoint activities
393  auto sigma_physics = std::make_unique<SigmaPhysics>();
394  sigma_physics->init_data(system);
395 
396  libMesh::out << "Initializing systems" << std::endl;
397 
398  // Initialize the system
399  equation_systems.init ();
400 
401  // Refine the grid again if requested
402  for (unsigned int i=0; i != param.extrarefinements; ++i)
403  {
404  mesh_refinement->uniformly_refine(1);
405  equation_systems.reinit();
406  }
407 
408  libMesh::out << "Setting primal initial conditions" << std::endl;
409 
411  equation_systems.parameters);
412 
413  // Output the H1 norm of the initial conditions
414  libMesh::out << "|U("
415  << system.time
416  << ")|= "
417  << system.calculate_norm(*system.solution, 0, H1)
418  << std::endl
419  << std::endl;
420 
421  // Tell system that we have two qois
422  system.init_qois(2);
423 
424  // Add the adjoint vectors (and the old adjoint vectors) to system
425  system.time_solver->init_adjoints();
426 
427  // Plot the initial conditions
428  write_output(equation_systems, 0, "primal", param);
429 
430  // Print information about the mesh and system to the screen.
431  mesh.print_info();
432  equation_systems.print_info();
433 
434  // Accumulated integrated QoIs
435  Number QoI_1_accumulated = 0.0;
436 
437  // In optimized mode we catch any solver errors, so that we can
438  // write the proper footers before closing. In debug mode we just
439  // let the exception throw so that gdb can grab it.
440 #if defined(NDEBUG) && defined(LIBMESH_ENABLE_EXCEPTIONS)
441  try
442 #endif
443  {
444  // Now we begin the timestep loop to compute the time-accurate
445  // solution of the equations.
446  for (unsigned int t_step=param.initial_timestep;
447  t_step != param.initial_timestep + param.n_timesteps; ++t_step)
448  {
449  // A pretty update message
450  libMesh::out << " Solving time step "
451  << t_step
452  << ", time = "
453  << system.time
454  << std::endl;
455 
456  // Solve the forward problem at time t, to obtain the solution at time t + dt
457  system.solve();
458 
459  // Output the H1 norm of the computed solution
460  libMesh::out << "|U("
461  << system.time + system.time_solver->last_completed_timestep_size()
462  << ")|= "
463  << system.calculate_norm(*system.solution, 0, H1)
464  << std::endl;
465 
466  // Advance to the next timestep in a transient problem
467  libMesh::out << "Advancing timestep" << std::endl << std::endl;
468  system.time_solver->advance_timestep();
469 
470  // Write out this timestep
471  write_output(equation_systems, t_step+1, "primal", param);
472  }
473  // End timestep loop
474 
475  // Set the time instant for the evaluation of the non-smooth QoI 0
476  (dynamic_cast<HeatSystem &>(system).QoI_time_instant)[0] = system.time;
477 
478  // Now loop over the timesteps again to get the QoIs
479 
480  // Reset the initial time
481  system.time = 0.0;
482 
483  system.time_solver->retrieve_timestep();
484 
485  QoI_1_accumulated = 0.0;
486 
487  for (unsigned int t_step=param.initial_timestep;
488  t_step != param.initial_timestep + param.n_timesteps; ++t_step)
489  {
490  system.time_solver->integrate_qoi_timestep();
491 
492  QoI_1_accumulated += system.get_qoi_value(1);
493  }
494 
495  std::cout<< "The computed QoI 0 is " << std::setprecision(17) << system.get_qoi_value(0) << std::endl;
496  std::cout<< "The computed QoI 1 is " << std::setprecision(17) << QoI_1_accumulated << std::endl;
497 
499 
500  // Now we will solve the backwards in time adjoint problem
501  libMesh::out << std::endl << "Solving the adjoint problems." << std::endl;
502 
503  // We need to tell the library that it needs to project the adjoint, so
504  // MemorySolutionHistory knows it has to save it
505 
506  // Tell the library to project the adjoint vectors, and hence, solution history to
507  // save them.
508  const std::string & adjoint_solution_name0 = "adjoint_solution0";
509  const std::string & adjoint_solution_name1 = "adjoint_solution1";
510  const std::string & old_adjoint_solution_name0 = "_old_adjoint_solution0";
511  const std::string & old_adjoint_solution_name1 = "_old_adjoint_solution1";
512 
513  system.set_vector_preservation(adjoint_solution_name0, true);
514  system.set_vector_preservation(adjoint_solution_name1, true);
515  system.set_vector_preservation(old_adjoint_solution_name0, true);
516  system.set_vector_preservation(old_adjoint_solution_name1, true);
517 
518  libMesh::out << "Setting adjoint initial conditions Z("
519  << system.time
520  << ")"
521  <<std::endl;
522 
523  // The first thing we have to do is to apply the adjoint initial
524  // condition. The user should supply these. Here they are specified
525  // in the functions adjoint_initial_value and adjoint_initial_gradient
528  equation_systems.parameters,
529  system.get_adjoint_solution(0));
530 
531  // The initial condition for adjoint 1 is zero, which is the default.
532 
533  // Since we have specified an adjoint solution for the current
534  // time (T), set the adjoint_already_solved boolean to true, so
535  // we dont solve unnecessarily in the adjoint sensitivity method
536  system.set_adjoint_already_solved(true);
537 
538  Real Z0_norm = system.calculate_norm(system.get_adjoint_solution(0), 0, H1);
539  Real Z1_norm = system.calculate_norm(system.get_adjoint_solution(1), 0, H1);
540 
541  libMesh::out << "|Z0("
542  << system.time
543  << ")|= "
544  << Z0_norm
545  << std::endl
546  << std::endl;
547 
548  libMesh::out << "|Z1("
549  << system.time
550  << ")|= "
551  << Z1_norm
552  << std::endl
553  << std::endl;
554 
555  // Call the adjoint advance timestep here to save the adjoint
556  // initial conditions to disk
557  system.time_solver->adjoint_advance_timestep();
558 
559  Real Z0_old_norm = system.calculate_norm(system.get_vector(old_adjoint_solution_name0), 0, H1);
560  Real Z1_old_norm = system.calculate_norm(system.get_vector(old_adjoint_solution_name1), 0, H1);
561 
562  // Make sure that we have copied the old adjoint solutions properly
563  libmesh_assert(Z0_norm == Z0_old_norm);
564  libmesh_assert(Z1_norm == Z1_old_norm);
565 
566  libMesh::out << "|Z0_old("
567  << system.time + (system.time_solver->last_completed_timestep_size())/((param.timesolver_tolerance) ? 2.0 : 1.0)
568  << ")|= "
569  << system.calculate_norm(system.get_vector(old_adjoint_solution_name0), 0, H1)
570  << std::endl
571  << std::endl;
572 
573  libMesh::out << "|Z1_old("
574  << system.time + (system.time_solver->last_completed_timestep_size())/((param.timesolver_tolerance) ? 2.0 : 1.0)
575  << ")|= "
576  << system.calculate_norm(system.get_vector(old_adjoint_solution_name1), 0, H1)
577  << std::endl
578  << std::endl;
579 
580  // Write out adjoint solutions for visualization.
581 
582  // Get a pointer to the primal solution vector
583  NumericVector<Number> & primal_solution = *system.solution;
584 
585  // Get a pointer to the solution vector of the adjoint problem for QoI 0
586  NumericVector<Number> & dual_solution_0 = system.get_adjoint_solution(0);
587 
588  // Swap the primal and dual solutions so we can write out the adjoint solution
589  primal_solution.swap(dual_solution_0);
590 
591  write_output(equation_systems, param.n_timesteps, "dual0", param);
592 
593  // Swap back
594  primal_solution.swap(dual_solution_0);
595 
596  // Get a pointer to the solution vector of the adjoint problem for QoI 0
597  NumericVector<Number> & dual_solution_1 = system.get_adjoint_solution(1);
598 
599  // Swap the primal and dual solutions so we can write out the adjoint solution
600  primal_solution.swap(dual_solution_1);
601 
602  write_output(equation_systems, param.n_timesteps, "dual1", param);
603 
604  // Swap back
605  primal_solution.swap(dual_solution_1);
606 
607  // Now that the adjoint initial condition is set, we will start the
608  // backwards in time adjoint integration
609 
610  // For loop stepping backwards in time
611  for (unsigned int t_step=param.initial_timestep;
612  t_step != param.initial_timestep + param.n_timesteps; ++t_step)
613  {
614  //A pretty update message
615  libMesh::out << " Solving adjoint time step "
616  << t_step
617  << ", time = "
618  << system.time
619  << std::endl;
620 
621  // Output the H1 norm of the retrieved primal solution from the last call
622  // to adjoint_advance_timestep
623  libMesh::out << "|U("
624  << system.time
625  << ")|= "
626  << system.calculate_norm(*system.solution, 0, H1)
627  << std::endl;
628 
629  libMesh::out << "|U("
630  << system.time - (system.time_solver->last_completed_timestep_size())/((param.timesolver_tolerance) ? 2.0 : 1.0)
631  << ")|= "
632  << system.calculate_norm(system.get_vector("_old_nonlinear_solution"), 0, H1)
633  << std::endl;
634 
635  system.set_adjoint_already_solved(false);
636 
637  // Temporarily enable the physics we want to be used for the adjoint evaluation
638  system.push_physics(*sigma_physics);
639 
640  system.adjoint_solve();
641 
642  Z0_norm = system.calculate_norm(system.get_adjoint_solution(0), 0, H1);
643  Z1_norm = system.calculate_norm(system.get_adjoint_solution(1), 0, H1);
644 
645  libMesh::out << "|Z0("
646  << system.time
647  << ")|= "
648  << Z0_norm
649  << std::endl
650  << std::endl;
651 
652  libMesh::out << "|Z1("
653  << system.time
654  << ")|= "
655  << Z1_norm
656  << std::endl
657  << std::endl;
658 
659  // Return to the original physics
660  system.pop_physics();
661 
662  // Now that we have solved the adjoint, set the
663  // adjoint_already_solved boolean to true, so we dont solve
664  // unnecessarily in the error estimator
665  system.set_adjoint_already_solved(true);
666 
667  libMesh::out << "Saving adjoint and retrieving primal solutions at time t=" << system.time - system.deltat << std::endl;
668 
669  // The adjoint_advance_timestep function calls the retrieve and store
670  // function of the memory_solution_history class via the
671  // memory_solution_history object we declared earlier. The
672  // retrieve function sets the system primal vectors to their
673  // values at the current time instance.
674  system.time_solver->adjoint_advance_timestep();
675 
676  Z0_old_norm = system.calculate_norm(system.get_vector(old_adjoint_solution_name0), 0, H1);
677  Z1_old_norm = system.calculate_norm(system.get_vector(old_adjoint_solution_name1), 0, H1);
678 
679  // Make sure that we have copied the old adjoint solutions properly
680  libmesh_assert(Z0_norm == Z0_old_norm);
681  libmesh_assert(Z1_norm == Z1_old_norm);
682 
683  libMesh::out << "|Z0_old("
684  << system.time + (system.time_solver->last_completed_timestep_size())/((param.timesolver_tolerance) ? 2.0 : 1.0)
685  << ")|= "
686  << Z0_old_norm
687  << std::endl
688  << std::endl;
689 
690  libMesh::out << "|Z1_old("
691  << system.time + (system.time_solver->last_completed_timestep_size())/((param.timesolver_tolerance) ? 2.0 : 1.0)
692  << ")|= "
693  << Z1_old_norm
694  << std::endl
695  << std::endl;
696 
697  // Get a pointer to the primal solution vector
698  primal_solution = *system.solution;
699 
700  // Get a pointer to the solution vector of the adjoint problem for QoI 0
701  dual_solution_0 = system.get_adjoint_solution(0);
702 
703  // Swap the primal and dual solutions so we can write out the adjoint solution
704  primal_solution.swap(dual_solution_0);
705 
706  write_output(equation_systems, param.n_timesteps - (t_step + 1), "dual0", param);
707 
708  // Swap back
709  primal_solution.swap(dual_solution_0);
710 
711  // Get a pointer to the solution vector of the adjoint problem for QoI 1
712  dual_solution_1 = system.get_adjoint_solution(1);
713 
714  // Swap the primal and dual solutions so we can write out the adjoint solution
715  primal_solution.swap(dual_solution_1);
716 
717  write_output(equation_systems, param.n_timesteps - (t_step + 1), "dual1", param);
718 
719  // Swap back
720  primal_solution.swap(dual_solution_1);
721  }
722  // End adjoint timestep loop
723 
724  // Reset the time before looping over time again
725  system.time = 0.0;
726 
727  // Now that we have computed both the primal and adjoint solutions, we can compute the goal-oriented error estimates.
728  // For this, we will need to build a ARefEE error estimator object, and supply a pointer to the 'true physics' object,
729  // as we did for the adjoint solve. This object will now define the residual for the dual weighted error evaluation at
730  // each timestep.
731  // For adjoint consistency, it is important that we maintain the same integration method for the error estimation integral
732  // as we did for the primal and QoI integration. This is ensured within the library, but is currently ONLY supported for
733  // the Backward-Euler (theta = 0.5) time integration method.
734  QoISet qois;
735 
736  qois.add_indices({0,1});
737  qois.set_weight(0, 0.5);
738  qois.set_weight(1, 0.5);
739 
740  // The adjoint refinement error estimator object (which also computed model error)
741  auto adjoint_refinement_error_estimator =
742  build_adjoint_refinement_error_estimator(qois, dynamic_cast<FEMPhysics *>(sigma_physics.get()), param);
743 
744  // Error Vector to be filled by the error estimator at each timestep
745  ErrorVector QoI_elementwise_error;
746  std::vector<Number> QoI_spatially_integrated_error(system.n_qois());
747 
748  // Error Vector to hold the total accumulated error
749  ErrorVector accumulated_QoI_elementwise_error;
750  std::vector<Number> accumulated_QoI_spatially_integrated_error(system.n_qois());
751 
752  // Retrieve the primal and adjoint solutions at the current timestep
753  system.time_solver->retrieve_timestep();
754 
755  // A pretty update message
756  libMesh::out << "Retrieved, "
757  << "time = "
758  << system.time
759  << std::endl;
760 
761  libMesh::out << "|U("
762  << system.time
763  << ")|= "
764  << system.calculate_norm(*system.solution, 0, H1)
765  << std::endl;
766 
767  libMesh::out << "|U_old("
768  << system.time
769  << ")|= "
770  << system.calculate_norm(system.get_vector("_old_nonlinear_solution"), 0, H1)
771  << std::endl;
772 
773  libMesh::out << "|Z0("
774  << system.time
775  << ")|= "
776  << system.calculate_norm(system.get_adjoint_solution(0), 0, H1)
777  << std::endl
778  << std::endl;
779 
780  libMesh::out << "|Z1("
781  << system.time
782  << ")|= "
783  << system.calculate_norm(system.get_adjoint_solution(1), 0, H1)
784  << std::endl
785  << std::endl;
786 
787  Z0_old_norm = system.calculate_norm(system.get_vector(old_adjoint_solution_name0), 0, H1);
788  Z1_old_norm = system.calculate_norm(system.get_vector(old_adjoint_solution_name1), 0, H1);
789 
790  // Assert that the old adjoint values match hard coded numbers for 1st timestep or 1st half time step from the adjoint solve
791  if(param.timesolver_tolerance)
792  {
793  libmesh_error_msg_if(std::abs(Z0_old_norm - (4.3523242593920151e-06)) >= 2.e-10,
794  "Mismatch in expected Z0_old norm for the 1st half timestep!");
795  libmesh_error_msg_if(std::abs(Z1_old_norm - (0.3118758855352407)) >= 2.e-5,
796  "Mismatch in expected Z1_old norm for the 1st half timestep!");
797  }
798  else
799  {
800  libmesh_error_msg_if(std::abs(Z0_old_norm - (0.0030758523361801263)) >= 2.e-7,
801  "Mismatch in expected Z0_old norm for the 1st timestep!");
802  libmesh_error_msg_if(std::abs(Z1_old_norm - (0.31162043809579365)) >= 2.e-5,
803  "Mismatch in expected Z1_old norm for the 1st timestep!");
804  }
805 
806  libMesh::out << "|Z0_old("
807  << system.time + (system.time_solver->last_completed_timestep_size())/((param.timesolver_tolerance) ? 2.0 : 1.0)
808  << ")|= "
809  << system.calculate_norm(system.get_vector(old_adjoint_solution_name0), 0, H1)
810  << std::endl
811  << std::endl;
812 
813  libMesh::out << "|Z1_old("
814  << system.time + (system.time_solver->last_completed_timestep_size())/((param.timesolver_tolerance) ? 2.0 : 1.0)
815  << ")|= "
816  << system.calculate_norm(system.get_vector(old_adjoint_solution_name1), 0, H1)
817  << std::endl
818  << std::endl;
819 
820  // Now we begin the timestep loop to compute the time-accurate
821  // adjoint sensitivities
822  for (unsigned int t_step=param.initial_timestep;
823  t_step != param.initial_timestep + param.n_timesteps; ++t_step)
824  {
825  system.time_solver->integrate_adjoint_refinement_error_estimate(*adjoint_refinement_error_estimator, QoI_elementwise_error);
826 
827  // A pretty update message
828  libMesh::out << "Retrieved, "
829  << "time = "
830  << system.time
831  << std::endl;
832 
833  libMesh::out << "|U("
834  << system.time
835  << ")|= "
836  << system.calculate_norm(*system.solution, 0, H1)
837  << std::endl;
838 
839  libMesh::out << "|U_old("
840  << system.time
841  << ")|= "
842  << system.calculate_norm(system.get_vector("_old_nonlinear_solution"), 0, H1)
843  << std::endl;
844 
845  libMesh::out << "|Z0("
846  << system.time
847  << ")|= "
848  << system.calculate_norm(system.get_adjoint_solution(0), 0, H1)
849  << std::endl
850  << std::endl;
851 
852  libMesh::out << "|Z1("
853  << system.time
854  << ")|= "
855  << system.calculate_norm(system.get_adjoint_solution(1), 0, H1)
856  << std::endl
857  << std::endl;
858 
859  libMesh::out << "|Z0_old("
860  << system.time + (system.time_solver->last_completed_timestep_size())/((param.timesolver_tolerance) ? 2.0 : 1.0)
861  << ")|= "
862  << system.calculate_norm(system.get_vector(old_adjoint_solution_name0), 0, H1)
863  << std::endl
864  << std::endl;
865 
866  libMesh::out << "|Z1_old("
867  << system.time + (system.time_solver->last_completed_timestep_size())/((param.timesolver_tolerance) ? 2.0 : 1.0)
868  << ")|= "
869  << system.calculate_norm(system.get_vector(old_adjoint_solution_name1), 0, H1)
870  << std::endl
871  << std::endl;
872 
873  // Error contribution from this timestep
874  for(unsigned int i = 0; i < QoI_elementwise_error.size(); i++)
875  accumulated_QoI_elementwise_error[i] += QoI_elementwise_error[i];
876 
877  // QoI wise error contribution from this timestep
878  for (auto j : make_range(system.n_qois()))
879  {
880  // Skip this QoI if not in the QoI Set
881  if ((adjoint_refinement_error_estimator->qoi_set()).has_index(j))
882  {
883  accumulated_QoI_spatially_integrated_error[j] += system.get_qoi_error_estimate_value(j);
884  }
885  }
886  }
887 
888  std::cout<<"Time integrated error estimate for QoI 0: "<<std::setprecision(17)<<accumulated_QoI_spatially_integrated_error[0]<<std::endl;
889  std::cout<<"Time integrated error estimate for QoI 1: "<<std::setprecision(17)<<accumulated_QoI_spatially_integrated_error[1]<<std::endl;
890 
891  // Hard coded test to ensure that the actual numbers we are
892  // getting are what they should be
893  // The 2e-4 tolerance is chosen to ensure success even with
894  // 32-bit floats
895  if(param.timesolver_tolerance)
896  {
897  libmesh_error_msg_if(std::abs(system.time - (1.7548735069535084)) >= 2.e-4,
898  "Mismatch in end time reached by adaptive timestepper!");
899 
900  libmesh_error_msg_if(std::abs(accumulated_QoI_spatially_integrated_error[0] - (-0.75139371165754232)) >= 2.e-4,
901  "Error Estimator identity not satisfied!");
902 
903  libmesh_error_msg_if(std::abs(accumulated_QoI_spatially_integrated_error[1] - (-0.70905030091469889)) >= 2.e-4,
904  "Error Estimator identity not satisfied!");
905  }
906  else
907  {
908  libmesh_error_msg_if(std::abs(accumulated_QoI_spatially_integrated_error[0] - (-0.44809323880671958)) >= 2.e-4,
909  "Error Estimator identity not satisfied!");
910 
911  libmesh_error_msg_if(std::abs(accumulated_QoI_spatially_integrated_error[1] - (-0.23895256319278346)) >= 2.e-4,
912  "Error Estimator identity not satisfied!");
913  }
914  }
915 #if defined(NDEBUG) && defined(LIBMESH_ENABLE_EXCEPTIONS)
916  catch (...)
917  {
918  libMesh::err << '[' << mesh.processor_id()
919  << "] Caught exception; exiting early." << std::endl;
920  }
921 #endif
922 
923  libMesh::err << '[' << mesh.processor_id()
924  << "] Completing output."
925  << std::endl;
926 
927  // All done.
928  return 0;
929 
930 #endif // LIBMESH_ENABLE_AMR
931 }
OStreamProxy err
libMesh::Real timesolver_upper_tolerance
Definition: femparameters.h:39
Real time
For time-dependent problems, this is the time t at the beginning of the current timestep.
Definition: system.h:1615
bool print_solution_norms
ElemType
Defines an enum for geometric element types.
libMesh::Real deltat
Definition: femparameters.h:39
bool print_element_residuals
std::string solution_history_type
Definition: femparameters.h:38
Real verify_analytic_jacobians
If verify_analytic_jacobian is equal to zero (as it is by default), no numeric jacobians will be calc...
Definition: fem_system.h:215
Number adjoint_initial_value0(const Point &, const Parameters &, const std::string &, const std::string &)
libMesh::Real timesolver_tolerance
Definition: femparameters.h:39
void set_adjoint_already_solved(bool setting)
Setter for the adjoint_already_solved boolean.
Definition: system.h:415
bool analytic_jacobians
void write_output(EquationSystems &es, unsigned int t_step, std::string solution_type, FEMParameters &param)
Definition: adjoints_ex7.C:87
virtual void set_constrain_in_solver(bool enable)
set_constrain_in_solver to false to apply constraints only via residual terms in the systems to be so...
Definition: diff_system.C:424
Number get_qoi_value(unsigned int qoi_index) const
Definition: system.C:2386
bool print_jacobian_norms
Set print_jacobian_norms to true to print |J| whenever it is assembled.
Definition: diff_system.h:361
int main(int argc, char **argv)
Definition: adjoints_ex7.C:323
unsigned int n_qois() const
Number of currently active quantities of interest.
Definition: system.h:2621
int extra_quadrature_order
A member int that can be employed to indicate increased or reduced quadrature order.
Definition: system.h:1578
Real numerical_jacobian_h
If calculating numeric jacobians is required, the FEMSystem will perturb each solution vector entry b...
Definition: fem_system.h:187
std::string timesolver_core
Definition: femparameters.h:37
bool require_residual_reduction
std::unique_ptr< TimeSolver > time_solver
A pointer to the solver object we&#39;re going to use.
Definition: diff_system.h:253
MeshBase & mesh
bool print_residual_norms
void init_qois(unsigned int n_qois)
Accessors for qoi and qoi_error_estimates vectors.
Definition: system.C:2371
libMesh::Real timesolver_maxgrowth
Definition: femparameters.h:39
Gradient adjoint_initial_grad0(const Point &, const Parameters &, const std::string &, const std::string &)
libMesh::Real relative_step_tolerance
void build_square(UnstructuredMesh &mesh, const unsigned int nx, const unsigned int ny, const Real xmin=0., const Real xmax=1., const Real ymin=0., const Real ymax=1., const ElemType type=INVALID_ELEM, const bool gauss_lobatto_grid=false)
A specialized build_cube() for 2D meshes.
bool print_jacobians
Set print_jacobians to true to print J whenever it is assembled.
Definition: diff_system.h:366
void pop_physics()
Pop a physics object off of our stack.
Definition: diff_system.C:411
bool print_element_residuals
Set print_element_residuals to true to print each R_elem contribution.
Definition: diff_system.h:376
SolverPackage default_solver_package()
Definition: libmesh.C:1117
void project_solution(FunctionBase< Number > *f, FunctionBase< Gradient > *g=nullptr) const
Projects arbitrary functions onto the current solution.
bool print_solution_norms
Set print_residual_norms to true to print |U| whenever it is used in an assembly() call...
Definition: diff_system.h:340
void libmesh_ignore(const Args &...)
unsigned int max_linear_iterations
int8_t boundary_id_type
Definition: id_types.h:51
bool constrain_in_solver
bool print_residual_norms
Set print_residual_norms to true to print |F| whenever it is assembled.
Definition: diff_system.h:351
double minimum_linear_tolerance
Real deltat
For time-dependent problems, this is the amount delta t to advance the solution in time...
Definition: diff_system.h:280
libMesh::Real verify_analytic_jacobians
std::unique_ptr< NumericVector< Number > > solution
Data structure to hold solution values.
Definition: system.h:1593
Real calculate_norm(const NumericVector< Number > &v, unsigned int var, FEMNormType norm_type, std::set< unsigned int > *skip_dimensions=nullptr) const
Definition: system.C:1724
bool print_residuals
Set print_residuals to true to print F whenever it is assembled.
Definition: diff_system.h:356
void init(triangulateio &t)
Initializes the fields of t to nullptr/0 as necessary.
libmesh_assert(ctx)
double initial_linear_tolerance
void set_system_parameters(HeatSystem &system, FEMParameters &param)
Definition: adjoints_ex7.C:144
int extra_quadrature_order
void read(GetPot &input, const std::vector< std::string > *other_variable_names=nullptr)
bool print_solutions
Set print_solutions to true to print U whenever it is used in an assembly() call. ...
Definition: diff_system.h:346
libMesh::Real relative_residual_tolerance
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
void set_vector_preservation(const std::string &vec_name, bool preserve)
Allows one to set the boolean controlling whether the vector identified by vec_name should be "preser...
Definition: system.C:1138
bool print_jacobian_norms
virtual void swap(NumericVector< T > &v)
Swaps the contents of this with v.
OStreamProxy out
libMesh::Real min_step_length
libMesh::Real timesolver_theta
Definition: femparameters.h:39
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition: int_range.h:140
Gradient initial_grad(const Point &, const Parameters &, const std::string &, const std::string &)
Definition: initial.C:30
NumericVector< Number > & get_adjoint_solution(unsigned int i=0)
Definition: system.C:1245
bool print_element_jacobians
Set print_element_jacobians to true to print each J_elem contribution.
Definition: diff_system.h:381
bool print_element_jacobians
unsigned int max_nonlinear_iterations
double linear_tolerance_multiplier
void add_dirichlet_boundary(const DirichletBoundary &dirichlet_boundary)
Adds a copy of the specified Dirichlet boundary to the system.
std::map< libMesh::boundary_id_type, std::vector< unsigned int > > dirichlet_condition_variables
Definition: femparameters.h:94
std::unique_ptr< AdjointRefinementEstimator > build_adjoint_refinement_error_estimator(QoISet &qois, FEMPhysics *supplied_physics, FEMParameters &)
Definition: adjoints_ex7.C:299
void project_vector(NumericVector< Number > &new_vector, FunctionBase< Number > *f, FunctionBase< Gradient > *g=nullptr, int is_adjoint=-1) const
Projects arbitrary functions onto a vector of degree of freedom values for the current system...
virtual void solve() override
Invokes the solver associated with the system.
Definition: fem_system.C:1076
void push_physics(DifferentiablePhysics &new_physics)
Push a clone of a new physics object onto our stack, overriding the current physics until the new phy...
Definition: diff_system.C:399
bool time_solver_quiet
Number get_qoi_error_estimate_value(unsigned int qoi_index) const
Definition: system.C:2413
bool & analytic_jacobians()
Definition: heatsystem.h:49
libMesh::Real numerical_jacobian_h
std::map< libMesh::boundary_id_type, libMesh::FunctionBase< libMesh::Number > * > dirichlet_conditions
Definition: femparameters.h:91
const DofMap & get_dof_map() const
Definition: system.h:2374
std::vector< libMesh::FEMNormType > timesolver_norm
Definition: femparameters.h:42
template class LIBMESH_EXPORT NumericVector< Number >
Number initial_value(const Point &, const Parameters &, const std::string &, const std::string &)
Definition: initial.C:18
virtual std::pair< unsigned int, Real > adjoint_solve(const QoISet &qoi_indices=QoISet()) override
This function sets the _is_adjoint boolean member of TimeSolver to true and then calls the adjoint_so...
Definition: diff_system.C:150
const NumericVector< Number > & get_vector(std::string_view vec_name) const
Definition: system.C:943
unsigned int deltat_reductions
Definition: femparameters.h:36