Ejemplo n.º 1
0
// The main program
int main(int argc, char** argv)
{
    // Initialize libMesh
    LibMeshInit init(argc, argv);

    // Parameters
    GetPot infile("fem_system_params.in");
    const Real global_tolerance          = infile("global_tolerance", 0.);
    const unsigned int nelem_target      = infile("n_elements", 400);
    const bool transient                 = infile("transient", true);
    const Real deltat                    = infile("deltat", 0.005);
    unsigned int n_timesteps             = infile("n_timesteps", 1);
    //const unsigned int coarsegridsize    = infile("coarsegridsize", 1);
    const unsigned int coarserefinements = infile("coarserefinements", 0);
    const unsigned int max_adaptivesteps = infile("max_adaptivesteps", 10);
    //const unsigned int dim               = 2;

#ifdef LIBMESH_HAVE_EXODUS_API
    const unsigned int write_interval    = infile("write_interval", 5);
#endif

    // Create a mesh, with dimension to be overridden later, distributed
    // across the default MPI communicator.
    Mesh mesh(init.comm());
    GetPot infileForMesh("convdiff_mprime.in");
    std::string find_mesh_here = infileForMesh("mesh","psiLF_mesh.xda");
    mesh.read(find_mesh_here);

    std::cout << "Read in mesh from: " << find_mesh_here << "\n\n";

    // And an object to refine it
    MeshRefinement mesh_refinement(mesh);
    mesh_refinement.coarsen_by_parents() = true;
    mesh_refinement.absolute_global_tolerance() = global_tolerance;
    mesh_refinement.nelem_target() = nelem_target;
    mesh_refinement.refine_fraction() = 0.3;
    mesh_refinement.coarsen_fraction() = 0.3;
    mesh_refinement.coarsen_threshold() = 0.1;

    //mesh_refinement.uniformly_refine(coarserefinements);

    // Print information about the mesh to the screen.
    mesh.print_info();

    // Create an equation systems object.
    EquationSystems equation_systems (mesh);

    // Name system
    ConvDiff_MprimeSys & system =
        equation_systems.add_system<ConvDiff_MprimeSys>("Diff_ConvDiff_MprimeSys");

    // Steady-state problem
    system.time_solver =
        AutoPtr<TimeSolver>(new SteadySolver(system));

    // Sanity check that we are indeed solving a steady problem
    libmesh_assert_equal_to (n_timesteps, 1);

    // Read in all the equation systems data from the LF solve (system, solutions, rhs, etc)
    std::string find_psiLF_here = infileForMesh("psiLF_file","psiLF.xda");
    std::cout << "Looking for psiLF at: " << find_psiLF_here << "\n\n";

    equation_systems.read(find_psiLF_here, READ,
                          EquationSystems::READ_HEADER |
                          EquationSystems::READ_DATA |
                          EquationSystems::READ_ADDITIONAL_DATA);

    // Check that the norm of the solution read in is what we expect it to be
    Real readin_L2 = system.calculate_norm(*system.solution, 0, L2);
    std::cout << "Read in solution norm: "<< readin_L2 << std::endl << std::endl;

    //DEBUG
    //equation_systems.write("right_back_out.xda", WRITE, EquationSystems::WRITE_DATA |
    //		 EquationSystems::WRITE_ADDITIONAL_DATA);
#ifdef LIBMESH_HAVE_GMV
    //GMVIO(equation_systems.get_mesh()).write_equation_systems(std::string("right_back_out.gmv"), equation_systems);
#endif

    // Initialize the system
    //equation_systems.init ();  //already initialized by read-in

    // And the nonlinear solver options
    NewtonSolver *solver = new NewtonSolver(system);
    system.time_solver->diff_solver() = AutoPtr<DiffSolver>(solver);
    solver->quiet = infile("solver_quiet", true);
    solver->verbose = !solver->quiet;
    solver->max_nonlinear_iterations =
        infile("max_nonlinear_iterations", 15);
    solver->relative_step_tolerance =
        infile("relative_step_tolerance", 1.e-3);
    solver->relative_residual_tolerance =
        infile("relative_residual_tolerance", 0.0);
    solver->absolute_residual_tolerance =
        infile("absolute_residual_tolerance", 0.0);

    // And the linear solver options
    solver->max_linear_iterations =
        infile("max_linear_iterations", 50000);
    solver->initial_linear_tolerance =
        infile("initial_linear_tolerance", 1.e-3);

    // Print information about the system to the screen.
    equation_systems.print_info();

    // Now we begin the timestep loop to compute the time-accurate
    // solution of the equations...not that this is transient, but eh, why not...
    for (unsigned int t_step=0; t_step != n_timesteps; ++t_step)
    {
        // A pretty update message
        std::cout << "\n\nSolving time step " << t_step << ", time = "
                  << system.time << std::endl;

        // Adaptively solve the timestep
        unsigned int a_step = 0;
        for (; a_step != max_adaptivesteps; ++a_step)
        {   // VESTIGIAL for now ('vestigial' eh ? ;) )

            std::cout << "\n\n I should be skipped what are you doing here lalalalalalala *!**!*!*!*!*!* \n\n";

            system.solve();
            system.postprocess();
            ErrorVector error;
            AutoPtr<ErrorEstimator> error_estimator;

            // To solve to a tolerance in this problem we
            // need a better estimator than Kelly
            if (global_tolerance != 0.)
            {
                // We can't adapt to both a tolerance and a mesh
                // size at once
                libmesh_assert_equal_to (nelem_target, 0);

                UniformRefinementEstimator *u =
                    new UniformRefinementEstimator;

                // The lid-driven cavity problem isn't in H1, so
                // lets estimate L2 error
                u->error_norm = L2;

                error_estimator.reset(u);
            }
            else
            {
                // If we aren't adapting to a tolerance we need a
                // target mesh size
                libmesh_assert_greater (nelem_target, 0);

                // Kelly is a lousy estimator to use for a problem
                // not in H1 - if we were doing more than a few
                // timesteps we'd need to turn off or limit the
                // maximum level of our adaptivity eventually
                error_estimator.reset(new KellyErrorEstimator);
            }

            // Calculate error
            std::vector<Real> weights(9,1.0);  // based on u, v, p, c, their adjoints, and source parameter

            // Keep the same default norm type.
            std::vector<FEMNormType>
            norms(1, error_estimator->error_norm.type(0));
            error_estimator->error_norm = SystemNorm(norms, weights);

            error_estimator->estimate_error(system, error);

            // Print out status at each adaptive step.
            Real global_error = error.l2_norm();
            std::cout << "Adaptive step " << a_step << ": " << std::endl;
            if (global_tolerance != 0.)
                std::cout << "Global_error = " << global_error
                          << std::endl;
            if (global_tolerance != 0.)
                std::cout << "Worst element error = " << error.maximum()
                          << ", mean = " << error.mean() << std::endl;

            if (global_tolerance != 0.)
            {
                // If we've reached our desired tolerance, we
                // don't need any more adaptive steps
                if (global_error < global_tolerance)
                    break;
                mesh_refinement.flag_elements_by_error_tolerance(error);
            }
            else
            {
                // If flag_elements_by_nelem_target returns true, this
                // should be our last adaptive step.
                if (mesh_refinement.flag_elements_by_nelem_target(error))
                {
                    mesh_refinement.refine_and_coarsen_elements();
                    equation_systems.reinit();
                    a_step = max_adaptivesteps;
                    break;
                }
            }

            // Carry out the adaptive mesh refinement/coarsening
            mesh_refinement.refine_and_coarsen_elements();
            equation_systems.reinit();

            std::cout << "Refined mesh to "
                      << mesh.n_active_elem()
                      << " active elements and "
                      << equation_systems.n_active_dofs()
                      << " active dofs." << std::endl;
        } // End loop over adaptive steps

        // Do one last solve if necessary
        if (a_step == max_adaptivesteps)
        {
            QoISet qois;
            std::vector<unsigned int> qoi_indices;

            qoi_indices.push_back(0);
            qois.add_indices(qoi_indices);

            qois.set_weight(0, 1.0);

            system.assemble_qoi_sides = true; //QoI doesn't involve sides

            std::cout << "\n~*~*~*~*~*~*~*~*~ adjoint solve start ~*~*~*~*~*~*~*~*~\n" << std::endl;
            std::pair<unsigned int, Real> adjsolve = system.adjoint_solve();
            std::cout << "number of iterations to solve adjoint: " << adjsolve.first << std::endl;
            std::cout << "final residual of adjoint solve: " << adjsolve.second << std::endl;
            std::cout << "\n~*~*~*~*~*~*~*~*~ adjoint solve end ~*~*~*~*~*~*~*~*~" << std::endl;

            NumericVector<Number> &dual_solution = system.get_adjoint_solution(0);
            NumericVector<Number> &primal_solution = *system.solution;

            primal_solution.swap(dual_solution);
            ExodusII_IO(mesh).write_timestep("super_adjoint.exo",
                                             equation_systems,
                                             1, /* This number indicates how many time steps
	                                       are being written to the file */
                                             system.time);
            primal_solution.swap(dual_solution);

            system.assemble(); //overwrite residual read in from psiLF solve

            // The total error estimate
            system.postprocess(); //to compute M_HF(psiLF) and M_LF(psiLF) terms
            Real QoI_error_estimate = (-0.5*(system.rhs)->dot(dual_solution)) + system.get_MHF_psiLF() - system.get_MLF_psiLF();
            std::cout << "\n\n 0.5*M'_HF(psiLF)(superadj): " << std::setprecision(17) << 0.5*(system.rhs)->dot(dual_solution) << "\n";
            std::cout << " M_HF(psiLF): " << std::setprecision(17) << system.get_MHF_psiLF() << "\n";
            std::cout << " M_LF(psiLF): " << std::setprecision(17) << system.get_MLF_psiLF() << "\n";
            std::cout << "\n\n Residual L2 norm: " << system.calculate_norm(*system.rhs, L2) << "\n";
            std::cout << " Residual discrete L2 norm: " << system.calculate_norm(*system.rhs, DISCRETE_L2) << "\n";
            std::cout << " Super-adjoint L2 norm: " << system.calculate_norm(dual_solution, L2) << "\n";
            std::cout << " Super-adjoint discrete L2 norm: " << system.calculate_norm(dual_solution, DISCRETE_L2) << "\n";
            std::cout << "\n\n QoI error estimate: " << std::setprecision(17) << QoI_error_estimate << "\n\n";

            //DEBUG
            std::cout << "\n------------ herp derp ------------" << std::endl;
            //libMesh::out.precision(16);
            //dual_solution.print();
            //system.get_adjoint_rhs().print();

            AutoPtr<NumericVector<Number> > adjresid = system.solution->clone();
            (system.matrix)->vector_mult(*adjresid,system.get_adjoint_solution(0));
            SparseMatrix<Number>& adjmat = *system.matrix;
            (system.matrix)->get_transpose(adjmat);
            adjmat.vector_mult(*adjresid,system.get_adjoint_solution(0));
            //std::cout << "******************** matrix-superadj product (libmesh) ************************" << std::endl;
            //adjresid->print();
            adjresid->add(-1.0, system.get_adjoint_rhs(0));
            //std::cout << "******************** superadjoint system residual (libmesh) ***********************" << std::endl;
            //adjresid->print();
            std::cout << "\n\nadjoint system residual (discrete L2): " << system.calculate_norm(*adjresid,DISCRETE_L2) << std::endl;
            std::cout << "adjoint system residual (L2, all): " << system.calculate_norm(*adjresid,L2) << std::endl;
            std::cout << "adjoint system residual (L2, 0): " << system.calculate_norm(*adjresid,0,L2) << std::endl;
            std::cout << "adjoint system residual (L2, 1): " << system.calculate_norm(*adjresid,1,L2) << std::endl;
            std::cout << "adjoint system residual (L2, 2): " << system.calculate_norm(*adjresid,2,L2) << std::endl;
            std::cout << "adjoint system residual (L2, 3): " << system.calculate_norm(*adjresid,3,L2) << std::endl;
            std::cout << "adjoint system residual (L2, 4): " << system.calculate_norm(*adjresid,4,L2) << std::endl;
            std::cout << "adjoint system residual (L2, 5): " << system.calculate_norm(*adjresid,5,L2) << std::endl;
            /*
            	AutoPtr<NumericVector<Number> > sadj_matlab = system.solution->clone();
            	AutoPtr<NumericVector<Number> > adjresid_matlab = system.solution->clone();
            	if(FILE *fp=fopen("superadj_matlab.txt","r")){
              	Real value;
              	int counter = 0;
              	int flag = 1;
              	while(flag != -1){
              		flag = fscanf(fp,"%lf",&value);
              		if(flag != -1){
            				sadj_matlab->set(counter, value);
            				counter += 1;
              		}
              	}
              	fclose(fp);
            	}
            	(system.matrix)->vector_mult(*adjresid_matlab,*sadj_matlab);
            	//std::cout << "******************** matrix-superadj product (matlab) ***********************" << std::endl;
            	//adjresid_matlab->print();
            	adjresid_matlab->add(-1.0, system.get_adjoint_rhs(0));
            	//std::cout << "******************** superadjoint system residual (matlab) ***********************" << std::endl;
            	//adjresid_matlab->print();
            	std::cout << "\n\nmatlab import adjoint system residual (discrete L2): " << system.calculate_norm(*adjresid_matlab,DISCRETE_L2) << "\n" << std::endl;
            */
            /*
            	AutoPtr<NumericVector<Number> > sadj_fwd_hack = system.solution->clone();
            	AutoPtr<NumericVector<Number> > adjresid_fwd_hack = system.solution->clone();
            	if(FILE *fp=fopen("superadj_forward_hack.txt","r")){
              	Real value;
              	int counter = 0;
              	int flag = 1;
              	while(flag != -1){
              		flag = fscanf(fp,"%lf",&value);
              		if(flag != -1){
            				sadj_fwd_hack->set(counter, value);
            				counter += 1;
              		}
              	}
              	fclose(fp);
            	}
            	(system.matrix)->vector_mult(*adjresid_fwd_hack,*sadj_fwd_hack);
            	//std::cout << "******************** matrix-superadj product (fwd_hack) ***********************" << std::endl;
            	//adjresid_fwd_hack->print();
            	adjresid_fwd_hack->add(-1.0, system.get_adjoint_rhs(0));
            	//std::cout << "******************** superadjoint system residual (fwd_hack) ***********************" << std::endl;
            	//adjresid_fwd_hack->print();
            	std::cout << "\n\nfwd_hack import adjoint system residual (discrete L2): " << system.calculate_norm(*adjresid_fwd_hack,DISCRETE_L2) << "\n" << std::endl;
            	std::cout << "fwd_hack adjoint system residual (L2, 0): " << system.calculate_norm(*adjresid_fwd_hack,0,L2) << std::endl;
            	std::cout << "fwd_hack adjoint system residual (L2, 1): " << system.calculate_norm(*adjresid_fwd_hack,1,L2) << std::endl;
            	std::cout << "fwd_hack adjoint system residual (L2, 2): " << system.calculate_norm(*adjresid_fwd_hack,2,L2) << std::endl;
            	std::cout << "fwd_hack adjoint system residual (L2, 3): " << system.calculate_norm(*adjresid_fwd_hack,3,L2) << std::endl;
            	std::cout << "fwd_hack adjoint system residual (L2, 4): " << system.calculate_norm(*adjresid_fwd_hack,4,L2) << std::endl;
            	std::cout << "fwd_hack adjoint system residual (L2, 5): " << system.calculate_norm(*adjresid_fwd_hack,5,L2) << std::endl;
            */
            //std::cout << "************************ system.matrix ***********************" << std::endl;
            //system.matrix->print();

            std::cout << "\n------------ herp derp ------------" << std::endl;

            // The cell wise breakdown
            ErrorVector cell_wise_error;
            cell_wise_error.resize((system.rhs)->size());
            for(unsigned int i = 0; i < (system.rhs)->size() ; i++)
            {
                if(i < system.get_mesh().n_elem())
                    cell_wise_error[i] = fabs(-0.5*((system.rhs)->el(i) * dual_solution(i))
                                              + system.get_MHF_psiLF(i) - system.get_MLF_psiLF(i));
                else
                    cell_wise_error[i] = fabs(-0.5*((system.rhs)->el(i) * dual_solution(i)));

                /*csv from 'save data' from gmv output gives a few values at each node point (value
                for every element that shares that node), yet paraview display only seems to show one
                of them -> the value in an element is given at each of the nodes that it has, hence the
                repetition; what is displayed in paraview is each element's value; even though MHF_psiLF
                and MLF_psiLF are stored by element this seems to give elemental contributions that
                agree with if we had taken the superadj-residual dot product by integrating over elements*/

                /*at higher mesh resolutions and lower k, weird-looking artifacts start to appear and
                it no longer agrees with output from manual integration of superadj-residual...*/
            }
            // Plot it
            std::ostringstream error_gmv;
            error_gmv << "error.gmv";
            cell_wise_error.plot_error(error_gmv.str(), equation_systems.get_mesh());

            //alternate element-wise breakdown, outputed as values matched to element centroids; for matlab plotz
            primal_solution.swap(dual_solution);
            system.postprocess(1);
            primal_solution.swap(dual_solution);
            system.postprocess(2);
            std::cout << "\n\n -0.5*M'_HF(psiLF)(superadj): " << std::setprecision(17) << system.get_half_adj_weighted_resid() << "\n";
            primal_solution.swap(dual_solution);

            std::string write_error_here = infileForMesh("error_est_output_file", "error_est_breakdown.dat");
            std::ofstream output(write_error_here);
            for(unsigned int i = 0 ; i < system.get_mesh().n_elem(); i++) {
                Point elem_cent = system.get_mesh().elem(i)->centroid();
                if(output.is_open()) {
                    output << elem_cent(0) << " " << elem_cent(1) << " "
                           << fabs(system.get_half_adj_weighted_resid(i) + system.get_MHF_psiLF(i) - system.get_MLF_psiLF(i)) << "\n";
                }
            }
            output.close();

        } // End if at max adaptive steps

#ifdef LIBMESH_HAVE_EXODUS_API
        // Write out this timestep if we're requested to
        if ((t_step+1)%write_interval == 0)
        {
            std::ostringstream file_name;
            /*
                // We write the file in the ExodusII format.
                file_name << "out_"
                          << std::setw(3)
                          << std::setfill('0')
                          << std::right
                          << t_step+1
                          << ".e";
            			//this should write out the primal which should be the same as what's read in...
            			ExodusII_IO(mesh).write_timestep(file_name.str(),
            							                        equation_systems,
            							                        1, //number of time steps written to file
            							                        system.time);
            */
        }
#endif // #ifdef LIBMESH_HAVE_EXODUS_API
    }

    // All done.
    return 0;

} //end main
Ejemplo n.º 2
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;
}
Ejemplo n.º 3
0
// The main program.
int main (int argc, char** argv)
{
  // Initialize libMesh.
  LibMeshInit init (argc, argv);

  // This example requires a linear solver package.
  libmesh_example_requires(libMesh::default_solver_package() != INVALID_SOLVER_PACKAGE,
                           "--enable-petsc, --enable-trilinos, or --enable-eigen");

  // Skip adaptive examples on a non-adaptive libMesh build
#ifndef LIBMESH_ENABLE_AMR
  libmesh_example_requires(false, "--enable-amr");
#else

  // This doesn't converge with Eigen BICGSTAB for some reason...
  libmesh_example_requires(libMesh::default_solver_package() != EIGEN_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);

  // Skip this default-2D example if libMesh was compiled as 1D-only.
  libmesh_example_requires(2 <= LIBMESH_DIM, "2D support");

  // 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
  std::unique_ptr<MeshRefinement> mesh_refinement =
    build_mesh_refinement(mesh, param);

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

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

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

  // Build the FEMSystem
  LaplaceSystem & system = equation_systems.add_system<LaplaceSystem> ("LaplaceSystem");

  // Set its parameters
  set_system_parameters(system, param);

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

        // 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 unnecessarily 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", param);

        // 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", param);

        // Swap back again
        primal_solution.swap(dual_solution_1);

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

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

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

        libMesh::out << "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 effectivity indices (estimated error/true error)
        libMesh::out << "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;

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

        // For refinement purposes we need to sort by error
        // *magnitudes*, but AdjointRefinement gives us signed errors.
        if (!param.refine_uniformly)
          for (std::size_t i=0; i<QoI_elementwise_error.size(); i++)
            if (QoI_elementwise_error[i] != 0.)
              QoI_elementwise_error[i] = std::abs(QoI_elementwise_error[i]);

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

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

        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 unnecessarily 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", param);

        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", param);

        primal_solution.swap(dual_solution_1);

        libMesh::out << "Adaptive step "
                     << a_step
                     << ", we have "
                     << mesh.n_active_elem()
                     << " active elements and "
                     << equation_systems.n_active_dofs()
                     << " active dofs."
                     << std::endl;

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

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

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

        libMesh::out << "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 effectivity indices (estimated error/true error)
        libMesh::out << "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;

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

        // Hard coded assert to ensure that the actual numbers we are getting are what they should be

        // The effectivity index isn't exactly reproducible at single precision
        // libmesh_assert_less(std::abs(std::abs(adjoint_refinement_error_estimator->get_global_QoI_error_estimate(0)) / std::abs(QoI_0_computed - QoI_0_exact) - 0.84010976704434637), 1.e-5);
        // libmesh_assert_less(std::abs(std::abs(adjoint_refinement_error_estimator->get_global_QoI_error_estimate(1)) / std::abs(QoI_1_computed - QoI_1_exact) - 0.48294428289950514), 1.e-5);

        // But the effectivity indices should always be sane
        libmesh_assert_less(std::abs(adjoint_refinement_error_estimator->get_global_QoI_error_estimate(0)) / std::abs(QoI_0_computed - QoI_0_exact), 2.5);
        libmesh_assert_greater(std::abs(adjoint_refinement_error_estimator->get_global_QoI_error_estimate(0)) / std::abs(QoI_0_computed - QoI_0_exact), .4);
        libmesh_assert_less(std::abs(adjoint_refinement_error_estimator->get_global_QoI_error_estimate(1)) / std::abs(QoI_1_computed - QoI_1_exact), 2.5);
        libmesh_assert_greater(std::abs(adjoint_refinement_error_estimator->get_global_QoI_error_estimate(1)) / std::abs(QoI_1_computed - QoI_1_exact), .4);

        // And the computed errors should still be low
        libmesh_assert_less(std::abs(QoI_0_computed - QoI_0_exact), 2e-4);
        libmesh_assert_less(std::abs(QoI_1_computed - QoI_1_exact), 2e-4);
      }
  }

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

