// The main program. int main (int argc, char** argv) { // Skip adaptive examples on a non-adaptive libMesh build #ifndef LIBMESH_ENABLE_AMR libmesh_example_assert(false, "--enable-amr"); #else // Skip this 2D example if libMesh was compiled as 1D-only. libmesh_example_assert(2 <= LIBMESH_DIM, "2D support"); // Initialize libMesh. LibMeshInit init (argc, argv); std::cout << "Started " << argv[0] << std::endl; // Make sure the general input file exists, and parse it { std::ifstream i("general.in"); if (!i) { std::cerr << '[' << init.comm().rank() << "] Can't find general.in; exiting early." << std::endl; libmesh_error(); } } GetPot infile("general.in"); // Read in parameters from the input file FEMParameters param; param.read(infile); // Create a mesh with the given dimension, distributed // across the default MPI communicator. Mesh mesh(init.comm(), param.dimension); // And an object to refine it AutoPtr<MeshRefinement> mesh_refinement(new MeshRefinement(mesh)); // And an EquationSystems to run on it EquationSystems equation_systems (mesh); std::cout << "Building mesh" << std::endl; // Build a unit square ElemType elemtype; if (param.elementtype == "tri" || param.elementtype == "unstructured") elemtype = TRI3; else elemtype = QUAD4; MeshTools::Generation::build_square (mesh, param.coarsegridx, param.coarsegridy, param.domain_xmin, param.domain_xmin + param.domain_edge_width, param.domain_ymin, param.domain_ymin + param.domain_edge_length, elemtype); std::cout << "Building system" << std::endl; HeatSystem &system = equation_systems.add_system<HeatSystem> ("HeatSystem"); set_system_parameters(system, param); std::cout << "Initializing systems" << std::endl; // Initialize the system equation_systems.init (); // Refine the grid again if requested for (unsigned int i=0; i != param.extrarefinements; ++i) { mesh_refinement->uniformly_refine(1); equation_systems.reinit(); } std::cout<<"Setting primal initial conditions"<<std::endl; read_initial_parameters(); system.project_solution(initial_value, initial_grad, equation_systems.parameters); // Output the H1 norm of the initial conditions libMesh::out << "|U(" <<system.time<< ")|= " << system.calculate_norm(*system.solution, 0, H1) << std::endl<<std::endl; // Add an adjoint vector, this will be computed after the forward // time stepping is complete // // Tell the library not to save adjoint solutions during the forward // solve // // Tell the library not to project this vector, and hence, memory // solution history to not save it. // // Make this vector ghosted so we can localize it to each element // later. const std::string & adjoint_solution_name = "adjoint_solution0"; system.add_vector("adjoint_solution0", false, GHOSTED); // Close up any resources initial.C needed finish_initialization(); // Plot the initial conditions write_output(equation_systems, 0, "primal"); // Print information about the mesh and system to the screen. mesh.print_info(); equation_systems.print_info(); // In optimized mode we catch any solver errors, so that we can // write the proper footers before closing. In debug mode we just // let the exception throw so that gdb can grab it. #ifdef NDEBUG try { #endif // Now we begin the timestep loop to compute the time-accurate // solution of the equations. for (unsigned int t_step=param.initial_timestep; t_step != param.initial_timestep + param.n_timesteps; ++t_step) { // A pretty update message std::cout << " Solving time step " << t_step << ", time = " << system.time << std::endl; // Solve the forward problem at time t, to obtain the solution at time t + dt system.solve(); // Output the H1 norm of the computed solution libMesh::out << "|U(" <<system.time + system.deltat<< ")|= " << system.calculate_norm(*system.solution, 0, H1) << std::endl; // Advance to the next timestep in a transient problem std::cout<<"Advancing timestep"<<std::endl<<std::endl; system.time_solver->advance_timestep(); // Write out this timestep write_output(equation_systems, t_step+1, "primal"); } // End timestep loop ///////////////// Now for the Adjoint Solution ////////////////////////////////////// // Now we will solve the backwards in time adjoint problem std::cout << std::endl << "Solving the adjoint problem" << std::endl; // We need to tell the library that it needs to project the adjoint, so // MemorySolutionHistory knows it has to save it // Tell the library to project the adjoint vector, and hence, memory solution history to // save it system.set_vector_preservation(adjoint_solution_name, true); std::cout << "Setting adjoint initial conditions Z("<<system.time<<")"<<std::endl; // Need to call adjoint_advance_timestep once for the initial condition setup std::cout<<"Retrieving solutions at time t="<<system.time<<std::endl; system.time_solver->adjoint_advance_timestep(); // Output the H1 norm of the retrieved solutions (u^i and u^i+1) libMesh::out << "|U(" <<system.time + system.deltat<< ")|= " << system.calculate_norm(*system.solution, 0, H1) << std::endl; libMesh::out << "|U(" <<system.time<< ")|= " << system.calculate_norm(system.get_vector("_old_nonlinear_solution"), 0, H1) << std::endl; // The first thing we have to do is to apply the adjoint initial // condition. The user should supply these. Here they are specified // in the functions adjoint_initial_value and adjoint_initial_gradient system.project_vector(adjoint_initial_value, adjoint_initial_grad, equation_systems.parameters, system.get_adjoint_solution(0)); // Since we have specified an adjoint solution for the current time (T), set the adjoint_already_solved boolean to true, so we dont solve unneccesarily in the adjoint sensitivity method system.set_adjoint_already_solved(true); libMesh::out << "|Z(" <<system.time<< ")|= " << system.calculate_norm(system.get_adjoint_solution(), 0, H1) << std::endl<<std::endl; write_output(equation_systems, param.n_timesteps, "dual"); // Now that the adjoint initial condition is set, we will start the // backwards in time adjoint integration // For loop stepping backwards in time for (unsigned int t_step=param.initial_timestep; t_step != param.initial_timestep + param.n_timesteps; ++t_step) { //A pretty update message std::cout << " Solving adjoint time step " << t_step << ", time = " << system.time << std::endl; // The adjoint_advance_timestep // function calls the retrieve function of the memory_solution_history // class via the memory_solution_history object we declared earlier. // The retrieve function sets the system primal vectors to their values // at the current timestep std::cout<<"Retrieving solutions at time t="<<system.time<<std::endl; system.time_solver->adjoint_advance_timestep(); // Output the H1 norm of the retrieved solution libMesh::out << "|U(" <<system.time + system.deltat << ")|= " << system.calculate_norm(*system.solution, 0, H1) << std::endl; libMesh::out << "|U(" <<system.time<< ")|= " << system.calculate_norm(system.get_vector("_old_nonlinear_solution"), 0, H1) << std::endl; system.set_adjoint_already_solved(false); system.adjoint_solve(); // Now that we have solved the adjoint, set the adjoint_already_solved boolean to true, so we dont solve unneccesarily in the error estimator system.set_adjoint_already_solved(true); libMesh::out << "|Z(" <<system.time<< ")|= "<< system.calculate_norm(system.get_adjoint_solution(), 0, H1) << std::endl << std::endl; // Get a pointer to the primal solution vector NumericVector<Number> &primal_solution = *system.solution; // Get a pointer to the solution vector of the adjoint problem for QoI 0 NumericVector<Number> &dual_solution_0 = system.get_adjoint_solution(0); // Swap the primal and dual solutions so we can write out the adjoint solution primal_solution.swap(dual_solution_0); write_output(equation_systems, param.n_timesteps - (t_step + 1), "dual"); // Swap back primal_solution.swap(dual_solution_0); } // End adjoint timestep loop // Now that we have computed both the primal and adjoint solutions, we compute the sensitivties to the parameter p // dQ/dp = partialQ/partialp - partialR/partialp // partialQ/partialp = (Q(p+dp) - Q(p-dp))/(2*dp), this is not supported by the library yet // partialR/partialp = (R(u,z;p+dp) - R(u,z;p-dp))/(2*dp), where // R(u,z;p+dp) = int_{0}^{T} f(z;p+dp) - <partialu/partialt, z>(p+dp) - <g(u),z>(p+dp) // To do this we need to step forward in time, and compute the perturbed R at each time step and accumulate it // Then once all time steps are over, we can compute (R(u,z;p+dp) - R(u,z;p-dp))/(2*dp) // Now we begin the timestep loop to compute the time-accurate // adjoint sensitivities for (unsigned int t_step=param.initial_timestep; t_step != param.initial_timestep + param.n_timesteps; ++t_step) { // A pretty update message std::cout << "Retrieving " << t_step << ", time = " << system.time << std::endl; // Retrieve the primal and adjoint solutions at the current timestep system.time_solver->retrieve_timestep(); libMesh::out << "|U(" <<system.time + system.deltat << ")|= " << system.calculate_norm(*system.solution, 0, H1) << std::endl; libMesh::out << "|U(" <<system.time<< ")|= " << system.calculate_norm(system.get_vector("_old_nonlinear_solution"), 0, H1) << std::endl; libMesh::out << "|Z(" <<system.time<< ")|= "<< system.calculate_norm(system.get_adjoint_solution(0), 0, H1) << std::endl << std::endl; // Call the postprocess function which we have overloaded to compute // accumulate the perturbed residuals (dynamic_cast<HeatSystem&>(system)).perturb_accumulate_residuals(dynamic_cast<HeatSystem&>(system).get_parameter_vector()); // Move the system time forward (retrieve_timestep does not do this) system.time += system.deltat; } // A pretty update message std::cout << "Retrieving " << " final time = " << system.time << std::endl; // Retrieve the primal and adjoint solutions at the current timestep system.time_solver->retrieve_timestep(); libMesh::out << "|U(" <<system.time + system.deltat << ")|= " << system.calculate_norm(*system.solution, 0, H1) << std::endl; libMesh::out << "|U(" <<system.time<< ")|= " << system.calculate_norm(system.get_vector("_old_nonlinear_solution"), 0, H1) << std::endl; libMesh::out << "|Z(" <<system.time<< ")|= "<< system.calculate_norm(system.get_adjoint_solution(0), 0, H1) << std::endl<<std::endl; // Call the postprocess function which we have overloaded to compute // accumulate the perturbed residuals (dynamic_cast<HeatSystem&>(system)).perturb_accumulate_residuals(dynamic_cast<HeatSystem&>(system).get_parameter_vector()); // Now that we computed the accumulated, perturbed residuals, we can compute the // approximate sensitivity Number sensitivity_0_0 = (dynamic_cast<HeatSystem&>(system)).compute_final_sensitivity(); // Print it out std::cout<<"Sensitivity of QoI 0 w.r.t parameter 0 is: " << sensitivity_0_0 << std::endl; #ifdef NDEBUG } catch (...) { std::cerr << '[' << mesh.processor_id() << "] Caught exception; exiting early." << std::endl; } #endif std::cerr << '[' << mesh.processor_id() << "] Completing output." << std::endl; // All done. return 0; #endif // LIBMESH_ENABLE_AMR }
// The main program. int main (int argc, char** argv) { // Initialize libMesh. LibMeshInit init (argc, argv); // Skip adaptive examples on a non-adaptive libMesh build #ifndef LIBMESH_ENABLE_AMR libmesh_example_assert(false, "--enable-amr"); #else std::cout << "Started " << argv[0] << std::endl; // Make sure the general input file exists, and parse it { std::ifstream i("general.in"); if (!i) { std::cerr << '[' << libMesh::processor_id() << "] Can't find general.in; exiting early." << std::endl; libmesh_error(); } } GetPot infile("general.in"); // Read in parameters from the input file FEMParameters param; param.read(infile); // Skip this default-2D example if libMesh was compiled as 1D-only. libmesh_example_assert(2 <= LIBMESH_DIM, "2D support"); // Create a mesh. Mesh mesh; // And an object to refine it AutoPtr<MeshRefinement> mesh_refinement = build_mesh_refinement(mesh, param); // And an EquationSystems to run on it EquationSystems equation_systems (mesh); std::cout << "Reading in and building the mesh" << std::endl; // Read in the mesh mesh.read(param.domainfile.c_str()); // Make all the elements of the mesh second order so we can compute // with a higher order basis mesh.all_second_order(); // Create a mesh refinement object to do the initial uniform refinements // on the coarse grid read in from lshaped.xda MeshRefinement initial_uniform_refinements(mesh); initial_uniform_refinements.uniformly_refine(param.coarserefinements); std::cout << "Building system" << std::endl; // Build the FEMSystem LaplaceSystem &system = equation_systems.add_system<LaplaceSystem> ("LaplaceSystem"); // Set its parameters set_system_parameters(system, param); std::cout << "Initializing systems" << std::endl; equation_systems.init (); // Print information about the mesh and system to the screen. mesh.print_info(); equation_systems.print_info(); LinearSolver<Number> *linear_solver = system.get_linear_solver(); { // Adaptively solve the timestep unsigned int a_step = 0; for (; a_step != param.max_adaptivesteps; ++a_step) { // We can't adapt to both a tolerance and a // target mesh size if (param.global_tolerance != 0.) libmesh_assert_equal_to (param.nelem_target, 0); // If we aren't adapting to a tolerance we need a // target mesh size else libmesh_assert_greater (param.nelem_target, 0); linear_solver->reuse_preconditioner(false); // Solve the forward problem system.solve(); // Write out the computed primal solution write_output(equation_systems, a_step, "primal"); // Get a pointer to the primal solution vector NumericVector<Number> &primal_solution = *system.solution; // Declare a QoISet object, we need this object to set weights for our QoI error contributions QoISet qois; // Declare a qoi_indices vector, each index will correspond to a QoI std::vector<unsigned int> qoi_indices; qoi_indices.push_back(0); qoi_indices.push_back(1); qois.add_indices(qoi_indices); // Set weights for each index, these will weight the contribution of each QoI in the final error // estimate to be used for flagging elements for refinement qois.set_weight(0, 0.5); qois.set_weight(1, 0.5); // Make sure we get the contributions to the adjoint RHS from the sides system.assemble_qoi_sides = true; // We are about to solve the adjoint system, but before we do this we see the same preconditioner // flag to reuse the preconditioner from the forward solver linear_solver->reuse_preconditioner(param.reuse_preconditioner); // Solve the adjoint system. This takes the transpose of the stiffness matrix and then // solves the resulting system system.adjoint_solve(); // Now that we have solved the adjoint, set the adjoint_already_solved boolean to true, so we dont solve unneccesarily in the error estimator system.set_adjoint_already_solved(true); // Get a pointer to the solution vector of the adjoint problem for QoI 0 NumericVector<Number> &dual_solution_0 = system.get_adjoint_solution(0); // Swap the primal and dual solutions so we can write out the adjoint solution primal_solution.swap(dual_solution_0); write_output(equation_systems, a_step, "adjoint_0"); // Swap back primal_solution.swap(dual_solution_0); // Get a pointer to the solution vector of the adjoint problem for QoI 0 NumericVector<Number> &dual_solution_1 = system.get_adjoint_solution(1); // Swap again primal_solution.swap(dual_solution_1); write_output(equation_systems, a_step, "adjoint_1"); // Swap back again primal_solution.swap(dual_solution_1); std::cout << "Adaptive step " << a_step << ", we have " << mesh.n_active_elem() << " active elements and " << equation_systems.n_active_dofs() << " active dofs." << std::endl ; // Postprocess, compute the approximate QoIs and write them out to the console std::cout << "Postprocessing: " << std::endl; system.postprocess_sides = true; system.postprocess(); Number QoI_0_computed = system.get_QoI_value("computed", 0); Number QoI_0_exact = system.get_QoI_value("exact", 0); Number QoI_1_computed = system.get_QoI_value("computed", 1); Number QoI_1_exact = system.get_QoI_value("exact", 1); std::cout<< "The relative error in QoI 0 is " << std::setprecision(17) << std::abs(QoI_0_computed - QoI_0_exact) / std::abs(QoI_0_exact) << std::endl; std::cout<< "The relative error in QoI 1 is " << std::setprecision(17) << std::abs(QoI_1_computed - QoI_1_exact) / std::abs(QoI_1_exact) << std::endl << std::endl; // We will declare an error vector for passing to the adjoint refinement error estimator ErrorVector QoI_elementwise_error; // Build an adjoint refinement error estimator object AutoPtr<AdjointRefinementEstimator> adjoint_refinement_error_estimator = build_adjoint_refinement_error_estimator(qois); // Estimate the error in each element using the Adjoint Refinement estimator adjoint_refinement_error_estimator->estimate_error(system, QoI_elementwise_error); // Print out the computed error estimate, note that we access the global error estimates // using an accessor function, right now sum(QoI_elementwise_error) != global_QoI_error_estimate std::cout<< "The computed relative error in QoI 0 is " << std::setprecision(17) << std::abs(adjoint_refinement_error_estimator->get_global_QoI_error_estimate(0)) / std::abs(QoI_0_exact) << std::endl; std::cout<< "The computed relative error in QoI 1 is " << std::setprecision(17) << std::abs(adjoint_refinement_error_estimator->get_global_QoI_error_estimate(1)) / std::abs(QoI_1_exact) << std::endl << std::endl; // Also print out effecitivity indices (estimated error/true error) std::cout<< "The effectivity index for the computed error in QoI 0 is " << std::setprecision(17) << std::abs(adjoint_refinement_error_estimator->get_global_QoI_error_estimate(0)) / std::abs(QoI_0_computed - QoI_0_exact) << std::endl; std::cout<< "The effectivity index for the computed error in QoI 1 is " << std::setprecision(17) << std::abs(adjoint_refinement_error_estimator->get_global_QoI_error_estimate(1)) / std::abs(QoI_1_computed - QoI_1_exact) << std::endl << std::endl; // We have to refine either based on reaching an error tolerance or // a number of elements target, which should be verified above // Otherwise we flag elements by error tolerance or nelem target // Uniform refinement if(param.refine_uniformly) { mesh_refinement->uniformly_refine(1); } // Adaptively refine based on reaching an error tolerance else if(param.global_tolerance >= 0. && param.nelem_target == 0.) { mesh_refinement->flag_elements_by_error_tolerance (QoI_elementwise_error); mesh_refinement->refine_and_coarsen_elements(); } // Adaptively refine based on reaching a target number of elements else { if (mesh.n_active_elem() >= param.nelem_target) { std::cout<<"We reached the target number of elements."<<std::endl <<std::endl; break; } mesh_refinement->flag_elements_by_nelem_target (QoI_elementwise_error); mesh_refinement->refine_and_coarsen_elements(); } // Dont forget to reinit the system after each adaptive refinement ! equation_systems.reinit(); std::cout << "Refined mesh to " << mesh.n_active_elem() << " active elements and " << equation_systems.n_active_dofs() << " active dofs." << std::endl; } // Do one last solve if necessary if (a_step == param.max_adaptivesteps) { linear_solver->reuse_preconditioner(false); system.solve(); write_output(equation_systems, a_step, "primal"); NumericVector<Number> &primal_solution = *system.solution; QoISet qois; std::vector<unsigned int> qoi_indices; qoi_indices.push_back(0); qoi_indices.push_back(1); qois.add_indices(qoi_indices); qois.set_weight(0, 0.5); qois.set_weight(1, 0.5); system.assemble_qoi_sides = true; linear_solver->reuse_preconditioner(param.reuse_preconditioner); system.adjoint_solve(); // Now that we have solved the adjoint, set the adjoint_already_solved boolean to true, so we dont solve unneccesarily in the error estimator system.set_adjoint_already_solved(true); NumericVector<Number> &dual_solution_0 = system.get_adjoint_solution(0); primal_solution.swap(dual_solution_0); write_output(equation_systems, a_step, "adjoint_0"); primal_solution.swap(dual_solution_0); NumericVector<Number> &dual_solution_1 = system.get_adjoint_solution(1); primal_solution.swap(dual_solution_1); write_output(equation_systems, a_step, "adjoint_1"); primal_solution.swap(dual_solution_1); std::cout << "Adaptive step " << a_step << ", we have " << mesh.n_active_elem() << " active elements and " << equation_systems.n_active_dofs() << " active dofs." << std::endl ; std::cout << "Postprocessing: " << std::endl; system.postprocess_sides = true; system.postprocess(); Number QoI_0_computed = system.get_QoI_value("computed", 0); Number QoI_0_exact = system.get_QoI_value("exact", 0); Number QoI_1_computed = system.get_QoI_value("computed", 1); Number QoI_1_exact = system.get_QoI_value("exact", 1); std::cout<< "The relative error in QoI 0 is " << std::setprecision(17) << std::abs(QoI_0_computed - QoI_0_exact) / std::abs(QoI_0_exact) << std::endl; std::cout<< "The relative error in QoI 1 is " << std::setprecision(17) << std::abs(QoI_1_computed - QoI_1_exact) / std::abs(QoI_1_exact) << std::endl << std::endl; // We will declare an error vector for passing to the adjoint refinement error estimator // Right now, only the first entry of this vector will be filled (with the global QoI error estimate) // Later, each entry of the vector will contain elementwise error that the user can sum to get the total error ErrorVector QoI_elementwise_error; // Build an adjoint refinement error estimator object AutoPtr<AdjointRefinementEstimator> adjoint_refinement_error_estimator = build_adjoint_refinement_error_estimator(qois); // Estimate the error in each element using the Adjoint Refinement estimator adjoint_refinement_error_estimator->estimate_error(system, QoI_elementwise_error); // Print out the computed error estimate, note that we access the global error estimates // using an accessor function, right now sum(QoI_elementwise_error) != global_QoI_error_estimate std::cout<< "The computed relative error in QoI 0 is " << std::setprecision(17) << std::abs(adjoint_refinement_error_estimator->get_global_QoI_error_estimate(0)) / std::abs(QoI_0_exact) << std::endl; std::cout<< "The computed relative error in QoI 1 is " << std::setprecision(17) << std::abs(adjoint_refinement_error_estimator->get_global_QoI_error_estimate(1)) / std::abs(QoI_1_exact) << std::endl << std::endl; // Also print out effecitivity indices (estimated error/true error) std::cout<< "The effectivity index for the computed error in QoI 0 is " << std::setprecision(17) << std::abs(adjoint_refinement_error_estimator->get_global_QoI_error_estimate(0)) / std::abs(QoI_0_computed - QoI_0_exact) << std::endl; std::cout<< "The effectivity index for the computed error in QoI 1 is " << std::setprecision(17) << std::abs(adjoint_refinement_error_estimator->get_global_QoI_error_estimate(1)) / std::abs(QoI_1_computed - QoI_1_exact) << std::endl << std::endl; } } std::cerr << '[' << libMesh::processor_id() << "] Completing output." << std::endl; #endif // #ifndef LIBMESH_ENABLE_AMR // All done. return 0; }
// The main program. int main (int argc, char** argv) { // Initialize libMesh. LibMeshInit init (argc, argv); // Skip adaptive examples on a non-adaptive libMesh build #ifndef LIBMESH_ENABLE_AMR libmesh_example_assert(false, "--enable-amr"); #else // Only our PETSc interface currently supports adjoint solves libmesh_example_assert(libMesh::default_solver_package() == PETSC_SOLVERS, "--enable-petsc"); std::cout << "Started " << argv[0] << std::endl; // Make sure the general input file exists, and parse it { std::ifstream i("general.in"); if (!i) { std::cerr << '[' << libMesh::processor_id() << "] Can't find general.in; exiting early." << std::endl; libmesh_error(); } } GetPot infile("general.in"); GetPot infile_l_shaped("l-shaped.in"); // Read in parameters from the input file FEMParameters param; param.read(infile); // Skip this default-2D example if libMesh was compiled as 1D-only. libmesh_example_assert(2 <= LIBMESH_DIM, "2D support"); // Create a mesh. Mesh mesh; // And an object to refine it AutoPtr<MeshRefinement> mesh_refinement = build_mesh_refinement(mesh, param); // And an EquationSystems to run on it EquationSystems equation_systems (mesh); std::cout << "Reading in and building the mesh" << std::endl; // Read in the mesh mesh.read(param.domainfile.c_str()); // Make all the elements of the mesh second order so we can compute // with a higher order basis mesh.all_second_order(); // Create a mesh refinement object to do the initial uniform refinements // on the coarse grid read in from lshaped.xda MeshRefinement initial_uniform_refinements(mesh); initial_uniform_refinements.uniformly_refine(param.coarserefinements); std::cout << "Building system" << std::endl; // Build the FEMSystem LaplaceSystem &system = equation_systems.add_system<LaplaceSystem> ("LaplaceSystem"); // Set its parameters set_system_parameters(system, param); std::cout << "Initializing systems" << std::endl; equation_systems.init (); // Print information about the mesh and system to the screen. mesh.print_info(); equation_systems.print_info(); { // Adaptively solve the timestep unsigned int a_step = 0; for (; a_step != param.max_adaptivesteps; ++a_step) { // We can't adapt to both a tolerance and a // target mesh size if (param.global_tolerance != 0.) libmesh_assert (param.nelem_target == 0); // If we aren't adapting to a tolerance we need a // target mesh size else libmesh_assert (param.nelem_target > 0); // Solve the forward problem system.solve(); // Write out the computed primal solution write_output(equation_systems, a_step, "primal"); // Get a pointer to the primal solution vector NumericVector<Number> &primal_solution = *system.solution; // Declare a QoISet object, we need this object to set weights for our QoI error contributions QoISet qois; // Declare a qoi_indices vector, each index will correspond to a QoI std::vector<unsigned int> qoi_indices; qoi_indices.push_back(0); qoi_indices.push_back(1); qois.add_indices(qoi_indices); // Set weights for each index, these will weight the contribution of each QoI in the final error // estimate to be used for flagging elements for refinement qois.set_weight(0, 0.5); qois.set_weight(1, 0.5); // A SensitivityData object to hold the qois and parameters SensitivityData sensitivities(qois, system, system.get_parameter_vector()); // Make sure we get the contributions to the adjoint RHS from the sides system.assemble_qoi_sides = true; // Compute the sensitivities system.adjoint_qoi_parameter_sensitivity(qois, system.get_parameter_vector(), sensitivities); GetPot infile("l-shaped.in"); Number sensitivity_QoI_0_0_computed = sensitivities[0][0]; Number sensitivity_QoI_0_0_exact = infile("sensitivity_0_0", 0.0); Number sensitivity_QoI_0_1_computed = sensitivities[0][1]; Number sensitivity_QoI_0_1_exact = infile("sensitivity_0_1", 0.0); std::cout << "Adaptive step " << a_step << ", we have " << mesh.n_active_elem() << " active elements and " << equation_systems.n_active_dofs() << " active dofs." << std::endl ; std::cout<<"Sensitivity of QoI one to Parameter one is "<<sensitivity_QoI_0_0_computed<<std::endl; std::cout<<"Sensitivity of QoI one to Parameter two is "<<sensitivity_QoI_0_1_computed<<std::endl; std::cout<< "The relative error in sensitivity QoI_0_0 is " << std::setprecision(17) << std::abs(sensitivity_QoI_0_0_computed - sensitivity_QoI_0_0_exact) / std::abs(sensitivity_QoI_0_0_exact) << std::endl; std::cout<< "The relative error in sensitivity QoI_0_1 is " << std::setprecision(17) << std::abs(sensitivity_QoI_0_1_computed - sensitivity_QoI_0_1_exact) / std::abs(sensitivity_QoI_0_1_exact) << std::endl << std::endl; // Get a pointer to the solution vector of the adjoint problem for QoI 0 NumericVector<Number> &dual_solution_0 = system.get_adjoint_solution(0); // Swap the primal and dual solutions so we can write out the adjoint solution primal_solution.swap(dual_solution_0); write_output(equation_systems, a_step, "adjoint_0"); // Swap back primal_solution.swap(dual_solution_0); // We have to refine either based on reaching an error tolerance or // a number of elements target, which should be verified above // Otherwise we flag elements by error tolerance or nelem target // Uniform refinement if(param.refine_uniformly) { std::cout<<"Refining Uniformly"<<std::endl<<std::endl; mesh_refinement->uniformly_refine(1); } // Adaptively refine based on reaching an error tolerance else if(param.global_tolerance >= 0. && param.nelem_target == 0.) { // Now we construct the data structures for the mesh refinement process ErrorVector error; // Build an error estimator object AutoPtr<ErrorEstimator> error_estimator = build_error_estimator(param); // Estimate the error in each element using the Adjoint Residual or Kelly error estimator error_estimator->estimate_error(system, error); mesh_refinement->flag_elements_by_error_tolerance (error); mesh_refinement->refine_and_coarsen_elements(); } // Adaptively refine based on reaching a target number of elements else { // Now we construct the data structures for the mesh refinement process ErrorVector error; // Build an error estimator object AutoPtr<ErrorEstimator> error_estimator = build_error_estimator(param); // Estimate the error in each element using the Adjoint Residual or Kelly error estimator error_estimator->estimate_error(system, error); if (mesh.n_active_elem() >= param.nelem_target) { std::cout<<"We reached the target number of elements."<<std::endl <<std::endl; break; } mesh_refinement->flag_elements_by_nelem_target (error); mesh_refinement->refine_and_coarsen_elements(); } // Dont forget to reinit the system after each adaptive refinement ! equation_systems.reinit(); std::cout << "Refined mesh to " << mesh.n_active_elem() << " active elements and " << equation_systems.n_active_dofs() << " active dofs." << std::endl; } // Do one last solve if necessary if (a_step == param.max_adaptivesteps) { system.solve(); write_output(equation_systems, a_step, "primal"); NumericVector<Number> &primal_solution = *system.solution; QoISet qois; std::vector<unsigned int> qoi_indices; qoi_indices.push_back(0); qoi_indices.push_back(1); qois.add_indices(qoi_indices); qois.set_weight(0, 0.5); qois.set_weight(1, 0.5); SensitivityData sensitivities(qois, system, system.get_parameter_vector()); system.assemble_qoi_sides = true; system.adjoint_qoi_parameter_sensitivity(qois, system.get_parameter_vector(), sensitivities); GetPot infile("l-shaped.in"); Number sensitivity_QoI_0_0_computed = sensitivities[0][0]; Number sensitivity_QoI_0_0_exact = infile("sensitivity_0_0", 0.0); Number sensitivity_QoI_0_1_computed = sensitivities[0][1]; Number sensitivity_QoI_0_1_exact = infile("sensitivity_0_1", 0.0); std::cout << "Adaptive step " << a_step << ", we have " << mesh.n_active_elem() << " active elements and " << equation_systems.n_active_dofs() << " active dofs." << std::endl ; std::cout<<"Sensitivity of QoI one to Parameter one is "<<sensitivity_QoI_0_0_computed<<std::endl; std::cout<<"Sensitivity of QoI one to Parameter two is "<<sensitivity_QoI_0_1_computed<<std::endl; std::cout<< "The error in sensitivity QoI_0_0 is " << std::setprecision(17) << std::abs(sensitivity_QoI_0_0_computed - sensitivity_QoI_0_0_exact)/sensitivity_QoI_0_0_exact << std::endl; std::cout<< "The error in sensitivity QoI_0_1 is " << std::setprecision(17) << std::abs(sensitivity_QoI_0_1_computed - sensitivity_QoI_0_1_exact)/sensitivity_QoI_0_1_exact << std::endl << std::endl; NumericVector<Number> &dual_solution_0 = system.get_adjoint_solution(0); primal_solution.swap(dual_solution_0); write_output(equation_systems, a_step, "adjoint_0"); primal_solution.swap(dual_solution_0); } } std::cerr << '[' << libMesh::processor_id() << "] Completing output." << std::endl; #endif // #ifndef LIBMESH_ENABLE_AMR // All done. return 0; }
// ********************************************************************** // The main program. int main (int argc, char** argv) { // Initialize libMesh. LibMeshInit init (argc, argv); // Skip adaptive examples on a non-adaptive libMesh build #ifndef LIBMESH_ENABLE_AMR libmesh_example_requires(false, "--enable-amr"); #else std::cout << "Started " << argv[0] << std::endl; // Make sure the general input file exists, and parse it { std::ifstream i("general.in"); if (!i) libmesh_error_msg('[' << init.comm().rank() << "] Can't find general.in; exiting early."); } GetPot infile("general.in"); // Read in parameters from the input file FEMParameters param; param.read(infile); // Create a mesh, with dimension to be overridden later, distributed // across the default MPI communicator. Mesh mesh(init.comm()); // And an object to refine it AutoPtr<MeshRefinement> mesh_refinement = build_mesh_refinement(mesh, param); // And an EquationSystems to run on it EquationSystems equation_systems (mesh); std::cout << "Building the mesh" << std::endl; MeshTools::Generation::build_square (mesh, 20, 20, 0., 1., 0., 1., QUAD9); //if changed, check pressure pinning // Create a mesh refinement object to do the initial uniform refinements //MeshRefinement initial_uniform_refinements(mesh); //initial_uniform_refinements.uniformly_refine(param.coarserefinements); std::cout << "Building system" << std::endl; // Build the FEMSystem ConvDiffSys &system = equation_systems.add_system<ConvDiffSys> ("ConvDiffSys"); // Set its parameters set_system_parameters(system, param); std::cout << "Initializing systems" << std::endl; equation_systems.init (); // Print information about the mesh and system to the screen. mesh.print_info(); equation_systems.print_info(); LinearSolver<Number> *linear_solver = system.get_linear_solver(); { // Adaptively solve the timestep unsigned int a_step = 0; for (; a_step != param.max_adaptivesteps; ++a_step) { // We can't adapt to both a tolerance and a // target mesh size if (param.global_tolerance != 0.) libmesh_assert_equal_to (param.nelem_target, 0); // If we aren't adapting to a tolerance we need a // target mesh size else libmesh_assert_greater (param.nelem_target, 0); linear_solver->reuse_preconditioner(false); //can reuse for adjoint, but not for new forwards solve // Solve the forward problem system.solve(); // Write out the computed primal solution write_output(equation_systems, a_step, "primal"); // Get a pointer to the primal solution vector NumericVector<Number> &primal_solution = *system.solution; // Declare a QoISet object, we need this object to set weights for our QoI error contributions QoISet qois; // Declare a qoi_indices vector, each index will correspond to a QoI std::vector<unsigned int> qoi_indices; qoi_indices.push_back(0); qois.add_indices(qoi_indices); // Set weights for each index, these will weight the contribution of each QoI in the final error // estimate to be used for flagging elements for refinement qois.set_weight(0, 1.0); // A SensitivityData object to hold the qois and parameters SensitivityData sensitivities_adj(qois, system, system.get_parameter_vector()); //use adjoint solve SensitivityData sensitivities_for(qois, system, system.get_parameter_vector()); //use forward solve // Make sure we get the contributions to the adjoint RHS from the sides system.assemble_qoi_sides = true; //does nothing anyways... // We are about to solve the adjoint system, but before we do this we see the same preconditioner // flag to reuse the preconditioner from the forward solver linear_solver->reuse_preconditioner(param.reuse_preconditioner); // Here we solve the adjoint problem inside the adjoint_qoi_parameter_sensitivity // function, so we have to set the adjoint_already_solved boolean to false system.set_adjoint_already_solved(false); // Compute the sensitivities system.adjoint_qoi_parameter_sensitivity(qois, system.get_parameter_vector(), sensitivities_adj); system.adjoint_qoi_parameter_sensitivity(qois, system.get_parameter_vector(), sensitivities_for); // Now that we have solved the adjoint, set the adjoint_already_solved boolean to true, //so we dont solve unneccesarily in the error estimator system.set_adjoint_already_solved(true); std::cout << "(Adjoint) Sensitivity of QoI to rho is " << sensitivities_adj[0][0] << std::endl; std::cout << "(Forward) Sensitivity of QoI to rho is " << sensitivities_for[0][0] << std::endl; // Get a pointer to the solution vector of the adjoint problem for QoI 0 NumericVector<Number> &dual_solution = system.get_adjoint_solution(0); // Swap the (pointers to) primal and dual solutions so we can write out the adjoint solution primal_solution.swap(dual_solution); write_output(equation_systems, a_step, "adjoint"); // Swap back primal_solution.swap(dual_solution); std::cout << "Adaptive step " << a_step << ", we have " << mesh.n_active_elem() << " active elements and " << equation_systems.n_active_dofs() << " active dofs." << std::endl ; // Postprocess, compute the approximate QoIs and write them out to the console std::cout << "Postprocessing: " << std::endl; system.postprocess_sides = false; //QoI doesn't involve edges system.postprocess(); Number QoI_computed = system.get_QoI_value("computed", 0); std::cout<< "Computed QoI is " << std::setprecision(17) << QoI_computed << std::endl; // Now we construct the data structures for the mesh refinement process ErrorVector error; // Build an error estimator object AutoPtr<ErrorEstimator> error_estimator = build_error_estimator(param, qois); // Estimate the error in each element using the Adjoint Residual or Kelly error estimator error_estimator->estimate_error(system, error); // We have to refine either based on reaching an error tolerance or // a number of elements target, which should be verified above // Otherwise we flag elements by error tolerance or nelem target // Uniform refinement if(param.refine_uniformly) { mesh_refinement->uniformly_refine(1); } // Adaptively refine based on reaching an error tolerance else if(param.global_tolerance >= 0. && param.nelem_target == 0.) { mesh_refinement->flag_elements_by_error_tolerance (error); mesh_refinement->refine_and_coarsen_elements(); } // Adaptively refine based on reaching a target number of elements else { if (mesh.n_active_elem() >= param.nelem_target) { std::cout<<"We reached the target number of elements."<<std::endl <<std::endl; break; } mesh_refinement->flag_elements_by_nelem_target (error); mesh_refinement->refine_and_coarsen_elements(); } // Dont forget to reinit the system after each adaptive refinement ! equation_systems.reinit(); std::cout << "Refined mesh to " << mesh.n_active_elem() << " active elements and " << equation_systems.n_active_dofs() << " active dofs." << std::endl; } // Do one last solve if necessary if (a_step == param.max_adaptivesteps) { linear_solver->reuse_preconditioner(false); system.solve(); write_output(equation_systems, a_step, "primal"); NumericVector<Number> &primal_solution = *system.solution; QoISet qois; std::vector<unsigned int> qoi_indices; qoi_indices.push_back(0); qois.add_indices(qoi_indices); qois.set_weight(0, 1.0); SensitivityData sensitivities_adj(qois, system, system.get_parameter_vector()); SensitivityData sensitivities_for(qois, system, system.get_parameter_vector()); system.assemble_qoi_sides = false; //QoI doesn't involve sides linear_solver->reuse_preconditioner(param.reuse_preconditioner); system.set_adjoint_already_solved(false); system.adjoint_qoi_parameter_sensitivity(qois, system.get_parameter_vector(), sensitivities_adj); system.forward_qoi_parameter_sensitivity(qois, system.get_parameter_vector(), sensitivities_for); // Now that we have solved the adjoint, set the adjoint_already_solved boolean to true, // so we dont solve unneccesarily in the error estimator system.set_adjoint_already_solved(true); std::cout << "(Adjoint) Sensitivity of QoI to rho is " << sensitivities_adj[0][0] << std::endl; std::cout << "(Forward) Sensitivity of QoI to rho is " << sensitivities_for[0][0] << std::endl; NumericVector<Number> &dual_solution = system.get_adjoint_solution(0); primal_solution.swap(dual_solution); write_output(equation_systems, a_step, "adjoint"); primal_solution.swap(dual_solution); std::cout << "Adaptive step " << a_step << ", we have " << mesh.n_active_elem() << " active elements and " << equation_systems.n_active_dofs() << " active dofs." << std::endl ; std::cout << "Postprocessing: " << std::endl; system.postprocess_sides = false; //nothing special on sides system.postprocess(); Number QoI_computed = system.get_QoI_value("computed", 0); std::cout<< "Computed QoI is " << std::setprecision(17) << QoI_computed << std::endl; } } std::cerr << '[' << mesh.processor_id() << "] Completing output." << std::endl; #endif // #ifndef LIBMESH_ENABLE_AMR // All done. return 0; }