int main(int argc, char** argv) {

	// - Start libmesh ---------------------------------------------------------
	const bool MASTER_bPerfLog_carl_libmesh = true;
	libMesh::LibMeshInit init(argc, argv);

	libMesh::PerfLog perf_log("Main program", MASTER_bPerfLog_carl_libmesh);

	// - Displacement conditions -----------------------------------------------
	// boundary_displacement x_max_BIG(1.0,0,0);
	// boundary_displacement x_min_BIG(-1.0,0,0);
	boundary_id_cube boundary_ids;

	// - Set up inputs
	GetPot command_line(argc, argv);
	GetPot field_parser;
	std::string input_filename;

	if (command_line.search(1, "--inputfile")) {
		input_filename = command_line.next(input_filename);
		field_parser.parse_input_file(input_filename, "#", "\n", " \t\n");
	} else {
		field_parser = command_line;
	}

	carl::coupling_generation_input_params input_params;
	carl::get_input_params(field_parser, input_params);

	const unsigned int dim = 3;

	libmesh_example_requires(dim == LIBMESH_DIM, "3D support");

	// Set up the communicator and the rank variables
	libMesh::Parallel::Communicator& WorldComm = init.comm();
	libMesh::Parallel::Communicator LocalComm;

	int rank = WorldComm.rank();
	int nodes = WorldComm.size();

	WorldComm.split(rank,rank,LocalComm);

	// - Read the meshes -------------------------------------------------------

	// - Parallelized meshes: A, B, mediator and weight

	perf_log.push("Meshes - Parallel","Read files:");
	libMesh::Mesh mesh_BIG(WorldComm, dim);
//	mesh_BIG.allow_renumbering(false);
	mesh_BIG.read(input_params.mesh_BIG_file);
	mesh_BIG.prepare_for_use();

	libMesh::Mesh mesh_micro(WorldComm, dim);
//	mesh_micro.allow_renumbering(false);
	mesh_micro.read(input_params.mesh_micro_file);
	mesh_micro.prepare_for_use();

	libMesh::Mesh mesh_mediator(WorldComm, dim);
	mesh_mediator.allow_renumbering(false);
	mesh_mediator.read(input_params.mesh_mediator_file);
	mesh_mediator.prepare_for_use();

	libMesh::Mesh mesh_weight(WorldComm, dim);
	mesh_weight.allow_renumbering(false);
	mesh_weight.read(input_params.mesh_weight_file);
	mesh_weight.prepare_for_use();

//	// DEBUG - Test: print info per proc
//	{
//		std::ofstream mesh_info_ofstream;
//		mesh_info_ofstream.open("meshes/parallel_test/output/mesh_A_" + std::to_string(rank) + "_info.txt");
//		mesh_BIG.print_info(mesh_info_ofstream);
//		mesh_info_ofstream.close();
//
//		WorldComm.barrier();
//
//		mesh_info_ofstream.open("meshes/parallel_test/output/mesh_B_" + std::to_string(rank) + "_info.txt");
//		mesh_micro.print_info(mesh_info_ofstream);
//		mesh_info_ofstream.close();
//
//		WorldComm.barrier();
//
//		mesh_info_ofstream.open("meshes/parallel_test/output/mesh_inter_" + std::to_string(rank) + "_info.txt");
//		mesh_inter.print_info(mesh_info_ofstream);
//		mesh_info_ofstream.close();
//
//		WorldComm.barrier();
//	}

	perf_log.pop("Meshes - Parallel","Read files:");

	// - Local meshes: restrict A and restrict B

	// -> libMesh's default mesh IS the SerialMesh, which creates a copy of
	//    itself on each processor, but partitions the iterators over each
	//    processor to allow the parallelization of the code. The usage of
	//    ParallelMesh is still a bit risky, and, performance-wise, is worse
	//    than SerialMesh for less than a few thousand processors.

	perf_log.push("Meshes - Serial","Read files:");
	libMesh::ReplicatedMesh mesh_R_BIG(WorldComm, dim);
	mesh_R_BIG.allow_renumbering(false);
	mesh_R_BIG.read(input_params.mesh_restrict_BIG_file);
	mesh_R_BIG.prepare_for_use();

	libMesh::ReplicatedMesh mesh_R_micro(WorldComm, dim);
	mesh_R_micro.allow_renumbering(false);
	mesh_R_micro.read(input_params.mesh_restrict_micro_file);
	mesh_R_micro.prepare_for_use();

//	// DEBUG - Test: print info per proc
//	{
//		std::ofstream mesh_info_ofstream;
//		mesh_info_ofstream.open("meshes/parallel_test/output/mesh_RA_" + std::to_string(rank) + "_info.txt");
//		mesh_R_BIG.print_info(mesh_info_ofstream);
//		mesh_info_ofstream.close();
//
//		WorldComm.barrier();
//
//		mesh_info_ofstream.open("meshes/parallel_test/output/mesh_RB_" + std::to_string(rank) + "_info.txt");
//		mesh_R_micro.print_info(mesh_info_ofstream);
//		mesh_info_ofstream.close();
//
//		WorldComm.barrier();
//
//		mesh_info_ofstream.open("meshes/parallel_test/output/mesh_mediator_" + std::to_string(rank) + "_info.txt");
//		mesh_mediator.print_info(mesh_info_ofstream);
//		mesh_info_ofstream.close();
//
//		std::ofstream mesh_data;
//		mesh_data.open("meshes/parallel_test/output/mesh_A_data_" + std::to_string(rank)  + ".dat");
//		libMesh::MeshBase::const_element_iterator       el     = mesh_BIG.active_local_elements_begin();
//		const libMesh::MeshBase::const_element_iterator end_el = mesh_BIG.active_local_elements_end();
//
//		for ( ; el != end_el; ++el)
//		{
//			const libMesh::Elem* elem = *el;
//			mesh_data << elem->id() << " " << elem->point(0) << " " << elem->point(1) << " " << elem->point(2)<< " " << elem->point(3)<< std::endl;
//		}
//		mesh_data.close();
//	}

	// - Local mesh: intersection mesh
	libMesh::Mesh mesh_inter(LocalComm, dim);
	mesh_inter.allow_renumbering(false);
	std::string local_inter_mesh_filename = input_params.mesh_inter_file + "_r_"
										+ std::to_string(rank) + "_n_" + std::to_string(nodes) + ".e";
	std::string local_inter_table_filename = input_params.intersection_table_full + "_r_"
										+ std::to_string(rank) + "_n_" + std::to_string(nodes) + "_inter_table.dat";
	std::string global_inter_table_filename = input_params.intersection_table_full + "_global_inter_pairs.dat";

	mesh_inter.read(local_inter_mesh_filename);
	mesh_inter.prepare_for_use();

	perf_log.pop("Meshes - Serial","Read files:");

	/*
	 * 		To do on the first proc
	 * 		- Read and build the equivalence tables between R_X and X, e_X - DONE
	 * 		- Read and build the intersection pairs table between A and B, p_AB - DONE
	 *		- Read and build the intersection indexes table, I_F - DONE
	 *
	 * 		Broadcast e_X, p_AB, I_F - DONE
	 *
	 * 		Build on each proc
	 * 		- The restricted intersection pairs table, p_R,AB- DONE
	 * 		- A local intersection indexes table, I_L - DONE
	 *
	 * 		Convert the pairs table to the libMesh indexing - DONE
	 */

	perf_log.push("Equivalence / intersection tables","Read files:");
	std::unordered_map<int,std::pair<int,int> > local_intersection_pairs_map;
	std::unordered_map<int,std::pair<int,int> > local_intersection_restricted_pairs_map;
	std::unordered_map<int,int> local_intersection_meshI_to_inter_map;

	std::unordered_map<int,int> equivalence_table_BIG_to_R_BIG;
	std::unordered_map<int,int> equivalence_table_micro_to_R_micro;
	std::unordered_map<int,int> equivalence_table_R_BIG_to_BIG;
	std::unordered_map<int,int> equivalence_table_R_micro_to_micro;

	//	Start by reading and broadcasting the equivalence tables
	carl::set_equivalence_tables(
			WorldComm,
			input_params.equivalence_table_restrict_BIG_file,
			input_params.equivalence_table_restrict_micro_file,

			equivalence_table_BIG_to_R_BIG,
			equivalence_table_micro_to_R_micro,
			equivalence_table_R_BIG_to_BIG,
			equivalence_table_R_micro_to_micro);

	if(input_params.b_UseMesh_BIG_AsMediator)
	{
		carl::set_local_intersection_tables(
				WorldComm,
				mesh_inter,
				local_inter_table_filename,
				input_params.equivalence_table_restrict_BIG_file,
				input_params.equivalence_table_restrict_micro_file,

				equivalence_table_BIG_to_R_BIG,
				equivalence_table_micro_to_R_micro,

				local_intersection_pairs_map,
				local_intersection_restricted_pairs_map,
				local_intersection_meshI_to_inter_map);
	}
	else if(input_params.b_UseMesh_micro_AsMediator)
	{
		carl::set_local_intersection_tables(
				WorldComm,
				mesh_inter,
				local_inter_table_filename,
				input_params.equivalence_table_restrict_micro_file,
				input_params.equivalence_table_restrict_BIG_file,

				equivalence_table_micro_to_R_micro,
				equivalence_table_BIG_to_R_BIG,

				local_intersection_pairs_map,
				local_intersection_restricted_pairs_map,
				local_intersection_meshI_to_inter_map);
	}

	std::unordered_multimap<int,int> inter_mediator_BIG;
	std::unordered_multimap<int,int> inter_mediator_micro;

	if(input_params.b_Repartition_micro)
		carl::repartition_system_meshes(WorldComm,mesh_micro,mesh_BIG,local_intersection_pairs_map);

	carl::set_global_mediator_system_intersection_lists(
			WorldComm,
			global_inter_table_filename,
			equivalence_table_BIG_to_R_BIG,
			equivalence_table_R_BIG_to_BIG,
			inter_mediator_BIG,
			inter_mediator_micro);

	perf_log.pop("Equivalence / intersection tables","Read files:");

	perf_log.push("Weight function domain","Read files:");

	// Set weight functions
	int domain_Idx_BIG = -1;
	int nb_of_domain_Idx = 1;
	std::vector<int> domain_Idx_micro;
	std::vector<int> domain_Idx_coupling;

	carl::set_weight_function_domain_idx(	input_params.weight_domain_idx_file,
											domain_Idx_BIG, nb_of_domain_Idx,
											domain_Idx_micro, domain_Idx_coupling
											);

	WorldComm.barrier();

	perf_log.pop("Weight function domain","Read files:");

	// - Generate the equation systems -----------------------------------------
	perf_log.push("Initialization","System initialization:");
	carl::coupled_system CoupledTest(WorldComm,input_params.solver_type);

	libMesh::EquationSystems& equation_systems_inter =
					CoupledTest.add_inter_EquationSystem("InterSys", mesh_inter);

	// Add the weight function mesh
	CoupledTest.add_alpha_mask("MicroSys",mesh_weight);
	CoupledTest.set_alpha_mask_parameters("MicroSys",domain_Idx_BIG,domain_Idx_micro[0],domain_Idx_coupling[0]);
	
	perf_log.pop("Initialization","System initialization:");

	// - Build the BIG system --------------------------------------------------

	perf_log.push("Macro system","System initialization:");
	libMesh::EquationSystems& equation_systems_BIG =
					CoupledTest.set_BIG_EquationSystem("BigSys", mesh_BIG);

	// [MACRO] Set up the physical properties
	libMesh::LinearImplicitSystem& elasticity_system_BIG
										= add_elasticity(equation_systems_BIG);

	// [MACRO] Defining the boundaries with Dirichlet conditions
	//set_displaced_border_translation(elasticity_system_BIG, x_min_BIG,boundary_ids.MIN_X);
	set_clamped_border(elasticity_system_BIG, boundary_ids.MIN_X);

	// [MACRO] Build stress system
	libMesh::ExplicitSystem& stress_system_BIG
										= add_stress(equation_systems_BIG);

	equation_systems_BIG.init();

	perf_log.pop("Macro system","System initialization:");

	// - Build the micro system ------------------------------------------------

	perf_log.push("Micro system","System initialization:");

	libMesh::EquationSystems& equation_systems_micro =
					CoupledTest.add_micro_EquationSystem<libMesh::PetscMatrix<libMesh::Number> >("MicroSys", mesh_micro);

	// [MICRO] Set up the physical properties
	libMesh::LinearImplicitSystem& elasticity_system_micro
										= add_elasticity(equation_systems_micro);

	// [MICRO] Build stress system
	libMesh::ExplicitSystem& stress_system_micro
										= add_stress(equation_systems_micro);

	equation_systems_micro.init();
	perf_log.pop("Micro system","System initialization:");

	// - Build the RESTRICTED BIG system ---------------------------------------

	perf_log.push("RESTRICTED macro system","System initialization:");
	libMesh::EquationSystems& equation_systems_R_BIG =
					CoupledTest.set_Restricted_BIG_EquationSystem("BigSys", mesh_R_BIG);

	// [R. MACRO] Set up the physical properties
	libMesh::ExplicitSystem& elasticity_system_R_BIG
										= add_explicit_elasticity(equation_systems_R_BIG);

	carl::reduced_system_init(elasticity_system_R_BIG);

	perf_log.pop("RESTRICTED macro system","System initialization:");

	// - Build the RESTRICTED micro system ------------------------------------------------

	perf_log.push("RESTRICTED micro system","System initialization:");

	libMesh::EquationSystems& equation_systems_R_micro =
					CoupledTest.add_Restricted_micro_EquationSystem("MicroSys", mesh_R_micro);

	// [R. MICRO] Set up the physical properties
	libMesh::ExplicitSystem& elasticity_system_R_micro
										= add_explicit_elasticity(equation_systems_R_micro);

	carl::reduced_system_init(elasticity_system_R_micro);
	
	perf_log.pop("RESTRICTED micro system","System initialization:");

	// - Build the mediator system ---------------------------------------------

	perf_log.push("Mediator system","System initialization:");

	libMesh::EquationSystems& equation_systems_mediator =
					CoupledTest.add_mediator_EquationSystem("MediatorSys", mesh_mediator);

	libMesh::LinearImplicitSystem& elasticity_system_mediator
										= add_elasticity(equation_systems_mediator);

	equation_systems_mediator.init();

	WorldComm.barrier();

	perf_log.pop("Mediator system","System initialization:");

	// - Build the dummy inter system ------------------------------------------

	perf_log.push("Intersection system","System initialization:");

	libMesh::ExplicitSystem& elasticity_system_inter
										= add_explicit_elasticity(equation_systems_inter);

	carl::reduced_system_init(elasticity_system_inter);

	WorldComm.barrier();

	perf_log.pop("Intersection system","System initialization:");

	perf_log.push("Physical properties","System initialization:");
	double BIG_E = 0;
	double BIG_Mu = 0;

	double coupling_const = -1;
	std::ifstream phys_params_file(input_params.physical_params_file);
	carl::jump_lines(phys_params_file);
	phys_params_file >> BIG_E >> BIG_Mu;
	phys_params_file.close();

	set_constant_physical_properties(equation_systems_BIG,BIG_E,BIG_Mu);
	set_constant_physical_properties(equation_systems_micro,BIG_E,BIG_Mu);
	perf_log.pop("Physical properties","System initialization:");

	// - Set the coupling matrix -----------------------------------------------
	perf_log.push("Set coupling matrices");
	coupling_const = BIG_E;
	CoupledTest.set_coupling_parameters("MicroSys",coupling_const,input_params.mean_distance);

	CoupledTest.use_H1_coupling("MicroSys");
	CoupledTest.assemble_coupling_elasticity_3D_parallel("BigSys","MicroSys",
			"InterSys","MediatorSys",
			mesh_R_BIG, mesh_R_micro,
			local_intersection_pairs_map,
			local_intersection_restricted_pairs_map,
			local_intersection_meshI_to_inter_map,
			inter_mediator_BIG,
			inter_mediator_micro);

	std::cout << std::endl;
	std::cout << "| ---> Constants " << std::endl;
	std::cout << "| Macro :" << std::endl;
	std::cout << "|    E            : " << BIG_E << std::endl;
	std::cout << "|    Mu (lamba_2) : " << BIG_Mu << std::endl;
	std::cout << "|    lambda_1     : " << eval_lambda_1(BIG_E,BIG_Mu) << std::endl;
	std::cout << "| Coupling :" << std::endl;
	std::cout << "|    kappa        : " << coupling_const << std::endl;
	std::cout << "|    e            : " << input_params.mean_distance << std::endl;

	switch(input_params.solver_type)
	{
		case carl::LATIN_MODIFIED_STIFFNESS:
		case carl::LATIN_ORIGINAL_STIFFNESS:
		{
			std::cout << "| LATIN :" << std::endl;
			std::cout << "|    k_dA, k_dB   : " << input_params.k_dA << " " << input_params.k_dB << std::endl;
			std::cout << "|    k_cA, k_cB   : " << input_params.k_cA << " " << input_params.k_cB << std::endl;
			break;
		}
		case carl::CG:
		{
			break;
		}
	}
	perf_log.pop("Set coupling matrices");
	
	std::cout << "| restart file    : " << input_params.coupled_restart_file_base << "*" << std::endl;

	std::cout << std::endl << "| --> Testing the solver " << std::endl << std::endl;
	perf_log.push("Set up","Coupled Solver:");
	if(input_params.b_PrintRestartFiles || input_params.b_UseRestartFiles)
	{
		CoupledTest.set_restart(	input_params.b_UseRestartFiles,
				input_params.b_PrintRestartFiles,
				input_params.coupled_restart_file_base);
	}
	std::cout << std::endl << "| --> Setting the solver " << std::endl << std::endl;

	switch(input_params.solver_type)
	{
		case carl::LATIN_MODIFIED_STIFFNESS:
		case carl::LATIN_ORIGINAL_STIFFNESS:
		{
			CoupledTest.set_LATIN_solver(	"MicroSys","Elasticity",
									assemble_elasticity_with_weight,
									assemble_elasticity_with_weight_micro_and_traction,
									input_params.k_dA, input_params.k_dB, input_params.k_cA, input_params.k_cB,
									input_params.LATIN_eps, input_params.LATIN_conv_max, input_params.LATIN_relax);
			break;
		}
		case carl::CG:
		{
			CoupledTest.use_null_space_micro("MicroSys",true);
			CoupledTest.set_cg_preconditioner_type(input_params.CG_precond_type);
			CoupledTest.set_CG_solver(	"MicroSys","Elasticity",
											assemble_elasticity_with_weight,
											assemble_elasticity_with_weight_micro_and_traction,
											input_params.CG_coupled_conv_abs,input_params.CG_coupled_conv_rel,
											input_params.CG_coupled_conv_max,input_params.CG_coupled_div,
											input_params.CG_coupled_conv_corr);
			break;
		}
	}

	perf_log.pop("Set up","Coupled Solver:");


	// Solve !
	perf_log.push("Solve","Coupled Solver:");
	CoupledTest.solve("MicroSys","Elasticity",input_params.coupled_convergence_output);
	perf_log.pop("Solve","Coupled Solver:");

	// Calculate stress
	perf_log.push("Compute stress - micro","Output:");
	compute_stresses(equation_systems_micro);
	perf_log.pop("Compute stress - micro","Output:");

	perf_log.push("Compute stress - macro","Output:");
	compute_stresses(equation_systems_BIG);
	perf_log.pop("Compute stress - macro","Output:");

	// Export solution
#ifdef LIBMESH_HAVE_EXODUS_API
	if(input_params.b_PrintOutput)
	{
		perf_log.push("Save output","Output:");
		libMesh::ExodusII_IO exo_io_micro(mesh_micro, /*single_precision=*/true);

		std::set<std::string> system_names_micro;
		system_names_micro.insert("Elasticity");
		exo_io_micro.write_equation_systems(input_params.output_file_micro,equation_systems_micro,&system_names_micro);

		exo_io_micro.write_element_data(equation_systems_micro);

		libMesh::ExodusII_IO exo_io_BIG(mesh_BIG, /*single_precision=*/true);

		std::set<std::string> system_names_BIG;
		system_names_BIG.insert("Elasticity");
		exo_io_BIG.write_equation_systems(input_params.output_file_BIG,equation_systems_BIG,&system_names_BIG);

		exo_io_BIG.write_element_data(equation_systems_BIG);
		perf_log.pop("Save output","Output:");
	}
#endif

	if(input_params.b_ExportScalingData)
	{
		CoupledTest.print_perf_log(input_params.scaling_data_file);
	}

	return 0;
}
Example #2
0
// The main program.
int main (int argc, char** argv)
{
  // Skip adaptive examples on a non-adaptive libMesh build
#ifndef LIBMESH_ENABLE_AMR
  libmesh_example_requires(false, "--enable-amr");
#else
  // Skip this 2D example if libMesh was compiled as 1D-only.
  libmesh_example_requires(2 <= LIBMESH_DIM, "2D support");

  // Initialize libMesh.
  LibMeshInit init (argc, argv);

  // This doesn't converge with Trilinos for some reason...
  libmesh_example_requires(libMesh::default_solver_package() == PETSC_SOLVERS, "--enable-petsc");

  libMesh::out << "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(init.comm());
  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
  UniquePtr<MeshRefinement> mesh_refinement(new MeshRefinement(mesh));

  // And an EquationSystems to run on it
  EquationSystems equation_systems (mesh);

  libMesh::out << "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);

  libMesh::out << "Building system" << std::endl;

  HeatSystem & system = equation_systems.add_system<HeatSystem> ("HeatSystem");

  set_system_parameters(system, param);

  libMesh::out << "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();
    }

  libMesh::out << "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
          libMesh::out << " 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
          libMesh::out << "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
      libMesh::out << 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);

      libMesh::out << "Setting adjoint initial conditions Z("
                   << system.time
                   << ")"
                   <<std::endl;

      // Need to call adjoint_advance_timestep once for the initial condition setup
      libMesh::out<<"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
          libMesh::out << " 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.
          libMesh::out << "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
          libMesh::out << "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
      libMesh::out << "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
      libMesh::out << "Sensitivity of QoI 0 w.r.t parameter 0 is: "
                   << sensitivity_0_0
                   << std::endl;

      // Hard coded assert to ensure that the actual numbers we are
      // getting are what they should be
      // The 2e-4 tolerance is chosen to ensure success even with
      // 32-bit floats
      libmesh_assert_less(std::abs(sensitivity_0_0 - (-5.37173)), 2.e-4);