#endif // #ifndef LIBMESH_ENABLE_AMR

  // All done.
  return 0;
}
Ejemplo n.º 4
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

  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);

  // Skip this default-2D example if libMesh was compiled as 1D-only.
  libmesh_example_assert(2 <= LIBMESH_DIM, "2D support");

  // 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 << "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;

        // 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);
        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;
      }
  }

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

#endif // #ifndef LIBMESH_ENABLE_AMR

  // All done.
  return 0;
}
Ejemplo n.º 5
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;
}
Ejemplo n.º 6
0
void ImplicitSystem::qoi_parameter_hessian_vector_product (const QoISet & qoi_indices,
                                                           const ParameterVector & parameters_in,
                                                           const ParameterVector & vector,
                                                           SensitivityData & sensitivities)
{
  // We currently get partial derivatives via finite differencing
  const Real delta_p = TOLERANCE;

  ParameterVector & parameters =
    const_cast<ParameterVector &>(parameters_in);

  // We'll use a single temporary vector for matrix-vector-vector products
  std::unique_ptr<NumericVector<Number>> tempvec = this->solution->zero_clone();

  const unsigned int Np = cast_int<unsigned int>
    (parameters.size());
  const unsigned int Nq = this->n_qois();

  // For each quantity of interest q, the parameter sensitivity
  // Hessian is defined as q''_{kl} = {d^2 q}/{d p_k d p_l}.
  // Given a vector of parameter perturbation weights w_l, this
  // function evaluates the hessian-vector product sum_l(q''_{kl}*w_l)
  //
  // We calculate it from values and partial derivatives of the
  // quantity of interest function Q, solution u, adjoint solution z,
  // parameter sensitivity adjoint solutions z^l, and residual R, as:
  //
  // sum_l(q''_{kl}*w_l) =
  // sum_l(w_l * Q''_{kl}) + Q''_{uk}(u)*(sum_l(w_l u'_l)) -
  // R'_k(u, sum_l(w_l*z^l)) - R'_{uk}(u,z)*(sum_l(w_l u'_l) -
  // sum_l(w_l*R''_{kl}(u,z))
  //
  // See the adjoints model document for more details.

  // We first do an adjoint solve to get z for each quantity of
  // interest
  // if we havent already or dont have an initial condition for the adjoint
  if (!this->is_adjoint_already_solved())
    {
      this->adjoint_solve(qoi_indices);
    }

  // Get ready to fill in sensitivities:
  sensitivities.allocate_data(qoi_indices, *this, parameters);

  // We can't solve for all the solution sensitivities u'_l or for all
  // of the parameter sensitivity adjoint solutions z^l without
  // requiring O(Nq*Np) linear solves.  So we'll solve directly for their
  // weighted sum - this is just O(Nq) solves.

  // First solve for sum_l(w_l u'_l).
  this->weighted_sensitivity_solve(parameters, vector);

  // Then solve for sum_l(w_l z^l).
  this->weighted_sensitivity_adjoint_solve(parameters, vector, qoi_indices);

  for (unsigned int k=0; k != Np; ++k)
    {
      // We approximate sum_l(w_l * Q''_{kl}) with a central
      // differencing perturbation:
      // sum_l(w_l * Q''_{kl}) ~=
      // (Q(p + dp*w_l*e_l + dp*e_k) - Q(p - dp*w_l*e_l + dp*e_k) -
      // Q(p + dp*w_l*e_l - dp*e_k) + Q(p - dp*w_l*e_l - dp*e_k))/(4*dp^2)

      // The sum(w_l*R''_kl) term requires the same sort of perturbation,
      // and so we subtract it in at the same time:
      // sum_l(w_l * R''_{kl}) ~=
      // (R(p + dp*w_l*e_l + dp*e_k) - R(p - dp*w_l*e_l + dp*e_k) -
      // R(p + dp*w_l*e_l - dp*e_k) + R(p - dp*w_l*e_l - dp*e_k))/(4*dp^2)

      ParameterVector oldparameters, parameterperturbation;
      parameters.deep_copy(oldparameters);
      vector.deep_copy(parameterperturbation);
      parameterperturbation *= delta_p;
      parameters += parameterperturbation;

      Number old_parameter = *parameters[k];

      *parameters[k] = old_parameter + delta_p;
      this->assemble_qoi(qoi_indices);
      this->assembly(true, false, true);
      this->rhs->close();
      std::vector<Number> partial2q_term = this->qoi;
      std::vector<Number> partial2R_term(this->n_qois());
      for (unsigned int i=0; i != Nq; ++i)
        if (qoi_indices.has_index(i))
          partial2R_term[i] = this->rhs->dot(this->get_adjoint_solution(i));

      *parameters[k] = old_parameter - delta_p;
      this->assemble_qoi(qoi_indices);
      this->assembly(true, false, true);
      this->rhs->close();
      for (unsigned int i=0; i != Nq; ++i)
        if (qoi_indices.has_index(i))
          {
            partial2q_term[i] -= this->qoi[i];
            partial2R_term[i] -= this->rhs->dot(this->get_adjoint_solution(i));
          }

      oldparameters.value_copy(parameters);
      parameterperturbation *= -1.0;
      parameters += parameterperturbation;

      // Re-center old_parameter, which may be affected by vector
      old_parameter = *parameters[k];

      *parameters[k] = old_parameter + delta_p;
      this->assemble_qoi(qoi_indices);
      this->assembly(true, false, true);
      this->rhs->close();
      for (unsigned int i=0; i != Nq; ++i)
        if (qoi_indices.has_index(i))
          {
            partial2q_term[i] -= this->qoi[i];
            partial2R_term[i] -= this->rhs->dot(this->get_adjoint_solution(i));
          }

      *parameters[k] = old_parameter - delta_p;
      this->assemble_qoi(qoi_indices);
      this->assembly(true, false, true);
      this->rhs->close();
      for (unsigned int i=0; i != Nq; ++i)
        if (qoi_indices.has_index(i))
          {
            partial2q_term[i] += this->qoi[i];
            partial2R_term[i] += this->rhs->dot(this->get_adjoint_solution(i));
          }

      for (unsigned int i=0; i != Nq; ++i)
        if (qoi_indices.has_index(i))
          {
            partial2q_term[i] /= (4. * delta_p * delta_p);
            partial2R_term[i] /= (4. * delta_p * delta_p);
          }

      for (unsigned int i=0; i != Nq; ++i)
        if (qoi_indices.has_index(i))
          sensitivities[i][k] = partial2q_term[i] - partial2R_term[i];

      // We get (partial q / partial u), R, and
      // (partial R / partial u) from the user, but centrally
      // difference to get q_uk, R_k, and R_uk terms:
      // (partial R / partial k)
      // R_k*sum(w_l*z^l) = (R(p+dp*e_k)*sum(w_l*z^l) - R(p-dp*e_k)*sum(w_l*z^l))/(2*dp)
      // (partial^2 q / partial u partial k)
      // q_uk = (q_u(p+dp*e_k) - q_u(p-dp*e_k))/(2*dp)
      // (partial^2 R / partial u partial k)
      // R_uk*z*sum(w_l*u'_l) = (R_u(p+dp*e_k)*z*sum(w_l*u'_l) - R_u(p-dp*e_k)*z*sum(w_l*u'_l))/(2*dp)

      // To avoid creating Nq temporary vectors for q_uk or R_uk, we add
      // subterms to the sensitivities output one by one.
      //
      // FIXME: this is probably a bad order of operations for
      // controlling floating point error.

      *parameters[k] = old_parameter + delta_p;
      this->assembly(true, true);
      this->rhs->close();
      this->matrix->close();
      this->assemble_qoi_derivative(qoi_indices,
                                    /* include_liftfunc = */ true,
                                    /* apply_constraints = */ false);

      this->matrix->vector_mult(*tempvec, this->get_weighted_sensitivity_solution());

      for (unsigned int i=0; i != Nq; ++i)
        if (qoi_indices.has_index(i))
          {
            this->get_adjoint_rhs(i).close();
            sensitivities[i][k] += (this->get_adjoint_rhs(i).dot(this->get_weighted_sensitivity_solution()) -
                                    this->rhs->dot(this->get_weighted_sensitivity_adjoint_solution(i)) -
                                    this->get_adjoint_solution(i).dot(*tempvec)) / (2.*delta_p);
          }

      *parameters[k] = old_parameter - delta_p;
      this->assembly(true, true);
      this->rhs->close();
      this->matrix->close();
      this->assemble_qoi_derivative(qoi_indices,
                                    /* include_liftfunc = */ true,
                                    /* apply_constraints = */ false);

      this->matrix->vector_mult(*tempvec, this->get_weighted_sensitivity_solution());

      for (unsigned int i=0; i != Nq; ++i)
        if (qoi_indices.has_index(i))
          {
            this->get_adjoint_rhs(i).close();
            sensitivities[i][k] += (-this->get_adjoint_rhs(i).dot(this->get_weighted_sensitivity_solution()) +
                                    this->rhs->dot(this->get_weighted_sensitivity_adjoint_solution(i)) +
                                    this->get_adjoint_solution(i).dot(*tempvec)) / (2.*delta_p);
          }
    }

  // All parameters have been reset.
  // Don't leave the qoi or system changed - principle of least
  // surprise.
  this->assembly(true, true);
  this->rhs->close();
  this->matrix->close();
  this->assemble_qoi(qoi_indices);
}
Ejemplo n.º 7
0
void ImplicitSystem::forward_qoi_parameter_sensitivity (const QoISet & qoi_indices,
                                                        const ParameterVector & parameters_in,
                                                        SensitivityData & sensitivities)
{
  ParameterVector & parameters =
    const_cast<ParameterVector &>(parameters_in);

  const unsigned int Np = cast_int<unsigned int>
    (parameters.size());
  const unsigned int Nq = this->n_qois();

  // An introduction to the problem:
  //
  // Residual R(u(p),p) = 0
  // partial R / partial u = J = system matrix
  //
  // This implies that:
  // d/dp(R) = 0
  // (partial R / partial p) +
  // (partial R / partial u) * (partial u / partial p) = 0

  // We first solve for (partial u / partial p) for each parameter:
  // J * (partial u / partial p) = - (partial R / partial p)

  this->sensitivity_solve(parameters);

  // Get ready to fill in sensitivities:
  sensitivities.allocate_data(qoi_indices, *this, parameters);

  // We use the identity:
  // dq/dp = (partial q / partial p) + (partial q / partial u) *
  //         (partial u / partial p)

  // We get (partial q / partial u) from the user
  this->assemble_qoi_derivative(qoi_indices,
                                /* include_liftfunc = */ true,
                                /* apply_constraints = */ false);

  // We don't need these to be closed() in this function, but libMesh
  // standard practice is to have them closed() by the time the
  // function exits
  for (unsigned int i=0; i != this->n_qois(); ++i)
    if (qoi_indices.has_index(i))
      this->get_adjoint_rhs(i).close();

  for (unsigned int j=0; j != Np; ++j)
    {
      // We currently get partial derivatives via central differencing

      // (partial q / partial p) ~= (q(p+dp)-q(p-dp))/(2*dp)

      Number old_parameter = *parameters[j];

      const Real delta_p =
        TOLERANCE * std::max(std::abs(old_parameter), 1e-3);

      *parameters[j] = old_parameter - delta_p;
      this->assemble_qoi(qoi_indices);
      std::vector<Number> qoi_minus = this->qoi;

      *parameters[j] = old_parameter + delta_p;
      this->assemble_qoi(qoi_indices);
      std::vector<Number> & qoi_plus = this->qoi;

      std::vector<Number> partialq_partialp(Nq, 0);
      for (unsigned int i=0; i != Nq; ++i)
        if (qoi_indices.has_index(i))
          partialq_partialp[i] = (qoi_plus[i] - qoi_minus[i]) / (2.*delta_p);

      // Don't leave the parameter changed
      *parameters[j] = old_parameter;

      for (unsigned int i=0; i != Nq; ++i)
        if (qoi_indices.has_index(i))
          sensitivities[i][j] = partialq_partialp[i] +
            this->get_adjoint_rhs(i).dot(this->get_sensitivity_solution(j));
    }

  // All parameters have been reset.
  // We didn't cache the original rhs or matrix for memory reasons,
  // but we can restore them to a state consistent solution -
  // principle of least surprise.
  this->assembly(true, true);
  this->rhs->close();
  this->matrix->close();
  this->assemble_qoi(qoi_indices);
}
Ejemplo n.º 8
0
void ImplicitSystem::adjoint_qoi_parameter_sensitivity (const QoISet & qoi_indices,
                                                        const ParameterVector & parameters_in,
                                                        SensitivityData & sensitivities)
{
  ParameterVector & parameters =
    const_cast<ParameterVector &>(parameters_in);

  const unsigned int Np = cast_int<unsigned int>
    (parameters.size());
  const unsigned int Nq = this->n_qois();

  // An introduction to the problem:
  //
  // Residual R(u(p),p) = 0
  // partial R / partial u = J = system matrix
  //
  // This implies that:
  // d/dp(R) = 0
  // (partial R / partial p) +
  // (partial R / partial u) * (partial u / partial p) = 0

  // We first do an adjoint solve:
  // J^T * z = (partial q / partial u)
  // if we havent already or dont have an initial condition for the adjoint
  if (!this->is_adjoint_already_solved())
    {
      this->adjoint_solve(qoi_indices);
    }

  this->assemble_residual_derivatives(parameters_in);

  // Get ready to fill in sensitivities:
  sensitivities.allocate_data(qoi_indices, *this, parameters);

  // We use the identities:
  // dq/dp = (partial q / partial p) + (partial q / partial u) *
  //         (partial u / partial p)
  // dq/dp = (partial q / partial p) + (J^T * z) *
  //         (partial u / partial p)
  // dq/dp = (partial q / partial p) + z * J *
  //         (partial u / partial p)

  // Leading to our final formula:
  // dq/dp = (partial q / partial p) - z * (partial R / partial p)

  // In the case of adjoints with heterogenous Dirichlet boundary
  // function phi, where
  // q :=  S(u) - R(u,phi)
  // the final formula works out to:
  // dq/dp = (partial S / partial p) - z * (partial R / partial p)
  // Because we currently have no direct access to
  // (partial S / partial p), we use the identity
  // (partial S / partial p) = (partial q / partial p) +
  //                           phi * (partial R / partial p)
  // to derive an equivalent equation:
  // dq/dp = (partial q / partial p) - (z-phi) * (partial R / partial p)

  // Since z-phi degrees of freedom are zero for constrained indices,
  // we can use the same constrained -(partial R / partial p) that we
  // use for forward sensitivity solves, taking into account the
  // differing sign convention.
  //
  // Since that vector is constrained, its constrained indices are
  // zero, so its product with phi is zero, so we can neglect the
  // evaluation of phi terms.

  for (unsigned int j=0; j != Np; ++j)
    {
      // We currently get partial derivatives via central differencing

      // (partial q / partial p) ~= (q(p+dp)-q(p-dp))/(2*dp)
      // (partial R / partial p) ~= (rhs(p+dp) - rhs(p-dp))/(2*dp)

      Number old_parameter = *parameters[j];

      const Real delta_p =
        TOLERANCE * std::max(std::abs(old_parameter), 1e-3);

      *parameters[j] = old_parameter - delta_p;
      this->assemble_qoi(qoi_indices);
      std::vector<Number> qoi_minus = this->qoi;

      NumericVector<Number> & neg_partialR_partialp = this->get_sensitivity_rhs(j);

      *parameters[j] = old_parameter + delta_p;
      this->assemble_qoi(qoi_indices);
      std::vector<Number> & qoi_plus = this->qoi;

      std::vector<Number> partialq_partialp(Nq, 0);
      for (unsigned int i=0; i != Nq; ++i)
        if (qoi_indices.has_index(i))
          partialq_partialp[i] = (qoi_plus[i] - qoi_minus[i]) / (2.*delta_p);

      // Don't leave the parameter changed
      *parameters[j] = old_parameter;

      for (unsigned int i=0; i != Nq; ++i)
        if (qoi_indices.has_index(i))
          sensitivities[i][j] = partialq_partialp[i] +
            neg_partialR_partialp.dot(this->get_adjoint_solution(i));
    }

  // All parameters have been reset.
  // Reset the original qoi.

  this->assemble_qoi(qoi_indices);
}
Ejemplo n.º 9
0
std::pair<unsigned int, Real>
ImplicitSystem::weighted_sensitivity_adjoint_solve (const ParameterVector & parameters_in,
                                                    const ParameterVector & weights,
                                                    const QoISet & qoi_indices)
{
  // Log how long the linear solve takes.
  LOG_SCOPE("weighted_sensitivity_adjoint_solve()", "ImplicitSystem");

  // We currently get partial derivatives via central differencing
  const Real delta_p = TOLERANCE;

  ParameterVector & parameters =
    const_cast<ParameterVector &>(parameters_in);

  // The forward system should now already be solved.
  // The adjoint system should now already be solved.
  // Now we're assembling a weighted sum of adjoint-adjoint systems:
  //
  // dR/du (u, sum_l(w_l*z^l)) = sum_l(w_l*(Q''_ul - R''_ul (u, z)))

  // FIXME: The derivation here does not yet take adjoint boundary
  // conditions into account.
  for (unsigned int i=0; i != this->n_qois(); ++i)
    if (qoi_indices.has_index(i))
      libmesh_assert(!this->get_dof_map().has_adjoint_dirichlet_boundaries(i));

  // We'll assemble the rhs first, because the R'' term will require
  // perturbing the jacobian

  // We'll use temporary rhs vectors, because we haven't (yet) found
  // any good reasons why users might want to save these:

  std::vector<std::unique_ptr<NumericVector<Number>>> temprhs(this->n_qois());
  for (unsigned int i=0; i != this->n_qois(); ++i)
    if (qoi_indices.has_index(i))
      temprhs[i] = this->rhs->zero_clone();

  // We approximate the _l partial derivatives via a central
  // differencing perturbation in the w_l direction:
  //
  // sum_l(w_l*v_l) ~= (v(p + dp*w_l*e_l) - v(p - dp*w_l*e_l))/(2*dp)

  // PETSc doesn't implement SGEMX, so neither does NumericVector,
  // so we want to avoid calculating f -= R'*z.  We'll thus evaluate
  // the above equation by first adding -v(p+dp...), then multiplying
  // the intermediate result vectors by -1, then adding -v(p-dp...),
  // then finally dividing by 2*dp.

  ParameterVector oldparameters, parameterperturbation;
  parameters.deep_copy(oldparameters);
  weights.deep_copy(parameterperturbation);
  parameterperturbation *= delta_p;
  parameters += parameterperturbation;

  this->assembly(false, true);
  this->matrix->close();

  // Take the discrete adjoint, so that we can calculate R_u(u,z) with
  // a matrix-vector product of R_u and z.
  matrix->get_transpose(*matrix);

  this->assemble_qoi_derivative(qoi_indices,
                                /* include_liftfunc = */ false,
                                /* apply_constraints = */ true);
  for (unsigned int i=0; i != this->n_qois(); ++i)
    if (qoi_indices.has_index(i))
      {
        this->get_adjoint_rhs(i).close();
        *(temprhs[i]) -= this->get_adjoint_rhs(i);
        this->matrix->vector_mult_add(*(temprhs[i]), this->get_adjoint_solution(i));
        *(temprhs[i]) *= -1.0;
      }

  oldparameters.value_copy(parameters);
  parameterperturbation *= -1.0;
  parameters += parameterperturbation;

  this->assembly(false, true);
  this->matrix->close();
  matrix->get_transpose(*matrix);

  this->assemble_qoi_derivative(qoi_indices,
                                /* include_liftfunc = */ false,
                                /* apply_constraints = */ true);
  for (unsigned int i=0; i != this->n_qois(); ++i)
    if (qoi_indices.has_index(i))
      {
        this->get_adjoint_rhs(i).close();
        *(temprhs[i]) -= this->get_adjoint_rhs(i);
        this->matrix->vector_mult_add(*(temprhs[i]), this->get_adjoint_solution(i));
        *(temprhs[i]) /= (2.0*delta_p);
      }

  // Finally, assemble the jacobian at the non-perturbed parameter
  // values.  Ignore assemble_before_solve; if we had a good
  // non-perturbed matrix before we've already overwritten it.
  oldparameters.value_copy(parameters);

  // if (this->assemble_before_solve)
  {
    // Build the Jacobian
    this->assembly(false, true);
    this->matrix->close();

    // Take the discrete adjoint
    matrix->get_transpose(*matrix);
  }

  // The weighted adjoint-adjoint problem is linear
  LinearSolver<Number> * linear_solver = this->get_linear_solver();

  // Our iteration counts and residuals will be sums of the individual
  // results
  std::pair<unsigned int, Real> solver_params =
    this->get_linear_solve_parameters();
  std::pair<unsigned int, Real> totalrval = std::make_pair(0,0.0);

  for (unsigned int i=0; i != this->n_qois(); ++i)
    if (qoi_indices.has_index(i))
      {
        const std::pair<unsigned int, Real> rval =
          linear_solver->solve (*matrix, this->add_weighted_sensitivity_adjoint_solution(i),
                                *(temprhs[i]),
                                solver_params.second,
                                solver_params.first);

        totalrval.first  += rval.first;
        totalrval.second += rval.second;
      }

  this->release_linear_solver(linear_solver);

  // The linear solver may not have fit our constraints exactly
#ifdef LIBMESH_ENABLE_CONSTRAINTS
  for (unsigned int i=0; i != this->n_qois(); ++i)
    if (qoi_indices.has_index(i))
      this->get_dof_map().enforce_constraints_exactly
        (*this, &this->get_weighted_sensitivity_adjoint_solution(i),
         /* homogeneous = */ true);
#endif

  return totalrval;
}
Ejemplo n.º 10
0
void ImplicitSystem::qoi_parameter_hessian (const QoISet & qoi_indices,
                                            const ParameterVector & parameters_in,
                                            SensitivityData & sensitivities)
{
  // We currently get partial derivatives via finite differencing
  const Real delta_p = TOLERANCE;

  ParameterVector & parameters =
    const_cast<ParameterVector &>(parameters_in);

  // We'll use one temporary vector for matrix-vector-vector products
  std::unique_ptr<NumericVector<Number>> tempvec = this->solution->zero_clone();

  // And another temporary vector to hold a copy of the true solution
  // so we can safely perturb this->solution.
  std::unique_ptr<NumericVector<Number>> oldsolution = this->solution->clone();

  const unsigned int Np = cast_int<unsigned int>
    (parameters.size());
  const unsigned int Nq = this->n_qois();

  // For each quantity of interest q, the parameter sensitivity
  // Hessian is defined as q''_{kl} = {d^2 q}/{d p_k d p_l}.
  //
  // We calculate it from values and partial derivatives of the
  // quantity of interest function Q, solution u, adjoint solution z,
  // and residual R, as:
  //
  // q''_{kl} =
  // Q''_{kl} + Q''_{uk}(u)*u'_l + Q''_{ul}(u) * u'_k +
  // Q''_{uu}(u)*u'_k*u'_l -
  // R''_{kl}(u,z) -
  // R''_{uk}(u,z)*u'_l - R''_{ul}(u,z)*u'_k -
  // R''_{uu}(u,z)*u'_k*u'_l
  //
  // See the adjoints model document for more details.

  // We first do an adjoint solve to get z for each quantity of
  // interest
  // if we havent already or dont have an initial condition for the adjoint
  if (!this->is_adjoint_already_solved())
    {
      this->adjoint_solve(qoi_indices);
    }

  // And a sensitivity solve to get u_k for each parameter
  this->sensitivity_solve(parameters);

  // Get ready to fill in second derivatives:
  sensitivities.allocate_hessian_data(qoi_indices, *this, parameters);

  for (unsigned int k=0; k != Np; ++k)
    {
      Number old_parameterk = *parameters[k];

      // The Hessian is symmetric, so we just calculate the lower
      // triangle and the diagonal, and we get the upper triangle from
      // the transpose of the lower

      for (unsigned int l=0; l != k+1; ++l)
        {
          // The second partial derivatives with respect to parameters
          // are all calculated via a central finite difference
          // stencil:
          // F''_{kl} ~= (F(p+dp*e_k+dp*e_l) - F(p+dp*e_k-dp*e_l) -
          //              F(p-dp*e_k+dp*e_l) + F(p-dp*e_k-dp*e_l))/(4*dp^2)
          // We will add Q''_{kl}(u) and subtract R''_{kl}(u,z) at the
          // same time.
          //
          // We have to be careful with the perturbations to handle
          // the k=l case

          Number old_parameterl = *parameters[l];

          *parameters[k] += delta_p;
          *parameters[l] += delta_p;
          this->assemble_qoi(qoi_indices);
          this->assembly(true, false, true);
          this->rhs->close();
          std::vector<Number> partial2q_term = this->qoi;
          std::vector<Number> partial2R_term(this->n_qois());
          for (unsigned int i=0; i != Nq; ++i)
            if (qoi_indices.has_index(i))
              partial2R_term[i] = this->rhs->dot(this->get_adjoint_solution(i));

          *parameters[l] -= 2.*delta_p;
          this->assemble_qoi(qoi_indices);
          this->assembly(true, false, true);
          this->rhs->close();
          for (unsigned int i=0; i != Nq; ++i)
            if (qoi_indices.has_index(i))
              {
                partial2q_term[i] -= this->qoi[i];
                partial2R_term[i] -= this->rhs->dot(this->get_adjoint_solution(i));
              }

          *parameters[k] -= 2.*delta_p;
          this->assemble_qoi(qoi_indices);
          this->assembly(true, false, true);
          this->rhs->close();
          for (unsigned int i=0; i != Nq; ++i)
            if (qoi_indices.has_index(i))
              {
                partial2q_term[i] += this->qoi[i];
                partial2R_term[i] += this->rhs->dot(this->get_adjoint_solution(i));
              }

          *parameters[l] += 2.*delta_p;
          this->assemble_qoi(qoi_indices);
          this->assembly(true, false, true);
          this->rhs->close();
          for (unsigned int i=0; i != Nq; ++i)
            if (qoi_indices.has_index(i))
              {
                partial2q_term[i] -= this->qoi[i];
                partial2R_term[i] -= this->rhs->dot(this->get_adjoint_solution(i));
                partial2q_term[i] /= (4. * delta_p * delta_p);
                partial2R_term[i] /= (4. * delta_p * delta_p);
              }

          for (unsigned int i=0; i != Nq; ++i)
            if (qoi_indices.has_index(i))
              {
                Number current_terms = partial2q_term[i] - partial2R_term[i];
                sensitivities.second_derivative(i,k,l) += current_terms;
                if (k != l)
                  sensitivities.second_derivative(i,l,k) += current_terms;
              }

          // Don't leave the parameters perturbed
          *parameters[l] = old_parameterl;
          *parameters[k] = old_parameterk;
        }

      // We get (partial q / partial u) and
      // (partial R / partial u) from the user, but centrally
      // difference to get q_uk and R_uk terms:
      // (partial^2 q / partial u partial k)
      // q_uk*u'_l = (q_u(p+dp*e_k)*u'_l - q_u(p-dp*e_k)*u'_l)/(2*dp)
      // R_uk*z*u'_l = (R_u(p+dp*e_k)*z*u'_l - R_u(p-dp*e_k)*z*u'_l)/(2*dp)
      //
      // To avoid creating Nq temporary vectors, we add these
      // subterms to the sensitivities output one by one.
      //
      // FIXME: this is probably a bad order of operations for
      // controlling floating point error.

      *parameters[k] = old_parameterk + delta_p;
      this->assembly(false, true);
      this->matrix->close();
      this->assemble_qoi_derivative(qoi_indices,
                                    /* include_liftfunc = */ true,
                                    /* apply_constraints = */ false);

      for (unsigned int l=0; l != Np; ++l)
        {
          this->matrix->vector_mult(*tempvec, this->get_sensitivity_solution(l));
          for (unsigned int i=0; i != Nq; ++i)
            if (qoi_indices.has_index(i))
              {
                this->get_adjoint_rhs(i).close();
                Number current_terms =
                  (this->get_adjoint_rhs(i).dot(this->get_sensitivity_solution(l)) -
                   tempvec->dot(this->get_adjoint_solution(i))) / (2.*delta_p);
                sensitivities.second_derivative(i,k,l) += current_terms;

                // We use the _uk terms twice; symmetry lets us reuse
                // these calculations for the _ul terms.

                sensitivities.second_derivative(i,l,k) += current_terms;
              }
        }

      *parameters[k] = old_parameterk - delta_p;
      this->assembly(false, true);
      this->matrix->close();
      this->assemble_qoi_derivative(qoi_indices,
                                    /* include_liftfunc = */ true,
                                    /* apply_constraints = */ false);

      for (unsigned int l=0; l != Np; ++l)
        {
          this->matrix->vector_mult(*tempvec, this->get_sensitivity_solution(l));
          for (unsigned int i=0; i != Nq; ++i)
            if (qoi_indices.has_index(i))
              {
                this->get_adjoint_rhs(i).close();
                Number current_terms =
                  (-this->get_adjoint_rhs(i).dot(this->get_sensitivity_solution(l)) +
                   tempvec->dot(this->get_adjoint_solution(i))) / (2.*delta_p);
                sensitivities.second_derivative(i,k,l) += current_terms;

                // We use the _uk terms twice; symmetry lets us reuse
                // these calculations for the _ul terms.

                sensitivities.second_derivative(i,l,k) += current_terms;
              }
        }

      // Don't leave the parameter perturbed
      *parameters[k] = old_parameterk;

      // Our last remaining terms are -R_uu(u,z)*u_k*u_l and
      // Q_uu(u)*u_k*u_l
      //
      // We take directional central finite differences of R_u and Q_u
      // to approximate these terms, e.g.:
      //
      // Q_uu(u)*u_k ~= (Q_u(u+dp*u_k) - Q_u(u-dp*u_k))/(2*dp)

      *this->solution = this->get_sensitivity_solution(k);
      *this->solution *= delta_p;
      *this->solution += *oldsolution;

      // We've modified solution, so we need to update before calling
      // assembly since assembly may only use current_local_solution
      this->update();
      this->assembly(false, true);
      this->matrix->close();
      this->assemble_qoi_derivative(qoi_indices,
                                    /* include_liftfunc = */ true,
                                    /* apply_constraints = */ false);

      // The Hessian is symmetric, so we just calculate the lower
      // triangle and the diagonal, and we get the upper triangle from
      // the transpose of the lower
      //
      // Note that, because we took the directional finite difference
      // with respect to k and not l, we've added an O(delta_p^2)
      // error to any permutational symmetry in the Hessian...
      for (unsigned int l=0; l != k+1; ++l)
        {
          this->matrix->vector_mult(*tempvec, this->get_sensitivity_solution(l));
          for (unsigned int i=0; i != Nq; ++i)
            if (qoi_indices.has_index(i))
              {
                this->get_adjoint_rhs(i).close();
                Number current_terms =
                  (this->get_adjoint_rhs(i).dot(this->get_sensitivity_solution(l)) -
                   tempvec->dot(this->get_adjoint_solution(i))) / (2.*delta_p);
                sensitivities.second_derivative(i,k,l) += current_terms;
                if (k != l)
                  sensitivities.second_derivative(i,l,k) += current_terms;
              }
        }

      *this->solution = this->get_sensitivity_solution(k);
      *this->solution *= -delta_p;
      *this->solution += *oldsolution;

      // We've modified solution, so we need to update before calling
      // assembly since assembly may only use current_local_solution
      this->update();
      this->assembly(false, true);
      this->matrix->close();
      this->assemble_qoi_derivative(qoi_indices,
                                    /* include_liftfunc = */ true,
                                    /* apply_constraints = */ false);

      for (unsigned int l=0; l != k+1; ++l)
        {
          this->matrix->vector_mult(*tempvec, this->get_sensitivity_solution(l));
          for (unsigned int i=0; i != Nq; ++i)
            if (qoi_indices.has_index(i))
              {
                this->get_adjoint_rhs(i).close();
                Number current_terms =
                  (-this->get_adjoint_rhs(i).dot(this->get_sensitivity_solution(l)) +
                   tempvec->dot(this->get_adjoint_solution(i))) / (2.*delta_p);
                sensitivities.second_derivative(i,k,l) += current_terms;
                if (k != l)
                  sensitivities.second_derivative(i,l,k) += current_terms;
              }
        }

      // Don't leave the solution perturbed
      *this->solution = *oldsolution;
    }

  // All parameters have been reset.
  // Don't leave the qoi or system changed - principle of least
  // surprise.
  // We've modified solution, so we need to update before calling
  // assembly since assembly may only use current_local_solution
  this->update();
  this->assembly(true, true);
  this->rhs->close();
  this->matrix->close();
  this->assemble_qoi(qoi_indices);
}