libMesh
Loading...
Searching...
No Matches
adjoints_ex7.C
Go to the documentation of this file.
1// The libMesh Finite Element Library.
2// Copyright (C) 2002-2026 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
87void 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
298std::unique_ptr<AdjointRefinementEstimator>
299build_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.
323int 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}
Number initial_value(const Point &, const Parameters &, const std::string &, const std::string &)
Definition initial.C:18
Gradient initial_grad(const Point &, const Parameters &, const std::string &, const std::string &)
Definition initial.C:30
Number adjoint_initial_value0(const Point &, const Parameters &, const std::string &, const std::string &)
Gradient adjoint_initial_grad0(const Point &, const Parameters &, const std::string &, const std::string &)
void write_output(EquationSystems &es, unsigned int t_step, std::string solution_type, FEMParameters &param)
std::unique_ptr< AdjointRefinementEstimator > build_adjoint_refinement_error_estimator(QoISet &qois, FEMPhysics *supplied_physics, FEMParameters &)
void set_system_parameters(HeatSystem &system, FEMParameters &param)
std::string elementtype
std::map< libMesh::boundary_id_type, libMesh::FunctionBase< libMesh::Number > * > dirichlet_conditions
double minimum_linear_tolerance
double initial_linear_tolerance
unsigned int coarsegridx
libMesh::Real timesolver_theta
double linear_tolerance_multiplier
libMesh::Real deltat
libMesh::Real timesolver_tolerance
libMesh::Real domain_xmin
bool print_element_jacobians
std::map< libMesh::boundary_id_type, std::vector< unsigned int > > dirichlet_condition_variables
libMesh::Real verify_analytic_jacobians
unsigned int max_nonlinear_iterations
libMesh::Real timesolver_maxgrowth
libMesh::Real domain_ymin
bool print_element_residuals
libMesh::Real min_step_length
libMesh::Real timesolver_upper_tolerance
std::string timesolver_core
libMesh::Real numerical_jacobian_h
unsigned int initial_timestep
unsigned int dimension
libMesh::Real relative_residual_tolerance
bool print_residual_norms
bool print_jacobian_norms
std::string solution_history_type
unsigned int n_timesteps
std::vector< libMesh::FEMNormType > timesolver_norm
void read(GetPot &input, const std::vector< std::string > *other_variable_names=nullptr)
libMesh::Real domain_edge_length
libMesh::Real relative_step_tolerance
unsigned int max_linear_iterations
bool require_residual_reduction
unsigned int extrarefinements
unsigned int coarsegridy
bool print_solution_norms
unsigned int deltat_reductions
libMesh::Real domain_edge_width
bool & analytic_jacobians()
Definition heatsystem.h:49
bool print_jacobians
Set print_jacobians to true to print J whenever it is assembled.
bool print_residuals
Set print_residuals to true to print F whenever it is assembled.
void pop_physics()
Pop a physics object off of our stack.
Real deltat
For time-dependent problems, this is the amount delta t to advance the solution in time.
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...
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...
bool print_element_jacobians
Set print_element_jacobians to true to print each J_elem contribution.
bool print_solution_norms
Set print_residual_norms to true to print |U| whenever it is used in an assembly() call.
bool print_solutions
Set print_solutions to true to print U whenever it is used in an assembly() call.
bool print_residual_norms
Set print_residual_norms to true to print |F| whenever it is assembled.
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...
bool print_element_residuals
Set print_element_residuals to true to print each R_elem contribution.
bool print_jacobian_norms
Set print_jacobian_norms to true to print |J| whenever it is assembled.
std::unique_ptr< TimeSolver > time_solver
A pointer to the solver object we're going to use.
void add_dirichlet_boundary(const DirichletBoundary &dirichlet_boundary)
Adds a copy of the specified Dirichlet boundary to the system.
virtual void solve() override
Invokes the solver associated with the system.
Real numerical_jacobian_h
If calculating numeric jacobians is required, the FEMSystem will perturb each solution vector entry b...
Definition fem_system.h:187
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
virtual void swap(NumericVector< T > &v)
Swaps the contents of this with v.
int extra_quadrature_order
A member int that can be employed to indicate increased or reduced quadrature order.
Definition system.h:1640
void project_vector(NumericVector< Number > &new_vector, FunctionBase< Number > *f, FunctionBase< Gradient > *g=nullptr, int is_adjoint=-1, std::optional< ConstElemRange > active_local_range=std::nullopt, std::optional< std::vector< unsigned int > > variable_numbers=std::nullopt) const
Projects arbitrary functions onto a vector of degree of freedom values for the current system.
void project_solution(FunctionBase< Number > *f, FunctionBase< Gradient > *g=nullptr, std::optional< ConstElemRange > active_local_range=std::nullopt, std::optional< std::vector< unsigned int > > variable_numbers=std::nullopt) const
Projects arbitrary functions onto the current solution.
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:1125
Number get_qoi_value(unsigned int qoi_index) const
Definition system.C:2184
void init_qois(unsigned int n_qois)
Accessors for qoi and qoi_error_estimates vectors.
Definition system.C:2169
void set_adjoint_already_solved(bool setting)
Setter for the adjoint_already_solved boolean.
Definition system.h:417
Real time
For time-dependent problems, this is the time t at the beginning of the current timestep.
Definition system.h:1677
std::unique_ptr< NumericVector< Number > > solution
Data structure to hold solution values.
Definition system.h:1655
unsigned int n_qois() const
Number of currently active quantities of interest.
Definition system.h:2562
Real calculate_norm(const NumericVector< Number > &v, unsigned int var, FEMNormType norm_type, std::set< unsigned int > *skip_dimensions=nullptr) const
Definition system.C:1511
Number get_qoi_error_estimate_value(unsigned int qoi_index) const
Definition system.C:2211
const DofMap & get_dof_map() const
Definition system.h:2417
NumericVector< Number > & get_adjoint_solution(unsigned int i=0)
Definition system.C:1232
const NumericVector< Number > & get_vector(std::string_view vec_name) const
Definition system.C:931
static const Real b
MeshBase & mesh
OStreamProxy err
SolverPackage default_solver_package()
Definition libmesh.C:1064
OStreamProxy out
int main()