#ifdef NDEBUG
    }
  catch (...)
    {
      libMesh::err << '[' << mesh.processor_id()
                   << "] Caught exception; exiting early." << std::endl;
    }
#endif

  libMesh::err << '[' << mesh.processor_id()
               << "] Completing output."
               << std::endl;

  // All done.
  return 0;

#endif // LIBMESH_ENABLE_AMR
}
int main(int argc, char* argv[])
{
  GRINS::Runner grins(argc,argv);

  // This is a tough problem to get to converge without PETSc
  libmesh_example_requires
    (libMesh::default_solver_package() != libMesh::LASPACK_SOLVERS,
     "--enable-petsc");

  grins.init();

  const GetPot & inputfile = grins.get_input_file();

  GRINS::Simulation & sim = grins.get_simulation();

  //FIXME: We need to move this to within the Simulation object somehow...
  std::string restart_file = inputfile( "restart-options/restart_file", "none" );

  if( restart_file == "none" )
    {
      // Asssign initial temperature value
      std::string system_name = inputfile( "screen-options/system_name", "GRINS" );
      std::shared_ptr<libMesh::EquationSystems> es = sim.get_equation_system();
      const libMesh::System& system = es->get_system(system_name);

      libMesh::Parameters &params = es->parameters;
      libMesh::Real T_init = inputfile("Materials/TestMaterial/ReferenceTemperature/value", 0.0);
      libMesh::Real p0_init = inputfile("Materials/TestMaterial/ThermodynamicPressure/value", 0.0);

      libMesh::Real& dummy_T  = params.set<libMesh::Real>("T_init");
      dummy_T = T_init;

      libMesh::Real& dummy_p0 = params.set<libMesh::Real>("p0_init");
      dummy_p0 = p0_init;

      system.project_solution( initial_values, NULL, params );
    }

  grins.run();

  libMesh::Real qoi = sim.get_qoi_value(0);

  // Note that this is a *really* coarse mesh. This is just for testing
  // and not even close to the real QoI for this problem.

  // Erroneous value from libMesh 0.9.2.2
  // const libMesh::Real exact_qoi = 4.8158910676325055;

  // Value after libMesh 7acb6fc9 bugfix
  const libMesh::Real exact_qoi = 4.8654229502012685;

  const libMesh::Real tol = 1.0e-9;

  int return_flag = 0;

  libMesh::Real rel_error = std::fabs( (qoi-exact_qoi)/exact_qoi );

  if( rel_error > tol )
    {
      // Skip this test until we know what changed
      // return_flag = 1;
      return_flag = 77;

      std::cerr << std::setprecision(16)
                << std::scientific
                << "Error: QoI value mismatch." << std::endl
                << "Computed qoi   = " << qoi << std::endl
                << "Exact qoi      = " << exact_qoi << std::endl
                << "Relative error = " << rel_error << std::endl;
    }

  return return_flag;
}
int main(int argc, char** argv) {

	// --- Initialize libMesh
	libMesh::LibMeshInit init(argc, argv);

	// Do performance log?
	libMesh::PerfLog perf_log("Main program");

	// libMesh's C++ / MPI communicator wrapper
	libMesh::Parallel::Communicator& WorldComm = init.comm();

	// --- Set up inputs

	// Command line parser
	GetPot command_line(argc, argv);

	// File parser
	GetPot field_parser;

	// If there is an input file, parse it to get the parameters. Else, parse the command line
	std::string input_filename;
	if (command_line.search(2, "--inputfile", "-i")) {
		input_filename = command_line.next(input_filename);
		field_parser.parse_input_file(input_filename, "#", "\n", " \t\n");
	} else {
		field_parser = command_line;
	}

	carl::libmesh_apply_solution_input_params input_params;
	carl::get_input_params(field_parser, input_params);

	// Check libMesh installation dimension
	const unsigned int dim = 3;

	libmesh_example_requires(dim == LIBMESH_DIM, "3D support");

	// - Parallelized meshes A
	libMesh::Mesh system_mesh(WorldComm, dim);
	system_mesh.read(input_params.input_mesh);
	system_mesh.prepare_for_use();

	// Set the equation systems object
	libMesh::EquationSystems equation_systems(system_mesh);

	// Add linear elasticity and stress
	libMesh::LinearImplicitSystem& elasticity_system
										= add_elasticity(equation_systems);
	add_stress(equation_systems);

	// Initialize the equation systems
	equation_systems.init();

	// Homogeneous physical properties
	set_homogeneous_physical_properties(equation_systems, input_params.physical_params_file);

	// Read the solution vector
	libMesh::PetscVector<libMesh::Real> * sol_vec_ptr = libMesh::cast_ptr<libMesh::PetscVector<libMesh::Real> * >(elasticity_system.solution.get());
	carl::read_PETSC_vector(sol_vec_ptr->vec(),input_params.input_vector, WorldComm.get());

	// Close it and update
	elasticity_system.solution->close();
	elasticity_system.update();

	// Calculate the stress
	compute_stresses(equation_systems);

	// Export solution
#ifdef LIBMESH_HAVE_EXODUS_API
		libMesh::ExodusII_IO exo_io_interface(system_mesh, /*single_precision=*/true);

		std::set<std::string> system_names;
		system_names.insert("Elasticity");
		exo_io_interface.write_equation_systems(input_params.output_mesh,equation_systems,&system_names);
		exo_io_interface.write_element_data(equation_systems);
#endif

	return 0;
}