void MeshRefinement::flag_elements_by_mean_stddev (const ErrorVector & error_per_cell, const Real refine_frac, const Real coarsen_frac, const unsigned int max_l) { // The function arguments are currently just there for // backwards_compatibility if (!_use_member_parameters) { // If the user used non-default parameters, lets warn // that they're deprecated if (refine_frac != 0.3 || coarsen_frac != 0.0 || max_l != libMesh::invalid_uint) libmesh_deprecated(); _refine_fraction = refine_frac; _coarsen_fraction = coarsen_frac; _max_h_level = max_l; } // Get the mean value from the error vector const Real mean = error_per_cell.mean(); // Get the standard deviation. This equals the // square-root of the variance const Real stddev = std::sqrt (error_per_cell.variance()); // Check for valid fractions libmesh_assert_greater_equal (_refine_fraction, 0); libmesh_assert_less_equal (_refine_fraction, 1); libmesh_assert_greater_equal (_coarsen_fraction, 0); libmesh_assert_less_equal (_coarsen_fraction, 1); // The refine and coarsen cutoff const Real refine_cutoff = mean + _refine_fraction * stddev; const Real coarsen_cutoff = std::max(mean - _coarsen_fraction * stddev, 0.); // Loop over the elements and flag them for coarsening or // refinement based on the element error for (auto & elem : _mesh.active_element_ptr_range()) { const dof_id_type id = elem->id(); libmesh_assert_less (id, error_per_cell.size()); const ErrorVectorReal elem_error = error_per_cell[id]; // Possibly flag the element for coarsening ... if (elem_error <= coarsen_cutoff) elem->set_refinement_flag(Elem::COARSEN); // ... or refinement if ((elem_error >= refine_cutoff) && (elem->level() < _max_h_level)) elem->set_refinement_flag(Elem::REFINE); } }
realv Layer::errorWeighting(ErrorVector _deltas, Mat _weights) { if (_deltas.getLength() != ((uint) _weights.rows)) { throw length_error("Layer : Uncorrect length between deltas and weights"); } realv sum = 0; for (uint i = 0; i < _deltas.getLength(); i++) { sum += _deltas[i] * _weights.at<realv>(i, 0); } return sum; }
void ErrorEstimator::estimate_errors(const EquationSystems & equation_systems, ErrorVector & error_per_cell, const std::map<const System *, SystemNorm> & error_norms, const std::map<const System *, const NumericVector<Number> *> * solution_vectors, bool estimate_parent_error) { SystemNorm old_error_norm = this->error_norm; // Sum the error values from each system for (unsigned int s = 0; s != equation_systems.n_systems(); ++s) { ErrorVector system_error_per_cell; const System & sys = equation_systems.get_system(s); if (error_norms.find(&sys) == error_norms.end()) this->error_norm = old_error_norm; else this->error_norm = error_norms.find(&sys)->second; const NumericVector<Number> * solution_vector = nullptr; if (solution_vectors && solution_vectors->find(&sys) != solution_vectors->end()) solution_vector = solution_vectors->find(&sys)->second; this->estimate_error(sys, system_error_per_cell, solution_vector, estimate_parent_error); if (s) { libmesh_assert_equal_to (error_per_cell.size(), system_error_per_cell.size()); for (std::size_t i=0; i != error_per_cell.size(); ++i) error_per_cell[i] += system_error_per_cell[i]; } else error_per_cell = system_error_per_cell; } // Restore our old state before returning this->error_norm = old_error_norm; }
int main(int argc, char** argv) { // Initialize libMesh. LibMeshInit init (argc, argv); // Adaptive constraint calculations for fine Hermite elements seems // to require half-decent precision #ifdef LIBMESH_DEFAULT_SINGLE_PRECISION libmesh_example_assert(false, "double precision"); #endif // This example requires Adaptive Mesh Refinement support #ifndef LIBMESH_ENABLE_AMR libmesh_example_assert(false, "--enable-amr"); #else // This example requires second derivative calculation support #ifndef LIBMESH_ENABLE_SECOND_DERIVATIVES libmesh_example_assert(false, "--enable-second"); #else // Parse the input file GetPot input_file("adaptivity_ex4.in"); // Read in parameters from the input file const unsigned int max_r_level = input_file("max_r_level", 10); const unsigned int max_r_steps = input_file("max_r_steps", 4); const std::string approx_type = input_file("approx_type", "HERMITE"); const unsigned int uniform_refine = input_file("uniform_refine", 0); const Real refine_percentage = input_file("refine_percentage", 0.5); const Real coarsen_percentage = input_file("coarsen_percentage", 0.5); const unsigned int dim = input_file("dimension", 2); const unsigned int max_linear_iterations = input_file("max_linear_iterations", 10000); // Skip higher-dimensional examples on a lower-dimensional libMesh build libmesh_example_assert(dim <= LIBMESH_DIM, "2D/3D support"); // We have only defined 2 and 3 dimensional problems libmesh_assert (dim == 2 || dim == 3); // Currently only the Hermite cubics give a 3D C^1 basis libmesh_assert (dim == 2 || approx_type == "HERMITE"); // Create a mesh. Mesh mesh; // Output file for plotting the error std::string output_file = ""; if (dim == 2) output_file += "2D_"; else if (dim == 3) output_file += "3D_"; if (approx_type == "HERMITE") output_file += "hermite_"; else if (approx_type == "SECOND") output_file += "reducedclough_"; else output_file += "clough_"; if (uniform_refine == 0) output_file += "adaptive"; else output_file += "uniform"; std::string exd_file = output_file; exd_file += ".e"; output_file += ".m"; std::ofstream out (output_file.c_str()); out << "% dofs L2-error H1-error H2-error\n" << "e = [\n"; // Set up the dimension-dependent coarse mesh and solution // We build more than one cell so as to avoid bugs on fewer than // 4 processors in 2D or 8 in 3D. if (dim == 2) { MeshTools::Generation::build_square(mesh, 2, 2); exact_solution = &exact_2D_solution; exact_derivative = &exact_2D_derivative; exact_hessian = &exact_2D_hessian; forcing_function = &forcing_function_2D; } else if (dim == 3) { MeshTools::Generation::build_cube(mesh, 2, 2, 2); exact_solution = &exact_3D_solution; exact_derivative = &exact_3D_derivative; exact_hessian = &exact_3D_hessian; forcing_function = &forcing_function_3D; } // Convert the mesh to second order: necessary for computing with // Clough-Tocher elements, useful for getting slightly less // broken visualization output with Hermite elements mesh.all_second_order(); // Convert it to triangles if necessary if (approx_type != "HERMITE") MeshTools::Modification::all_tri(mesh); // Mesh Refinement object MeshRefinement mesh_refinement(mesh); mesh_refinement.refine_fraction() = refine_percentage; mesh_refinement.coarsen_fraction() = coarsen_percentage; mesh_refinement.max_h_level() = max_r_level; // Create an equation systems object. EquationSystems equation_systems (mesh); // Declare the system and its variables. // Create a system named "Biharmonic" LinearImplicitSystem& system = equation_systems.add_system<LinearImplicitSystem> ("Biharmonic"); // Adds the variable "u" to "Biharmonic". "u" // will be approximated using Hermite tensor product squares // or (possibly reduced) cubic Clough-Tocher triangles if (approx_type == "HERMITE") system.add_variable("u", THIRD, HERMITE); else if (approx_type == "SECOND") system.add_variable("u", SECOND, CLOUGH); else if (approx_type == "CLOUGH") system.add_variable("u", THIRD, CLOUGH); else libmesh_error(); // Give the system a pointer to the matrix assembly // function. system.attach_assemble_function (assemble_biharmonic); // Initialize the data structures for the equation system. equation_systems.init(); // Set linear solver max iterations equation_systems.parameters.set<unsigned int> ("linear solver maximum iterations") = max_linear_iterations; // Linear solver tolerance. equation_systems.parameters.set<Real> ("linear solver tolerance") = TOLERANCE * TOLERANCE; // Prints information about the system to the screen. equation_systems.print_info(); // Construct ExactSolution object and attach function to compute exact solution ExactSolution exact_sol(equation_systems); exact_sol.attach_exact_value(exact_solution); exact_sol.attach_exact_deriv(exact_derivative); exact_sol.attach_exact_hessian(exact_hessian); // Construct zero solution object, useful for computing solution norms // Attaching "zero_solution" functions is unnecessary ExactSolution zero_sol(equation_systems); // A refinement loop. for (unsigned int r_step=0; r_step<max_r_steps; r_step++) { mesh.print_info(); equation_systems.print_info(); std::cout << "Beginning Solve " << r_step << std::endl; // Solve the system "Biharmonic", just like example 2. system.solve(); std::cout << "Linear solver converged at step: " << system.n_linear_iterations() << ", final residual: " << system.final_linear_residual() << std::endl; // Compute the error. exact_sol.compute_error("Biharmonic", "u"); // Compute the norm. zero_sol.compute_error("Biharmonic", "u"); // Print out the error values std::cout << "L2-Norm is: " << zero_sol.l2_error("Biharmonic", "u") << std::endl; std::cout << "H1-Norm is: " << zero_sol.h1_error("Biharmonic", "u") << std::endl; std::cout << "H2-Norm is: " << zero_sol.h2_error("Biharmonic", "u") << std::endl << std::endl; std::cout << "L2-Error is: " << exact_sol.l2_error("Biharmonic", "u") << std::endl; std::cout << "H1-Error is: " << exact_sol.h1_error("Biharmonic", "u") << std::endl; std::cout << "H2-Error is: " << exact_sol.h2_error("Biharmonic", "u") << std::endl << std::endl; // Print to output file out << equation_systems.n_active_dofs() << " " << exact_sol.l2_error("Biharmonic", "u") << " " << exact_sol.h1_error("Biharmonic", "u") << " " << exact_sol.h2_error("Biharmonic", "u") << std::endl; // Possibly refine the mesh if (r_step+1 != max_r_steps) { std::cout << " Refining the mesh..." << std::endl; if (uniform_refine == 0) { ErrorVector error; LaplacianErrorEstimator error_estimator; error_estimator.estimate_error(system, error); mesh_refinement.flag_elements_by_elem_fraction (error); std::cout << "Mean Error: " << error.mean() << std::endl; std::cout << "Error Variance: " << error.variance() << std::endl; mesh_refinement.refine_and_coarsen_elements(); } else { mesh_refinement.uniformly_refine(1); } // This call reinitializes the \p EquationSystems object for // the newly refined mesh. One of the steps in the // reinitialization is projecting the \p solution, // \p old_solution, etc... vectors from the old mesh to // the current one. equation_systems.reinit (); } } #ifdef LIBMESH_HAVE_EXODUS_API // Write out the solution // After solving the system write the solution // to a ExodusII-formatted plot file. ExodusII_IO (mesh).write_equation_systems (exd_file, equation_systems); #endif // #ifdef LIBMESH_HAVE_EXODUS_API // Close up the output file. out << "];\n" << "hold on\n" << "loglog(e(:,1), e(:,2), 'bo-');\n" << "loglog(e(:,1), e(:,3), 'ro-');\n" << "loglog(e(:,1), e(:,4), 'go-');\n" << "xlabel('log(dofs)');\n" << "ylabel('log(error)');\n" << "title('C1 " << approx_type << " elements');\n" << "legend('L2-error', 'H1-error', 'H2-error');\n"; // All done. return 0; #endif // #ifndef LIBMESH_ENABLE_SECOND_DERIVATIVES #endif // #ifndef LIBMESH_ENABLE_AMR }
void AdjointRefinementEstimator::estimate_error (const System & _system, ErrorVector & error_per_cell, const NumericVector<Number> * solution_vector, bool /*estimate_parent_error*/) { // We have to break the rules here, because we can't refine a const System System & system = const_cast<System &>(_system); // An EquationSystems reference will be convenient. EquationSystems & es = system.get_equation_systems(); // The current mesh MeshBase & mesh = es.get_mesh(); // Get coarse grid adjoint solutions. This should be a relatively // quick (especially with preconditioner reuse) way to get a good // initial guess for the fine grid adjoint solutions. More // importantly, subtracting off a coarse adjoint approximation gives // us better local error indication, and subtracting off *some* lift // function is necessary for correctness if we have heterogeneous // adjoint Dirichlet conditions. // Solve the adjoint problem(s) on the coarse FE space // Only if the user didn't already solve it for us if (!system.is_adjoint_already_solved()) system.adjoint_solve(_qoi_set); // Loop over all the adjoint problems and, if any have heterogenous // Dirichlet conditions, get the corresponding coarse lift // function(s) for (unsigned int j=0; j != system.qoi.size(); j++) { // Skip this QoI if it is not in the QoI Set or if there are no // heterogeneous Dirichlet boundaries for it if (_qoi_set.has_index(j) && system.get_dof_map().has_adjoint_dirichlet_boundaries(j)) { std::ostringstream liftfunc_name; liftfunc_name << "adjoint_lift_function" << j; NumericVector<Number> & liftvec = system.add_vector(liftfunc_name.str()); system.get_dof_map().enforce_constraints_exactly (system, &liftvec, true); } } // We'll want to back up all coarse grid vectors std::map<std::string, NumericVector<Number> *> coarse_vectors; for (System::vectors_iterator vec = system.vectors_begin(); vec != system.vectors_end(); ++vec) { // The (string) name of this vector const std::string & var_name = vec->first; coarse_vectors[var_name] = vec->second->clone().release(); } // Back up the coarse solution and coarse local solution NumericVector<Number> * coarse_solution = system.solution->clone().release(); NumericVector<Number> * coarse_local_solution = system.current_local_solution->clone().release(); // And we'll need to temporarily change solution projection settings bool old_projection_setting; old_projection_setting = system.project_solution_on_reinit(); // Make sure the solution is projected when we refine the mesh system.project_solution_on_reinit() = true; // And it'll be best to avoid any repartitioning UniquePtr<Partitioner> old_partitioner(mesh.partitioner().release()); // And we can't allow any renumbering const bool old_renumbering_setting = mesh.allow_renumbering(); mesh.allow_renumbering(false); // Use a non-standard solution vector if necessary if (solution_vector && solution_vector != system.solution.get()) { NumericVector<Number> * newsol = const_cast<NumericVector<Number> *> (solution_vector); newsol->swap(*system.solution); system.update(); } // Resize the error_per_cell vector to be // the number of elements, initialized to 0. error_per_cell.clear(); error_per_cell.resize (mesh.max_elem_id(), 0.); #ifndef NDEBUG // n_coarse_elem is only used in an assertion later so // avoid declaring it unless asserts are active. const dof_id_type n_coarse_elem = mesh.n_elem(); #endif // Uniformly refine the mesh MeshRefinement mesh_refinement(mesh); libmesh_assert (number_h_refinements > 0 || number_p_refinements > 0); // FIXME: this may break if there is more than one System // on this mesh but estimate_error was still called instead of // estimate_errors for (unsigned int i = 0; i != number_h_refinements; ++i) { mesh_refinement.uniformly_refine(1); es.reinit(); } for (unsigned int i = 0; i != number_p_refinements; ++i) { mesh_refinement.uniformly_p_refine(1); es.reinit(); } // Copy the projected coarse grid solutions, which will be // overwritten by solve() std::vector<NumericVector<Number> *> coarse_adjoints; for (unsigned int j=0; j != system.qoi.size(); j++) { if (_qoi_set.has_index(j)) { NumericVector<Number> * coarse_adjoint = NumericVector<Number>::build(mesh.comm()).release(); // Can do "fast" init since we're overwriting this in a sec coarse_adjoint->init(system.get_adjoint_solution(j), /* fast = */ true); *coarse_adjoint = system.get_adjoint_solution(j); coarse_adjoints.push_back(coarse_adjoint); } else coarse_adjoints.push_back(static_cast<NumericVector<Number> *>(libmesh_nullptr)); } // Rebuild the rhs with the projected primal solution (dynamic_cast<ImplicitSystem &>(system)).assembly(true, false); NumericVector<Number> & projected_residual = (dynamic_cast<ExplicitSystem &>(system)).get_vector("RHS Vector"); projected_residual.close(); // Solve the adjoint problem(s) on the refined FE space system.adjoint_solve(_qoi_set); // Now that we have the refined adjoint solution and the projected primal solution, // we first compute the global QoI error estimate // Resize the computed_global_QoI_errors vector to hold the error estimates for each QoI computed_global_QoI_errors.resize(system.qoi.size()); // Loop over all the adjoint solutions and get the QoI error // contributions from all of them. While we're looping anyway we'll // pull off the coarse adjoints for (unsigned int j=0; j != system.qoi.size(); j++) { // Skip this QoI if not in the QoI Set if (_qoi_set.has_index(j)) { // If the adjoint solution has heterogeneous dirichlet // values, then to get a proper error estimate here we need // to subtract off a coarse grid lift function. In any case // we can get a better error estimate by separating off a // coarse representation of the adjoint solution, so we'll // use that for our lift function. system.get_adjoint_solution(j) -= *coarse_adjoints[j]; computed_global_QoI_errors[j] = projected_residual.dot(system.get_adjoint_solution(j)); } } // Done with the global error estimates, now construct the element wise error indicators // We ought to account for 'spill-over' effects while computing the // element error indicators This happens because the same dof is // shared by multiple elements, one way of mitigating this is to // scale the contribution from each dof by the number of elements it // belongs to We first obtain the number of elements each node // belongs to // A map that relates a node id to an int that will tell us how many elements it is a node of LIBMESH_BEST_UNORDERED_MAP<dof_id_type, unsigned int>shared_element_count; // To fill this map, we will loop over elements, and then in each element, we will loop // over the nodes each element contains, and then query it for the number of coarse // grid elements it was a node of // Keep track of which nodes we have already dealt with LIBMESH_BEST_UNORDERED_SET<dof_id_type> processed_node_ids; // We will be iterating over all the active elements in the fine mesh that live on // this processor { MeshBase::const_element_iterator elem_it = mesh.active_local_elements_begin(); const MeshBase::const_element_iterator elem_end = mesh.active_local_elements_end(); // Start loop over elems for(; elem_it != elem_end; ++elem_it) { // Pointer to this element const Elem * elem = *elem_it; // Loop over the nodes in the element for(unsigned int n=0; n != elem->n_nodes(); ++n) { // Get a reference to the current node const Node & node = elem->node_ref(n); // Get the id of this node dof_id_type node_id = node.id(); // If we havent already processed this node, do so now if(processed_node_ids.find(node_id) == processed_node_ids.end()) { // Declare a neighbor_set to be filled by the find_point_neighbors std::set<const Elem *> fine_grid_neighbor_set; // Call find_point_neighbors to fill the neighbor_set elem->find_point_neighbors(node, fine_grid_neighbor_set); // A vector to hold the coarse grid parents neighbors std::vector<dof_id_type> coarse_grid_neighbors; // Iterators over the fine grid neighbors set std::set<const Elem *>::iterator fine_neighbor_it = fine_grid_neighbor_set.begin(); const std::set<const Elem *>::iterator fine_neighbor_end = fine_grid_neighbor_set.end(); // Loop over all the fine neighbors of this node for(; fine_neighbor_it != fine_neighbor_end ; ++fine_neighbor_it) { // Pointer to the current fine neighbor element const Elem * fine_elem = *fine_neighbor_it; // Find the element id for the corresponding coarse grid element const Elem * coarse_elem = fine_elem; for (unsigned int j = 0; j != number_h_refinements; ++j) { libmesh_assert (coarse_elem->parent()); coarse_elem = coarse_elem->parent(); } // Loop over the existing coarse neighbors and check if this one is // already in there const dof_id_type coarse_id = coarse_elem->id(); std::size_t j = 0; for (; j != coarse_grid_neighbors.size(); j++) { // If the set already contains this element break out of the loop if(coarse_grid_neighbors[j] == coarse_id) { break; } } // If we didn't leave the loop even at the last element, // this is a new neighbour, put in the coarse_grid_neighbor_set if(j == coarse_grid_neighbors.size()) { coarse_grid_neighbors.push_back(coarse_id); } } // End loop over fine neighbors // Set the shared_neighbour index for this node to the // size of the coarse grid neighbor set shared_element_count[node_id] = cast_int<unsigned int>(coarse_grid_neighbors.size()); // Add this node to processed_node_ids vector processed_node_ids.insert(node_id); } // End if not processed node } // End loop over nodes } // End loop over elems } // Get a DoF map, will be used to get the nodal dof_indices for each element DofMap & dof_map = system.get_dof_map(); // The global DOF indices, we will use these later on when we compute the element wise indicators std::vector<dof_id_type> dof_indices; // Localize the global rhs and adjoint solution vectors (which might be shared on multiple processsors) onto a // local ghosted vector, this ensures each processor has all the dof_indices to compute an error indicator for // an element it owns UniquePtr<NumericVector<Number> > localized_projected_residual = NumericVector<Number>::build(system.comm()); localized_projected_residual->init(system.n_dofs(), system.n_local_dofs(), system.get_dof_map().get_send_list(), false, GHOSTED); projected_residual.localize(*localized_projected_residual, system.get_dof_map().get_send_list()); // Each adjoint solution will also require ghosting; for efficiency we'll reuse the same memory UniquePtr<NumericVector<Number> > localized_adjoint_solution = NumericVector<Number>::build(system.comm()); localized_adjoint_solution->init(system.n_dofs(), system.n_local_dofs(), system.get_dof_map().get_send_list(), false, GHOSTED); // We will loop over each adjoint solution, localize that adjoint // solution and then loop over local elements for (unsigned int i=0; i != system.qoi.size(); i++) { // Skip this QoI if not in the QoI Set if (_qoi_set.has_index(i)) { // Get the weight for the current QoI Real error_weight = _qoi_set.weight(i); (system.get_adjoint_solution(i)).localize(*localized_adjoint_solution, system.get_dof_map().get_send_list()); // Loop over elements MeshBase::const_element_iterator elem_it = mesh.active_local_elements_begin(); const MeshBase::const_element_iterator elem_end = mesh.active_local_elements_end(); for(; elem_it != elem_end; ++elem_it) { // Pointer to the element const Elem * elem = *elem_it; // Go up number_h_refinements levels up to find the coarse parent const Elem * coarse = elem; for (unsigned int j = 0; j != number_h_refinements; ++j) { libmesh_assert (coarse->parent()); coarse = coarse->parent(); } const dof_id_type e_id = coarse->id(); // Get the local to global degree of freedom maps for this element dof_map.dof_indices (elem, dof_indices); // We will have to manually do the dot products. Number local_contribution = 0.; for (unsigned int j=0; j != dof_indices.size(); j++) { // The contribution to the error indicator for this element from the current QoI local_contribution += (*localized_projected_residual)(dof_indices[j]) * (*localized_adjoint_solution)(dof_indices[j]); } // Multiply by the error weight for this QoI local_contribution *= error_weight; // FIXME: we're throwing away information in the // --enable-complex case error_per_cell[e_id] += static_cast<ErrorVectorReal> (std::abs(local_contribution)); } // End loop over elements } // End if belong to QoI set } // End loop over QoIs for (unsigned int j=0; j != system.qoi.size(); j++) { if (_qoi_set.has_index(j)) { delete coarse_adjoints[j]; } } // Don't bother projecting the solution; we'll restore from backup // after coarsening system.project_solution_on_reinit() = false; // Uniformly coarsen the mesh, without projecting the solution libmesh_assert (number_h_refinements > 0 || number_p_refinements > 0); for (unsigned int i = 0; i != number_h_refinements; ++i) { mesh_refinement.uniformly_coarsen(1); // FIXME - should the reinits here be necessary? - RHS es.reinit(); } for (unsigned int i = 0; i != number_p_refinements; ++i) { mesh_refinement.uniformly_p_coarsen(1); es.reinit(); } // We should be back where we started libmesh_assert_equal_to (n_coarse_elem, mesh.n_elem()); // Restore old solutions and clean up the heap system.project_solution_on_reinit() = old_projection_setting; // Restore the coarse solution vectors and delete their copies *system.solution = *coarse_solution; delete coarse_solution; *system.current_local_solution = *coarse_local_solution; delete coarse_local_solution; for (System::vectors_iterator vec = system.vectors_begin(); vec != system.vectors_end(); ++vec) { // The (string) name of this vector const std::string & var_name = vec->first; // If it's a vector we already had (and not a newly created // vector like an adjoint rhs), we need to restore it. std::map<std::string, NumericVector<Number> *>::iterator it = coarse_vectors.find(var_name); if (it != coarse_vectors.end()) { NumericVector<Number> * coarsevec = it->second; system.get_vector(var_name) = *coarsevec; coarsevec->clear(); delete coarsevec; } } // Restore old partitioner and renumbering settings mesh.partitioner().reset(old_partitioner.release()); mesh.allow_renumbering(old_renumbering_setting); // Fiinally sum the vector of estimated error values. this->reduce_error(error_per_cell, system.comm()); // We don't take a square root here; this is a goal-oriented // estimate not a Hilbert norm estimate. } // end estimate_error function
void ExactErrorEstimator::estimate_error (const System & system, ErrorVector & error_per_cell, const NumericVector<Number> * solution_vector, bool estimate_parent_error) { // Ignore the fact that this variable is unused when !LIBMESH_ENABLE_AMR libmesh_ignore(estimate_parent_error); // The current mesh const MeshBase & mesh = system.get_mesh(); // The dimensionality of the mesh const unsigned int dim = mesh.mesh_dimension(); // The number of variables in the system const unsigned int n_vars = system.n_vars(); // The DofMap for this system const DofMap & dof_map = system.get_dof_map(); // Resize the error_per_cell vector to be // the number of elements, initialize it to 0. error_per_cell.resize (mesh.max_elem_id()); std::fill (error_per_cell.begin(), error_per_cell.end(), 0.); // Prepare current_local_solution to localize a non-standard // solution vector if necessary if (solution_vector && solution_vector != system.solution.get()) { NumericVector<Number> * newsol = const_cast<NumericVector<Number> *>(solution_vector); System & sys = const_cast<System &>(system); newsol->swap(*sys.solution); sys.update(); } // Loop over all the variables in the system for (unsigned int var=0; var<n_vars; var++) { // Possibly skip this variable if (error_norm.weight(var) == 0.0) continue; // The (string) name of this variable const std::string & var_name = system.variable_name(var); // The type of finite element to use for this variable const FEType & fe_type = dof_map.variable_type (var); UniquePtr<FEBase> fe (FEBase::build (dim, fe_type)); // Build an appropriate Gaussian quadrature rule UniquePtr<QBase> qrule = fe_type.default_quadrature_rule (dim, _extra_order); fe->attach_quadrature_rule (qrule.get()); // Prepare a global solution and a MeshFunction of the fine system if we need one UniquePtr<MeshFunction> fine_values; UniquePtr<NumericVector<Number> > fine_soln = NumericVector<Number>::build(system.comm()); if (_equation_systems_fine) { const System & fine_system = _equation_systems_fine->get_system(system.name()); std::vector<Number> global_soln; // FIXME - we're assuming that the fine system solution gets // used even when a different vector is used for the coarse // system fine_system.update_global_solution(global_soln); fine_soln->init (cast_int<numeric_index_type>(global_soln.size()), true, SERIAL); (*fine_soln) = global_soln; fine_values = UniquePtr<MeshFunction> (new MeshFunction(*_equation_systems_fine, *fine_soln, fine_system.get_dof_map(), fine_system.variable_number(var_name))); fine_values->init(); } else { // Initialize functors if we're using them for (unsigned int i=0; i != _exact_values.size(); ++i) if (_exact_values[i]) _exact_values[i]->init(); for (unsigned int i=0; i != _exact_derivs.size(); ++i) if (_exact_derivs[i]) _exact_derivs[i]->init(); for (unsigned int i=0; i != _exact_hessians.size(); ++i) if (_exact_hessians[i]) _exact_hessians[i]->init(); } // Request the data we'll need to compute with fe->get_JxW(); fe->get_phi(); fe->get_dphi(); #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES fe->get_d2phi(); #endif fe->get_xyz(); #ifdef LIBMESH_ENABLE_AMR // If we compute on parent elements, we'll want to do so only // once on each, so we need to keep track of which we've done. std::vector<bool> computed_var_on_parent; if (estimate_parent_error) computed_var_on_parent.resize(error_per_cell.size(), false); #endif // TODO: this ought to be threaded (and using subordinate // MeshFunction objects in each thread rather than a single // master) // Iterate over all the active elements in the mesh // that live on this processor. MeshBase::const_element_iterator elem_it = mesh.active_local_elements_begin(); const MeshBase::const_element_iterator elem_end = mesh.active_local_elements_end(); for (;elem_it != elem_end; ++elem_it) { // e is necessarily an active element on the local processor const Elem * elem = *elem_it; const dof_id_type e_id = elem->id(); #ifdef LIBMESH_ENABLE_AMR // See if the parent of element e has been examined yet; // if not, we may want to compute the estimator on it const Elem * parent = elem->parent(); // We only can compute and only need to compute on // parents with all active children bool compute_on_parent = true; if (!parent || !estimate_parent_error) compute_on_parent = false; else for (unsigned int c=0; c != parent->n_children(); ++c) if (!parent->child_ptr(c)->active()) compute_on_parent = false; if (compute_on_parent && !computed_var_on_parent[parent->id()]) { computed_var_on_parent[parent->id()] = true; // Compute a projection onto the parent DenseVector<Number> Uparent; FEBase::coarsened_dof_values(*(system.current_local_solution), dof_map, parent, Uparent, var, false); error_per_cell[parent->id()] += static_cast<ErrorVectorReal> (find_squared_element_error(system, var_name, parent, Uparent, fe.get(), fine_values.get())); } #endif // Get the local to global degree of freedom maps std::vector<dof_id_type> dof_indices; dof_map.dof_indices (elem, dof_indices, var); const unsigned int n_dofs = cast_int<unsigned int>(dof_indices.size()); DenseVector<Number> Uelem(n_dofs); for (unsigned int i=0; i != n_dofs; ++i) Uelem(i) = system.current_solution(dof_indices[i]); error_per_cell[e_id] += static_cast<ErrorVectorReal> (find_squared_element_error(system, var_name, elem, Uelem, fe.get(), fine_values.get())); } // End loop over active local elements } // End loop over variables // Each processor has now computed the error contribuions // for its local elements. We need to sum the vector // and then take the square-root of each component. Note // that we only need to sum if we are running on multiple // processors, and we only need to take the square-root // if the value is nonzero. There will in general be many // zeros for the inactive elements. // First sum the vector of estimated error values this->reduce_error(error_per_cell, system.comm()); // Compute the square-root of each component. { LOG_SCOPE("std::sqrt()", "ExactErrorEstimator"); for (dof_id_type i=0; i<error_per_cell.size(); i++) if (error_per_cell[i] != 0.) { libmesh_assert_greater (error_per_cell[i], 0.); error_per_cell[i] = std::sqrt(error_per_cell[i]); } } // If we used a non-standard solution before, now is the time to fix // the current_local_solution if (solution_vector && solution_vector != system.solution.get()) { NumericVector<Number> * newsol = const_cast<NumericVector<Number> *>(solution_vector); System & sys = const_cast<System &>(system); newsol->swap(*sys.solution); sys.update(); } }
int main(int argc, char** argv){ //initialize libMesh LibMeshInit init(argc, argv); //parameters GetPot infile("fem_system_params.in"); const bool transient = infile("transient", true); const Real deltat = infile("deltat", 0.005); unsigned int n_timesteps = infile("n_timesteps", 20); const int nx = infile("nx",100); const int ny = infile("ny",100); const int nz = infile("nz",100); //const unsigned int dim = 3; const unsigned int max_r_steps = infile("max_r_steps", 3); const unsigned int max_r_level = infile("max_r_level", 3); const Real refine_percentage = infile("refine_percentage", 0.1); const Real coarsen_percentage = infile("coarsen_percentage", 0.0); const std::string indicator_type = infile("indicator_type", "kelly"); const bool write_error = infile("write_error",false); const bool flag_by_elem_frac = infile("flag_by_elem_frac",true); #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()); //create mesh unsigned int dim; if(nz == 0){ //to check if oscillations happen in 2D as well... dim = 2; MeshTools::Generation::build_square(mesh, nx, ny, 497150.0, 501750.0, 537350.0, 540650.0, QUAD9); }else{ dim = 3; MeshTools::Generation::build_cube(mesh, nx, ny, nz, 497150.0, 501750.0, 537350.0, 540650.0, 0.0, 100.0, HEX27); } // Print information about the mesh to the screen. mesh.print_info(); // Create an equation systems object. EquationSystems equation_systems (mesh); //name system ContamTransSysInv & system = equation_systems.add_system<ContamTransSysInv>("ContamTransInv"); //solve as steady or transient if(transient){ //system.time_solver = AutoPtr<TimeSolver>(new EulerSolver(system)); //backward Euler system.time_solver = AutoPtr<TimeSolver>(new SteadySolver(system)); std::cout << "\n\nAaahhh transience not yet available!\n" << std::endl; n_timesteps = 1; } else{ system.time_solver = AutoPtr<TimeSolver>(new SteadySolver(system)); libmesh_assert_equal_to (n_timesteps, 1); //this doesn't seem to work? } // Initialize the system equation_systems.init (); //initial conditions read_initial_parameters(); system.project_solution(initial_value, initial_grad, equation_systems.parameters); finish_initialization(); // Set the time stepping options... system.deltat = deltat; //...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", 10000); solver->initial_linear_tolerance = infile("initial_linear_tolerance",1.e-13); solver->minimum_linear_tolerance = infile("minimum_linear_tolerance",1.e-13); solver->linear_tolerance_multiplier = infile("linear_tolerance_multiplier",1.e-3); // Mesh Refinement object - to test effect of constant refined mesh (not refined at every timestep) MeshRefinement mesh_refinement(mesh); mesh_refinement.refine_fraction() = refine_percentage; mesh_refinement.coarsen_fraction() = coarsen_percentage; mesh_refinement.max_h_level() = max_r_level; // Print information about the system to the screen. equation_systems.print_info(); ExodusII_IO exodusIO = ExodusII_IO(mesh); //for writing multiple timesteps to one file for (unsigned int r_step=0; r_step<max_r_steps; r_step++) { std::cout << "\nBeginning Solve " << r_step+1 << std::endl; for (unsigned int t_step=0; t_step != n_timesteps; ++t_step) { std::cout << "\n\nSolving time step " << t_step << ", time = " << system.time << std::endl; system.solve(); system.postprocess(); // Advance to the next timestep in a transient problem system.time_solver->advance_timestep(); } //end stepping through time loop std::cout << "\n Refining the mesh..." << std::endl; // The \p ErrorVector is a particular \p StatisticsVector // for computing error information on a finite element mesh. ErrorVector error; if (indicator_type == "patch") { // The patch recovery estimator should give a // good estimate of the solution interpolation // error. PatchRecoveryErrorEstimator error_estimator; error_estimator.set_patch_reuse(false); //anisotropy trips up reuse error_estimator.estimate_error (system, error); } else if (indicator_type == "kelly") { // The Kelly error estimator is based on // an error bound for the Poisson problem // on linear elements, but is useful for // driving adaptive refinement in many problems KellyErrorEstimator error_estimator; error_estimator.estimate_error (system, error); } // Write out the error distribution if(write_error){ std::ostringstream ss; ss << r_step; #ifdef LIBMESH_HAVE_EXODUS_API std::string error_output = "error_"+ss.str()+".e"; #else std::string error_output = "error_"+ss.str()+".gmv"; #endif error.plot_error( error_output, mesh ); } // This takes the error in \p error and decides which elements // will be coarsened or refined. if(flag_by_elem_frac) mesh_refinement.flag_elements_by_elem_fraction(error); else mesh_refinement.flag_elements_by_error_fraction (error); // This call actually refines and coarsens the flagged // elements. mesh_refinement.refine_and_coarsen_elements(); // This call reinitializes the \p EquationSystems object for // the newly refined mesh. One of the steps in the // reinitialization is projecting the \p solution, // \p old_solution, etc... vectors from the old mesh to // the current one. equation_systems.reinit (); std::cout << "System has: " << equation_systems.n_active_dofs() << " degrees of freedom." << std::endl; } //end refinement loop //use that final refinement for (unsigned int t_step=0; t_step != n_timesteps; ++t_step) { std::cout << "\n\nSolving time step " << t_step << ", time = " << system.time << std::endl; system.solve(); system.postprocess(); Number QoI_computed = system.get_QoI_value("computed", 0); std::cout<< "Computed QoI is " << std::setprecision(17) << QoI_computed << std::endl; // Advance to the next timestep in a transient problem system.time_solver->advance_timestep(); } //end stepping through time loop #ifdef LIBMESH_HAVE_EXODUS_API for (unsigned int t_step=0; t_step != n_timesteps; ++t_step) { // Write out this timestep if we're requested to if ((t_step+1)%write_interval == 0) { std::ostringstream ex_file_name; std::ostringstream tplot_file_name; // We write the file in the ExodusII format. //ex_file_name << "out_" // << std::setw(3) // << std::setfill('0') // << std::right // << t_step+1 // << ".e"; tplot_file_name << "out_" << std::setw(3) << std::setfill('0') << std::right << t_step+1 << ".plt"; //ExodusII_IO(mesh).write_timestep(ex_file_name.str(), // equation_systems, // 1, /* This number indicates how many time steps // are being written to the file */ // system.time); exodusIO.write_timestep("output.exo", equation_systems, t_step+1, system.time); //outputs all timesteps in one file TecplotIO(mesh).write_equation_systems(tplot_file_name.str(), equation_systems); } } #endif // #ifdef LIBMESH_HAVE_EXODUS_API // All done. return 0; } //end main
// 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
void AdjointResidualErrorEstimator::estimate_error (const System & _system, ErrorVector & error_per_cell, const NumericVector<Number> * solution_vector, bool estimate_parent_error) { LOG_SCOPE("estimate_error()", "AdjointResidualErrorEstimator"); // The current mesh const MeshBase & mesh = _system.get_mesh(); // Resize the error_per_cell vector to be // the number of elements, initialize it to 0. error_per_cell.resize (mesh.max_elem_id()); std::fill (error_per_cell.begin(), error_per_cell.end(), 0.); // Get the number of variables in the system unsigned int n_vars = _system.n_vars(); // We need to make a map of the pointer to the solution vector std::map<const System *, const NumericVector<Number> *>solutionvecs; solutionvecs[&_system] = _system.solution.get(); // Solve the dual problem if we have to if (!_system.is_adjoint_already_solved()) { // FIXME - we'll need to change a lot of APIs to make this trick // work with a const System... System & system = const_cast<System &>(_system); system.adjoint_solve(_qoi_set); } // Flag to check whether we have not been asked to weight the variable error contributions in any specific manner bool error_norm_is_identity = error_norm.is_identity(); // Create an ErrorMap/ErrorVector to store the primal, dual and total_dual variable errors ErrorMap primal_errors_per_cell; ErrorMap dual_errors_per_cell; ErrorMap total_dual_errors_per_cell; // Allocate ErrorVectors to this map if we're going to use it if (!error_norm_is_identity) for(unsigned int v = 0; v < n_vars; v++) { primal_errors_per_cell[std::make_pair(&_system, v)] = new ErrorVector; dual_errors_per_cell[std::make_pair(&_system, v)] = new ErrorVector; total_dual_errors_per_cell[std::make_pair(&_system, v)] = new ErrorVector; } ErrorVector primal_error_per_cell; ErrorVector dual_error_per_cell; ErrorVector total_dual_error_per_cell; // Have we been asked to weight the variable error contributions in any specific manner if(!error_norm_is_identity) // If we do { // Estimate the primal problem error for each variable _primal_error_estimator->estimate_errors (_system.get_equation_systems(), primal_errors_per_cell, &solutionvecs, estimate_parent_error); } else // If not { // Just get the combined error estimate _primal_error_estimator->estimate_error (_system, primal_error_per_cell, solution_vector, estimate_parent_error); } // Sum and weight the dual error estimate based on our QoISet for (unsigned int i = 0; i != _system.qoi.size(); ++i) { if (_qoi_set.has_index(i)) { // Get the weight for the current QoI Real error_weight = _qoi_set.weight(i); // We need to make a map of the pointer to the adjoint solution vector std::map<const System *, const NumericVector<Number> *>adjointsolutionvecs; adjointsolutionvecs[&_system] = &_system.get_adjoint_solution(i); // Have we been asked to weight the variable error contributions in any specific manner if(!error_norm_is_identity) // If we have { _dual_error_estimator->estimate_errors (_system.get_equation_systems(), dual_errors_per_cell, &adjointsolutionvecs, estimate_parent_error); } else // If not { // Just get the combined error estimate _dual_error_estimator->estimate_error (_system, dual_error_per_cell, &(_system.get_adjoint_solution(i)), estimate_parent_error); } std::size_t error_size; // Get the size of the first ErrorMap vector; this will give us the number of elements if(!error_norm_is_identity) // If in non default weights case { error_size = dual_errors_per_cell[std::make_pair(&_system, 0)]->size(); } else // If in the standard default weights case { error_size = dual_error_per_cell.size(); } // Resize the ErrorVector(s) if(!error_norm_is_identity) { // Loop over variables for(unsigned int v = 0; v < n_vars; v++) { libmesh_assert(!total_dual_errors_per_cell[std::make_pair(&_system, v)]->size() || total_dual_errors_per_cell[std::make_pair(&_system, v)]->size() == error_size) ; total_dual_errors_per_cell[std::make_pair(&_system, v)]->resize(error_size); } } else { libmesh_assert(!total_dual_error_per_cell.size() || total_dual_error_per_cell.size() == error_size); total_dual_error_per_cell.resize(error_size); } for (std::size_t e = 0; e != error_size; ++e) { // Have we been asked to weight the variable error contributions in any specific manner if(!error_norm_is_identity) // If we have { // Loop over variables for(unsigned int v = 0; v < n_vars; v++) { // Now fill in total_dual_error ErrorMap with the weight (*total_dual_errors_per_cell[std::make_pair(&_system, v)])[e] += static_cast<ErrorVectorReal> (error_weight * (*dual_errors_per_cell[std::make_pair(&_system, v)])[e]); } } else // If not { total_dual_error_per_cell[e] += static_cast<ErrorVectorReal>(error_weight * dual_error_per_cell[e]); } } } } // Do some debugging plots if requested if (!error_plot_suffix.empty()) { if(!error_norm_is_identity) // If we have { // Loop over variables for(unsigned int v = 0; v < n_vars; v++) { std::ostringstream primal_out; std::ostringstream dual_out; primal_out << "primal_" << error_plot_suffix << "."; dual_out << "dual_" << error_plot_suffix << "."; primal_out << std::setw(1) << std::setprecision(0) << std::setfill('0') << std::right << v; dual_out << std::setw(1) << std::setprecision(0) << std::setfill('0') << std::right << v; (*primal_errors_per_cell[std::make_pair(&_system, v)]).plot_error(primal_out.str(), _system.get_mesh()); (*total_dual_errors_per_cell[std::make_pair(&_system, v)]).plot_error(dual_out.str(), _system.get_mesh()); primal_out.clear(); dual_out.clear(); } } else // If not { std::ostringstream primal_out; std::ostringstream dual_out; primal_out << "primal_" << error_plot_suffix ; dual_out << "dual_" << error_plot_suffix ; primal_error_per_cell.plot_error(primal_out.str(), _system.get_mesh()); total_dual_error_per_cell.plot_error(dual_out.str(), _system.get_mesh()); primal_out.clear(); dual_out.clear(); } } // Weight the primal error by the dual error using the system norm object // FIXME: we ought to thread this for (unsigned int i=0; i != error_per_cell.size(); ++i) { // Have we been asked to weight the variable error contributions in any specific manner if(!error_norm_is_identity) // If we do { // Create Error Vectors to pass to calculate_norm std::vector<Real> cell_primal_error; std::vector<Real> cell_dual_error; for(unsigned int v = 0; v < n_vars; v++) { cell_primal_error.push_back((*primal_errors_per_cell[std::make_pair(&_system, v)])[i]); cell_dual_error.push_back((*total_dual_errors_per_cell[std::make_pair(&_system, v)])[i]); } error_per_cell[i] = static_cast<ErrorVectorReal> (error_norm.calculate_norm(cell_primal_error, cell_dual_error)); } else // If not { error_per_cell[i] = primal_error_per_cell[i]*total_dual_error_per_cell[i]; } } // Deallocate the ErrorMap contents if we allocated them earlier if (!error_norm_is_identity) for(unsigned int v = 0; v < n_vars; v++) { delete primal_errors_per_cell[std::make_pair(&_system, v)]; delete dual_errors_per_cell[std::make_pair(&_system, v)]; delete total_dual_errors_per_cell[std::make_pair(&_system, v)]; } }
//----------------------------------------------------------------- // Mesh refinement methods void MeshRefinement::flag_elements_by_error_fraction (const ErrorVector& error_per_cell, const Real refine_frac, const Real coarsen_frac, const unsigned int max_l) { parallel_only(); // The function arguments are currently just there for // backwards_compatibility if (!_use_member_parameters) { // If the user used non-default parameters, lets warn // that they're deprecated if (refine_frac != 0.3 || coarsen_frac != 0.0 || max_l != libMesh::invalid_uint) libmesh_deprecated(); _refine_fraction = refine_frac; _coarsen_fraction = coarsen_frac; _max_h_level = max_l; } // Check for valid fractions.. // The fraction values must be in [0,1] libmesh_assert_greater_equal (_refine_fraction, 0); libmesh_assert_less_equal (_refine_fraction, 1); libmesh_assert_greater_equal (_coarsen_fraction, 0); libmesh_assert_less_equal (_coarsen_fraction, 1); // Clean up the refinement flags. These could be left // over from previous refinement steps. this->clean_refinement_flags(); // We're getting the minimum and maximum error values // for the ACTIVE elements Real error_min = 1.e30; Real error_max = 0.; // And, if necessary, for their parents Real parent_error_min = 1.e30; Real parent_error_max = 0.; // Prepare another error vector if we need to sum parent errors ErrorVector error_per_parent; if (_coarsen_by_parents) { create_parent_error_vector(error_per_cell, error_per_parent, parent_error_min, parent_error_max); } // We need to loop over all active elements to find the minimum MeshBase::element_iterator el_it = _mesh.active_local_elements_begin(); const MeshBase::element_iterator el_end = _mesh.active_local_elements_end(); for (; el_it != el_end; ++el_it) { const unsigned int id = (*el_it)->id(); libmesh_assert_less (id, error_per_cell.size()); error_max = std::max (error_max, error_per_cell[id]); error_min = std::min (error_min, error_per_cell[id]); } CommWorld.max(error_max); CommWorld.min(error_min); // Compute the cutoff values for coarsening and refinement const Real error_delta = (error_max - error_min); const Real parent_error_delta = parent_error_max - parent_error_min; const Real refine_cutoff = (1.- _refine_fraction)*error_max; const Real coarsen_cutoff = _coarsen_fraction*error_delta + error_min; const Real parent_cutoff = _coarsen_fraction*parent_error_delta + error_min; // // Print information about the error // libMesh::out << " Error Information:" << std::endl // << " ------------------" << std::endl // << " min: " << error_min << std::endl // << " max: " << error_max << std::endl // << " delta: " << error_delta << std::endl // << " refine_cutoff: " << refine_cutoff << std::endl // << " coarsen_cutoff: " << coarsen_cutoff << std::endl; // Loop over the elements and flag them for coarsening or // refinement based on the element error MeshBase::element_iterator e_it = _mesh.active_elements_begin(); const MeshBase::element_iterator e_end = _mesh.active_elements_end(); for (; e_it != e_end; ++e_it) { Elem* elem = *e_it; const unsigned int id = elem->id(); libmesh_assert_less (id, error_per_cell.size()); const float elem_error = error_per_cell[id]; if (_coarsen_by_parents) { Elem* parent = elem->parent(); if (parent) { const unsigned int parentid = parent->id(); if (error_per_parent[parentid] >= 0. && error_per_parent[parentid] <= parent_cutoff) elem->set_refinement_flag(Elem::COARSEN); } } // Flag the element for coarsening if its error // is <= coarsen_fraction*delta + error_min else if (elem_error <= coarsen_cutoff) { elem->set_refinement_flag(Elem::COARSEN); } // Flag the element for refinement if its error // is >= refinement_cutoff. if (elem_error >= refine_cutoff) if (elem->level() < _max_h_level) elem->set_refinement_flag(Elem::REFINE); } }
bool MeshRefinement::flag_elements_by_nelem_target (const ErrorVector& error_per_cell) { parallel_only(); // Check for valid fractions.. // The fraction values must be in [0,1] libmesh_assert_greater_equal (_refine_fraction, 0); libmesh_assert_less_equal (_refine_fraction, 1); libmesh_assert_greater_equal (_coarsen_fraction, 0); libmesh_assert_less_equal (_coarsen_fraction, 1); // This function is currently only coded to work when coarsening by // parents - it's too hard to guess how many coarsenings will be // performed otherwise. libmesh_assert (_coarsen_by_parents); // The number of active elements in the mesh - hopefully less than // 2 billion on 32 bit machines const unsigned int n_active_elem = _mesh.n_active_elem(); // The maximum number of active elements to flag for coarsening const unsigned int max_elem_coarsen = static_cast<unsigned int>(_coarsen_fraction * n_active_elem) + 1; // The maximum number of elements to flag for refinement const unsigned int max_elem_refine = static_cast<unsigned int>(_refine_fraction * n_active_elem) + 1; // Clean up the refinement flags. These could be left // over from previous refinement steps. this->clean_refinement_flags(); // The target number of elements to add or remove const int n_elem_new = _nelem_target - n_active_elem; // Create an vector with active element errors and ids, // sorted by highest errors first const unsigned int max_elem_id = _mesh.max_elem_id(); std::vector<std::pair<float, unsigned int> > sorted_error; sorted_error.reserve (n_active_elem); // On a ParallelMesh, we need to communicate to know which remote ids // correspond to active elements. { std::vector<bool> is_active(max_elem_id, false); MeshBase::element_iterator elem_it = _mesh.active_local_elements_begin(); const MeshBase::element_iterator elem_end = _mesh.active_local_elements_end(); for (; elem_it != elem_end; ++elem_it) { const unsigned int eid = (*elem_it)->id(); is_active[eid] = true; libmesh_assert_less (eid, error_per_cell.size()); sorted_error.push_back (std::make_pair(error_per_cell[eid], eid)); } CommWorld.max(is_active); CommWorld.allgather(sorted_error); } // Default sort works since pairs are sorted lexicographically std::sort (sorted_error.begin(), sorted_error.end()); std::reverse (sorted_error.begin(), sorted_error.end()); // Create a sorted error vector with coarsenable parent elements // only, sorted by lowest errors first ErrorVector error_per_parent; std::vector<std::pair<float, unsigned int> > sorted_parent_error; Real parent_error_min, parent_error_max; create_parent_error_vector(error_per_cell, error_per_parent, parent_error_min, parent_error_max); // create_parent_error_vector sets values for non-parents and // non-coarsenable parents to -1. Get rid of them. for (unsigned int i=0; i != error_per_parent.size(); ++i) if (error_per_parent[i] != -1) sorted_parent_error.push_back(std::make_pair(error_per_parent[i], i)); std::sort (sorted_parent_error.begin(), sorted_parent_error.end()); // Keep track of how many elements we plan to coarsen & refine unsigned int coarsen_count = 0; unsigned int refine_count = 0; const unsigned int dim = _mesh.mesh_dimension(); unsigned int twotodim = 1; for (unsigned int i=0; i!=dim; ++i) twotodim *= 2; // First, let's try to get our element count to target_nelem if (n_elem_new >= 0) { // Every element refinement creates at least // 2^dim-1 new elements refine_count = std::min(static_cast<unsigned int>(n_elem_new / (twotodim-1)), max_elem_refine); } else { // Every successful element coarsening is likely to destroy // 2^dim-1 net elements. coarsen_count = std::min(static_cast<unsigned int>(-n_elem_new / (twotodim-1)), max_elem_coarsen); } // Next, let's see if we can trade any refinement for coarsening while (coarsen_count < max_elem_coarsen && refine_count < max_elem_refine && coarsen_count < sorted_parent_error.size() && refine_count < sorted_error.size() && sorted_error[refine_count].first > sorted_parent_error[coarsen_count].first * _coarsen_threshold) { coarsen_count++; refine_count++; } // On a ParallelMesh, we need to communicate to know which remote ids // correspond to refinable elements unsigned int successful_refine_count = 0; { std::vector<bool> is_refinable(max_elem_id, false); for (unsigned int i=0; i != sorted_error.size(); ++i) { unsigned int eid = sorted_error[i].second; Elem *elem = _mesh.query_elem(eid); if (elem && elem->level() < _max_h_level) is_refinable[eid] = true; } CommWorld.max(is_refinable); if (refine_count > max_elem_refine) refine_count = max_elem_refine; for (unsigned int i=0; i != sorted_error.size(); ++i) { if (successful_refine_count >= refine_count) break; unsigned int eid = sorted_error[i].second; Elem *elem = _mesh.query_elem(eid); if (is_refinable[eid]) { if (elem) elem->set_refinement_flag(Elem::REFINE); successful_refine_count++; } } } // If we couldn't refine enough elements, don't coarsen too many // either if (coarsen_count < (refine_count - successful_refine_count)) coarsen_count = 0; else coarsen_count -= (refine_count - successful_refine_count); if (coarsen_count > max_elem_coarsen) coarsen_count = max_elem_coarsen; unsigned int successful_coarsen_count = 0; if (coarsen_count) { for (unsigned int i=0; i != sorted_parent_error.size(); ++i) { if (successful_coarsen_count >= coarsen_count * twotodim) break; unsigned int parent_id = sorted_parent_error[i].second; Elem *parent = _mesh.query_elem(parent_id); // On a ParallelMesh we skip remote elements if (!parent) continue; libmesh_assert(parent->has_children()); for (unsigned int c=0; c != parent->n_children(); ++c) { Elem *elem = parent->child(c); if (elem && elem != remote_elem) { libmesh_assert(elem->active()); elem->set_refinement_flag(Elem::COARSEN); successful_coarsen_count++; } } } } // Return true if we've done all the AMR/C we can if (!successful_coarsen_count && !successful_refine_count) return true; // And false if there may still be more to do. return false; }
Real ErrorVector::median() const { ErrorVector ev = (*this); return ev.median(); }
// The main program. int main (int argc, char** argv) { // Initialize libMesh. LibMeshInit init (argc, argv); // This example fails without at least double precision FP #ifdef LIBMESH_DEFAULT_SINGLE_PRECISION libmesh_example_assert(false, "--disable-singleprecision"); #endif #ifndef LIBMESH_ENABLE_AMR libmesh_example_assert(false, "--enable-amr"); #else // Trilinos solver NaNs by default on the zero pressure block. // We'll skip this example for now. if (libMesh::default_solver_package() == TRILINOS_SOLVERS) { std::cout << "We skip fem_system_ex1 when using the Trilinos solvers.\n" << std::endl; return 0; } // Parse the input file GetPot infile("fem_system_ex1.in"); // Read in parameters from the input file 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", 20); const unsigned int write_interval = infile("write_interval", 5); 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 = infile("dimension", 2); // Skip higher-dimensional examples on a lower-dimensional libMesh build libmesh_example_assert(dim <= LIBMESH_DIM, "2D/3D support"); // We have only defined 2 and 3 dimensional problems libmesh_assert (dim == 2 || dim == 3); // Create a mesh. Mesh mesh; // 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; // Use the MeshTools::Generation mesh generator to create a uniform // grid on the square [-1,1]^D. We instruct the mesh generator // to build a mesh of 8x8 \p Quad9 elements in 2D, or \p Hex27 // elements in 3D. Building these higher-order elements allows // us to use higher-order approximation, as in example 3. if (dim == 2) MeshTools::Generation::build_square (mesh, coarsegridsize, coarsegridsize, 0., 1., 0., 1., QUAD9); else if (dim == 3) MeshTools::Generation::build_cube (mesh, coarsegridsize, coarsegridsize, coarsegridsize, 0., 1., 0., 1., 0., 1., HEX27); 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); // Declare the system "Navier-Stokes" and its variables. NavierSystem & system = equation_systems.add_system<NavierSystem> ("Navier-Stokes"); // Solve this as a time-dependent or steady system if (transient) system.time_solver = AutoPtr<TimeSolver>(new EulerSolver(system)); else { system.time_solver = AutoPtr<TimeSolver>(new SteadySolver(system)); libmesh_assert_equal_to (n_timesteps, 1); } // Initialize the system equation_systems.init (); // Set the time stepping options system.deltat = deltat; // And the nonlinear solver options DiffSolver &solver = *(system.time_solver->diff_solver().get()); 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. 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) { 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 based on u and v (and w?) but not p std::vector<Real> weights(2,1.0); // u, v if (dim == 3) weights.push_back(1.0); // w weights.push_back(0.0); // p // 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; } // Do one last solve if necessary if (a_step == max_adaptivesteps) { system.solve(); system.postprocess(); } // Advance to the next timestep in a transient problem system.time_solver->advance_timestep(); #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"; ExodusII_IO(mesh).write_timestep(file_name.str(), equation_systems, 1, /* This number indicates how many time steps are being written to the file */ system.time); } #endif // #ifdef LIBMESH_HAVE_EXODUS_API } #endif // #ifndef LIBMESH_ENABLE_AMR // All done. return 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"); #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"); // This doesn't converge without at least double precision libmesh_example_requires(sizeof(Real) > 4, "--disable-singleprecision"); // Parse the input file GetPot infile("fem_system_ex4.in"); // Read in parameters from the input file const Real global_tolerance = infile("global_tolerance", 0.); const unsigned int nelem_target = infile("n_elements", 400); const Real deltat = infile("deltat", 0.005); const unsigned int coarsegridsize = infile("coarsegridsize", 20); const unsigned int coarserefinements = infile("coarserefinements", 0); const unsigned int max_adaptivesteps = infile("max_adaptivesteps", 10); const unsigned int dim = infile("dimension", 2); // Skip higher-dimensional examples on a lower-dimensional libMesh build libmesh_example_requires(dim <= LIBMESH_DIM, "2D/3D support"); // We have only defined 2 and 3 dimensional problems libmesh_assert (dim == 2 || dim == 3); // 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 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; // Use the MeshTools::Generation mesh generator to create a uniform // grid on the square or cube. We crop the domain at y=2/3 to allow // for a homogeneous Neumann BC in our benchmark there. boundary_id_type bcid = 3; // +y in 3D if (dim == 2) { MeshTools::Generation::build_square (mesh, coarsegridsize, coarsegridsize*2/3, // int arithmetic best we can do here 0., 1., 0., 2./3., QUAD9); bcid = 2; // +y in 2D } else if (dim == 3) { MeshTools::Generation::build_cube (mesh, coarsegridsize, coarsegridsize*2/3, coarsegridsize, 0., 1., 0., 2./3., 0., 1., HEX27); } { // Add boundary elements corresponding to the +y boundary of our // volume mesh std::set<boundary_id_type> bcids; bcids.insert(bcid); mesh.get_boundary_info().add_elements(bcids, mesh); mesh.prepare_for_use(); } // To work around ExodusII file format limitations, we need elements // of different dimensionality to belong to different subdomains. // Our interior elements defaulted to subdomain id 0, so we'll set // boundary elements to subdomain 1. for (auto & elem : mesh.element_ptr_range()) if (elem->dim() < dim) elem->subdomain_id() = 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); // Declare the system "Heat" and its variables. HeatSystem & system = equation_systems.add_system<HeatSystem> ("Heat"); // Solve this as a steady system system.time_solver = libmesh_make_unique<SteadySolver>(system); // Initialize the system equation_systems.init (); // Set the time stepping options system.deltat = deltat; // And the nonlinear solver options DiffSolver & solver = *(system.time_solver->diff_solver().get()); 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(); // Adaptively solve the steady solution unsigned int a_step = 0; for (; a_step != max_adaptivesteps; ++a_step) { system.solve(); system.postprocess(); ErrorVector error; std::unique_ptr<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); } error_estimator->estimate_error(system, error); // Print out status at each adaptive step. Real global_error = error.l2_norm(); libMesh::out << "Adaptive step " << a_step << ": " << std::endl; if (global_tolerance != 0.) libMesh::out << "Global_error = " << global_error << std::endl; if (global_tolerance != 0.) libMesh::out << "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(); 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 == max_adaptivesteps) { system.solve(); system.postprocess(); } #ifdef LIBMESH_HAVE_EXODUS_API ExodusII_IO(mesh).write_equation_systems ("out.e", equation_systems); #endif // #ifdef LIBMESH_HAVE_EXODUS_API #ifdef LIBMESH_HAVE_GMV GMVIO(mesh).write_equation_systems ("out.gmv", equation_systems); #endif // #ifdef LIBMESH_HAVE_GMV #ifdef LIBMESH_HAVE_FPARSER // Check that we got close to the analytic solution ExactSolution exact_sol(equation_systems); const std::string exact_str = (dim == 2) ? "sin(pi*x)*sin(pi*y)" : "sin(pi*x)*sin(pi*y)*sin(pi*z)"; ParsedFunction<Number> exact_func(exact_str); exact_sol.attach_exact_value(0, &exact_func); exact_sol.compute_error("Heat", "T"); Number err = exact_sol.l2_error("Heat", "T"); // Print out the error value libMesh::out << "L2-Error is: " << err << std::endl; libmesh_assert_less(libmesh_real(err), 2e-3); #endif // #ifdef LIBMESH_HAVE_FPARSER #endif // #ifndef LIBMESH_ENABLE_AMR // All done. return 0; }
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("diff_convdiff_inv.in"); std::string find_mesh_here = infileForMesh("divided_mesh","meep.exo"); mesh.read(find_mesh_here); // 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 Diff_ConvDiff_InvSys & system = equation_systems.add_system<Diff_ConvDiff_InvSys>("Diff_ConvDiff_InvSys"); //steady-state problem system.time_solver = AutoPtr<TimeSolver>(new SteadySolver(system)); libmesh_assert_equal_to (n_timesteps, 1); std::string linearizeHere = "invHF.xda"; equation_systems.read(linearizeHere, READ, EquationSystems::READ_HEADER | EquationSystems::READ_DATA | EquationSystems::READ_ADDITIONAL_DATA); std::cout << "\n READING IN INITIAL GUESS" << std::endl; // Initialize the system //equation_systems.init (); // Set the time stepping options system.deltat = deltat; //this is ignored for SteadySolver...right? // 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) { 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(3,1.0); // 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; } // Do one last solve if necessary if (a_step == max_adaptivesteps) { using std::chrono::duration_cast; using std::chrono::milliseconds; typedef std::chrono::high_resolution_clock clock; auto start = clock::now(); system.solve(); auto end = clock::now(); std::cout << "\n Solve took " << duration_cast<milliseconds>(end-start).count() << " ms\n" << std::endl; system.postprocess(); Number QoI_computed = system.get_QoI_value("computed", 0); std::cout<< "Computed QoI is " << std::setprecision(17) << QoI_computed << std::endl; } // Advance to the next timestep in a transient problem system.time_solver->advance_timestep(); #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"; ExodusII_IO(mesh).write_timestep(file_name.str(), equation_systems, 1, /* This number indicates how many time steps are being written to the file */ system.time); equation_systems.write("invHF.xda", WRITE, EquationSystems::WRITE_DATA | EquationSystems::WRITE_ADDITIONAL_DATA); } #endif // #ifdef LIBMESH_HAVE_EXODUS_API } // All done. return 0; } //end main
void JumpErrorEstimator::estimate_error (const System& system, ErrorVector& error_per_cell, const NumericVector<Number>* solution_vector, bool estimate_parent_error) { START_LOG("estimate_error()", "JumpErrorEstimator"); /* Conventions for assigning the direction of the normal: - e & f are global element ids Case (1.) Elements are at the same level, e<f Compute the flux jump on the face and add it as a contribution to error_per_cell[e] and error_per_cell[f] ---------------------- | | | | | f | | | | | e |---> n | | | | | | | ---------------------- Case (2.) The neighbor is at a higher level. Compute the flux jump on e's face and add it as a contribution to error_per_cell[e] and error_per_cell[f] ---------------------- | | | | | | e |---> n | | | | | |-----------| f | | | | | | | | | | | | | ---------------------- */ // The current mesh const MeshBase& mesh = system.get_mesh(); // The dimensionality of the mesh const unsigned int dim = mesh.mesh_dimension(); // The number of variables in the system const unsigned int n_vars = system.n_vars(); // The DofMap for this system const DofMap& dof_map = system.get_dof_map(); // Resize the error_per_cell vector to be // the number of elements, initialize it to 0. error_per_cell.resize (mesh.max_elem_id()); std::fill (error_per_cell.begin(), error_per_cell.end(), 0.); // Declare a vector of floats which is as long as // error_per_cell above, and fill with zeros. This vector will be // used to keep track of the number of edges (faces) on each active // element which are either: // 1) an internal edge // 2) an edge on a Neumann boundary for which a boundary condition // function has been specified. // The error estimator can be scaled by the number of flux edges (faces) // which the element actually has to obtain a more uniform measure // of the error. Use floats instead of ints since in case 2 (above) // f gets 1/2 of a flux face contribution from each of his // neighbors std::vector<float> n_flux_faces (error_per_cell.size()); // Prepare current_local_solution to localize a non-standard // solution vector if necessary if (solution_vector && solution_vector != system.solution.get()) { NumericVector<Number>* newsol = const_cast<NumericVector<Number>*>(solution_vector); System &sys = const_cast<System&>(system); newsol->swap(*sys.solution); sys.update(); } // Loop over all the variables in the system for (var=0; var<n_vars; var++) { // Possibly skip this variable if (error_norm.weight(var) == 0.0) continue; // The type of finite element to use for this variable const FEType& fe_type = dof_map.variable_type (var); // Finite element objects for the same face from // different sides fe_fine = FEBase::build (dim, fe_type); fe_coarse = FEBase::build (dim, fe_type); // Build an appropriate Gaussian quadrature rule QGauss qrule (dim-1, fe_type.default_quadrature_order()); // Tell the finite element for the fine element about the quadrature // rule. The finite element for the coarse element need not know about it fe_fine->attach_quadrature_rule (&qrule); // By convention we will always do the integration // on the face of element e. We'll need its Jacobian values and // physical point locations, at least fe_fine->get_JxW(); fe_fine->get_xyz(); // Our derived classes may want to do some initialization here this->initialize(system, error_per_cell, estimate_parent_error); // The global DOF indices for elements e & f std::vector<dof_id_type> dof_indices_fine; std::vector<dof_id_type> dof_indices_coarse; // Iterate over all the active elements in the mesh // that live on this processor. MeshBase::const_element_iterator elem_it = mesh.active_local_elements_begin(); const MeshBase::const_element_iterator elem_end = mesh.active_local_elements_end(); for (; elem_it != elem_end; ++elem_it) { // e is necessarily an active element on the local processor const Elem* e = *elem_it; const dof_id_type e_id = e->id(); #ifdef LIBMESH_ENABLE_AMR // See if the parent of element e has been examined yet; // if not, we may want to compute the estimator on it const Elem* parent = e->parent(); // We only can compute and only need to compute on // parents with all active children bool compute_on_parent = true; if (!parent || !estimate_parent_error) compute_on_parent = false; else for (unsigned int c=0; c != parent->n_children(); ++c) if (!parent->child(c)->active()) compute_on_parent = false; if (compute_on_parent && !error_per_cell[parent->id()]) { // Compute a projection onto the parent DenseVector<Number> Uparent; FEBase::coarsened_dof_values(*(system.solution), dof_map, parent, Uparent, var, false); // Loop over the neighbors of the parent for (unsigned int n_p=0; n_p<parent->n_neighbors(); n_p++) { if (parent->neighbor(n_p) != NULL) // parent has a neighbor here { // Find the active neighbors in this direction std::vector<const Elem*> active_neighbors; parent->neighbor(n_p)-> active_family_tree_by_neighbor(active_neighbors, parent); // Compute the flux to each active neighbor for (unsigned int a=0; a != active_neighbors.size(); ++a) { const Elem *f = active_neighbors[a]; // FIXME - what about when f->level < // parent->level()?? if (f->level() >= parent->level()) { fine_elem = f; coarse_elem = parent; Ucoarse = Uparent; dof_map.dof_indices (fine_elem, dof_indices_fine, var); const unsigned int n_dofs_fine = libmesh_cast_int<unsigned int>(dof_indices_fine.size()); Ufine.resize(n_dofs_fine); for (unsigned int i=0; i<n_dofs_fine; i++) Ufine(i) = system.current_solution(dof_indices_fine[i]); this->reinit_sides(); this->internal_side_integration(); error_per_cell[fine_elem->id()] += static_cast<ErrorVectorReal>(fine_error); error_per_cell[coarse_elem->id()] += static_cast<ErrorVectorReal>(coarse_error); // Keep track of the number of internal flux // sides found on each element n_flux_faces[fine_elem->id()]++; n_flux_faces[coarse_elem->id()] += this->coarse_n_flux_faces_increment(); } } } else if (integrate_boundary_sides) { fine_elem = parent; Ufine = Uparent; // Reinitialize shape functions on the fine element side fe_fine->reinit (fine_elem, fine_side); if (this->boundary_side_integration()) { error_per_cell[fine_elem->id()] += static_cast<ErrorVectorReal>(fine_error); n_flux_faces[fine_elem->id()]++; } } } } #endif // #ifdef LIBMESH_ENABLE_AMR // If we do any more flux integration, e will be the fine element fine_elem = e; // Loop over the neighbors of element e for (unsigned int n_e=0; n_e<e->n_neighbors(); n_e++) { fine_side = n_e; if (e->neighbor(n_e) != NULL) // e is not on the boundary { const Elem* f = e->neighbor(n_e); const dof_id_type f_id = f->id(); // Compute flux jumps if we are in case 1 or case 2. if ((f->active() && (f->level() == e->level()) && (e_id < f_id)) || (f->level() < e->level())) { // f is now the coarse element coarse_elem = f; // Get the DOF indices for the two elements dof_map.dof_indices (fine_elem, dof_indices_fine, var); dof_map.dof_indices (coarse_elem, dof_indices_coarse, var); // The number of DOFS on each element const unsigned int n_dofs_fine = libmesh_cast_int<unsigned int>(dof_indices_fine.size()); const unsigned int n_dofs_coarse = libmesh_cast_int<unsigned int>(dof_indices_coarse.size()); Ufine.resize(n_dofs_fine); Ucoarse.resize(n_dofs_coarse); // The local solutions on each element for (unsigned int i=0; i<n_dofs_fine; i++) Ufine(i) = system.current_solution(dof_indices_fine[i]); for (unsigned int i=0; i<n_dofs_coarse; i++) Ucoarse(i) = system.current_solution(dof_indices_coarse[i]); this->reinit_sides(); this->internal_side_integration(); error_per_cell[fine_elem->id()] += static_cast<ErrorVectorReal>(fine_error); error_per_cell[coarse_elem->id()] += static_cast<ErrorVectorReal>(coarse_error); // Keep track of the number of internal flux // sides found on each element n_flux_faces[fine_elem->id()]++; n_flux_faces[coarse_elem->id()] += this->coarse_n_flux_faces_increment(); } // end if (case1 || case2) } // if (e->neigbor(n_e) != NULL) // Otherwise, e is on the boundary. If it happens to // be on a Dirichlet boundary, we need not do anything. // On the other hand, if e is on a Neumann (flux) boundary // with grad(u).n = g, we need to compute the additional residual // (h * \int |g - grad(u_h).n|^2 dS)^(1/2). // We can only do this with some knowledge of the boundary // conditions, i.e. the user must have attached an appropriate // BC function. else { if (integrate_boundary_sides) { // Reinitialize shape functions on the fine element side fe_fine->reinit (fine_elem, fine_side); // Get the DOF indices dof_map.dof_indices (fine_elem, dof_indices_fine, var); // The number of DOFS on each element const unsigned int n_dofs_fine = libmesh_cast_int<unsigned int>(dof_indices_fine.size()); Ufine.resize(n_dofs_fine); for (unsigned int i=0; i<n_dofs_fine; i++) Ufine(i) = system.current_solution(dof_indices_fine[i]); if (this->boundary_side_integration()) { error_per_cell[fine_elem->id()] += static_cast<ErrorVectorReal>(fine_error); n_flux_faces[fine_elem->id()]++; } } // end if _bc_function != NULL } // end if (e->neighbor(n_e) == NULL) } // end loop over neighbors } // End loop over active local elements } // End loop over variables // Each processor has now computed the error contribuions // for its local elements. We need to sum the vector // and then take the square-root of each component. Note // that we only need to sum if we are running on multiple // processors, and we only need to take the square-root // if the value is nonzero. There will in general be many // zeros for the inactive elements. // First sum the vector of estimated error values this->reduce_error(error_per_cell); // Compute the square-root of each component. for (std::size_t i=0; i<error_per_cell.size(); i++) if (error_per_cell[i] != 0.) error_per_cell[i] = std::sqrt(error_per_cell[i]); if (this->scale_by_n_flux_faces) { // Sum the vector of flux face counts this->reduce_error(n_flux_faces); // Sanity check: Make sure the number of flux faces is // always an integer value #ifdef DEBUG for (unsigned int i=0; i<n_flux_faces.size(); ++i) libmesh_assert_equal_to (n_flux_faces[i], static_cast<float>(static_cast<unsigned int>(n_flux_faces[i])) ); #endif // Scale the error by the number of flux faces for each element for (unsigned int i=0; i<n_flux_faces.size(); ++i) { if (n_flux_faces[i] == 0.0) // inactive or non-local element continue; //libMesh::out << "Element " << i << " has " << n_flux_faces[i] << " flux faces." << std::endl; error_per_cell[i] /= static_cast<ErrorVectorReal>(n_flux_faces[i]); } } // If we used a non-standard solution before, now is the time to fix // the current_local_solution if (solution_vector && solution_vector != system.solution.get()) { NumericVector<Number>* newsol = const_cast<NumericVector<Number>*>(solution_vector); System &sys = const_cast<System&>(system); newsol->swap(*sys.solution); sys.update(); } STOP_LOG("estimate_error()", "JumpErrorEstimator"); }
void JumpErrorEstimator::estimate_error (const System& system, ErrorVector& error_per_cell, const NumericVector<Number>* solution_vector, bool estimate_parent_error) { START_LOG("estimate_error()", "JumpErrorEstimator"); /* Conventions for assigning the direction of the normal: - e & f are global element ids Case (1.) Elements are at the same level, e<f Compute the flux jump on the face and add it as a contribution to error_per_cell[e] and error_per_cell[f] ---------------------- | | | | | f | | | | | e |---> n | | | | | | | ---------------------- Case (2.) The neighbor is at a higher level. Compute the flux jump on e's face and add it as a contribution to error_per_cell[e] and error_per_cell[f] ---------------------- | | | | | | e |---> n | | | | | |-----------| f | | | | | | | | | | | | | ---------------------- */ // The current mesh const MeshBase& mesh = system.get_mesh(); // The number of variables in the system const unsigned int n_vars = system.n_vars(); // The DofMap for this system const DofMap& dof_map = system.get_dof_map(); // Resize the error_per_cell vector to be // the number of elements, initialize it to 0. error_per_cell.resize (mesh.max_elem_id()); std::fill (error_per_cell.begin(), error_per_cell.end(), 0.); // Declare a vector of floats which is as long as // error_per_cell above, and fill with zeros. This vector will be // used to keep track of the number of edges (faces) on each active // element which are either: // 1) an internal edge // 2) an edge on a Neumann boundary for which a boundary condition // function has been specified. // The error estimator can be scaled by the number of flux edges (faces) // which the element actually has to obtain a more uniform measure // of the error. Use floats instead of ints since in case 2 (above) // f gets 1/2 of a flux face contribution from each of his // neighbors std::vector<float> n_flux_faces; if (scale_by_n_flux_faces) n_flux_faces.resize(error_per_cell.size(), 0); // Prepare current_local_solution to localize a non-standard // solution vector if necessary if (solution_vector && solution_vector != system.solution.get()) { NumericVector<Number>* newsol = const_cast<NumericVector<Number>*>(solution_vector); System &sys = const_cast<System&>(system); newsol->swap(*sys.solution); sys.update(); } fine_context.reset(new FEMContext(system)); coarse_context.reset(new FEMContext(system)); // Loop over all the variables we've been requested to find jumps in, to // pre-request for (var=0; var<n_vars; var++) { // Possibly skip this variable if (error_norm.weight(var) == 0.0) continue; // FIXME: Need to generalize this to vector-valued elements. [PB] FEBase* side_fe = NULL; const std::set<unsigned char>& elem_dims = fine_context->elem_dimensions(); for (std::set<unsigned char>::const_iterator dim_it = elem_dims.begin(); dim_it != elem_dims.end(); ++dim_it) { const unsigned char dim = *dim_it; fine_context->get_side_fe( var, side_fe, dim ); libmesh_assert_not_equal_to(side_fe->get_fe_type().family, SCALAR); side_fe->get_xyz(); } } this->init_context(*fine_context); this->init_context(*coarse_context); // Iterate over all the active elements in the mesh // that live on this processor. MeshBase::const_element_iterator elem_it = mesh.active_local_elements_begin(); const MeshBase::const_element_iterator elem_end = mesh.active_local_elements_end(); for (; elem_it != elem_end; ++elem_it) { // e is necessarily an active element on the local processor const Elem* e = *elem_it; const dof_id_type e_id = e->id(); #ifdef LIBMESH_ENABLE_AMR // See if the parent of element e has been examined yet; // if not, we may want to compute the estimator on it const Elem* parent = e->parent(); // We only can compute and only need to compute on // parents with all active children bool compute_on_parent = true; if (!parent || !estimate_parent_error) compute_on_parent = false; else for (unsigned int c=0; c != parent->n_children(); ++c) if (!parent->child(c)->active()) compute_on_parent = false; if (compute_on_parent && !error_per_cell[parent->id()]) { // Compute a projection onto the parent DenseVector<Number> Uparent; FEBase::coarsened_dof_values (*(system.solution), dof_map, parent, Uparent, false); // Loop over the neighbors of the parent for (unsigned int n_p=0; n_p<parent->n_neighbors(); n_p++) { if (parent->neighbor(n_p) != NULL) // parent has a neighbor here { // Find the active neighbors in this direction std::vector<const Elem*> active_neighbors; parent->neighbor(n_p)-> active_family_tree_by_neighbor(active_neighbors, parent); // Compute the flux to each active neighbor for (unsigned int a=0; a != active_neighbors.size(); ++a) { const Elem *f = active_neighbors[a]; // FIXME - what about when f->level < // parent->level()?? if (f->level() >= parent->level()) { fine_context->pre_fe_reinit(system, f); coarse_context->pre_fe_reinit(system, parent); libmesh_assert_equal_to (coarse_context->get_elem_solution().size(), Uparent.size()); coarse_context->get_elem_solution() = Uparent; this->reinit_sides(); // Loop over all significant variables in the system for (var=0; var<n_vars; var++) if (error_norm.weight(var) != 0.0) { this->internal_side_integration(); error_per_cell[fine_context->get_elem().id()] += static_cast<ErrorVectorReal>(fine_error); error_per_cell[coarse_context->get_elem().id()] += static_cast<ErrorVectorReal>(coarse_error); } // Keep track of the number of internal flux // sides found on each element if (scale_by_n_flux_faces) { n_flux_faces[fine_context->get_elem().id()]++; n_flux_faces[coarse_context->get_elem().id()] += this->coarse_n_flux_faces_increment(); } } } } else if (integrate_boundary_sides) { fine_context->pre_fe_reinit(system, parent); libmesh_assert_equal_to (fine_context->get_elem_solution().size(), Uparent.size()); fine_context->get_elem_solution() = Uparent; fine_context->side = n_p; fine_context->side_fe_reinit(); // If we find a boundary flux for any variable, // let's just count it as a flux face for all // variables. Otherwise we'd need to keep track of // a separate n_flux_faces and error_per_cell for // every single var. bool found_boundary_flux = false; for (var=0; var<n_vars; var++) if (error_norm.weight(var) != 0.0) { if (this->boundary_side_integration()) { error_per_cell[fine_context->get_elem().id()] += static_cast<ErrorVectorReal>(fine_error); found_boundary_flux = true; } } if (scale_by_n_flux_faces && found_boundary_flux) n_flux_faces[fine_context->get_elem().id()]++; } } } #endif // #ifdef LIBMESH_ENABLE_AMR // If we do any more flux integration, e will be the fine element fine_context->pre_fe_reinit(system, e); // Loop over the neighbors of element e for (unsigned int n_e=0; n_e<e->n_neighbors(); n_e++) { if ((e->neighbor(n_e) != NULL) || integrate_boundary_sides) { fine_context->side = n_e; fine_context->side_fe_reinit(); } if (e->neighbor(n_e) != NULL) // e is not on the boundary { const Elem* f = e->neighbor(n_e); const dof_id_type f_id = f->id(); // Compute flux jumps if we are in case 1 or case 2. if ((f->active() && (f->level() == e->level()) && (e_id < f_id)) || (f->level() < e->level())) { // f is now the coarse element coarse_context->pre_fe_reinit(system, f); this->reinit_sides(); // Loop over all significant variables in the system for (var=0; var<n_vars; var++) if (error_norm.weight(var) != 0.0) { this->internal_side_integration(); error_per_cell[fine_context->get_elem().id()] += static_cast<ErrorVectorReal>(fine_error); error_per_cell[coarse_context->get_elem().id()] += static_cast<ErrorVectorReal>(coarse_error); } // Keep track of the number of internal flux // sides found on each element if (scale_by_n_flux_faces) { n_flux_faces[fine_context->get_elem().id()]++; n_flux_faces[coarse_context->get_elem().id()] += this->coarse_n_flux_faces_increment(); } } // end if (case1 || case2) } // if (e->neigbor(n_e) != NULL) // Otherwise, e is on the boundary. If it happens to // be on a Dirichlet boundary, we need not do anything. // On the other hand, if e is on a Neumann (flux) boundary // with grad(u).n = g, we need to compute the additional residual // (h * \int |g - grad(u_h).n|^2 dS)^(1/2). // We can only do this with some knowledge of the boundary // conditions, i.e. the user must have attached an appropriate // BC function. else if (integrate_boundary_sides) { bool found_boundary_flux = false; for (var=0; var<n_vars; var++) if (error_norm.weight(var) != 0.0) if (this->boundary_side_integration()) { error_per_cell[fine_context->get_elem().id()] += static_cast<ErrorVectorReal>(fine_error); found_boundary_flux = true; } if (scale_by_n_flux_faces && found_boundary_flux) n_flux_faces[fine_context->get_elem().id()]++; } // end if (e->neighbor(n_e) == NULL) } // end loop over neighbors } // End loop over active local elements // Each processor has now computed the error contribuions // for its local elements. We need to sum the vector // and then take the square-root of each component. Note // that we only need to sum if we are running on multiple // processors, and we only need to take the square-root // if the value is nonzero. There will in general be many // zeros for the inactive elements. // First sum the vector of estimated error values this->reduce_error(error_per_cell, system.comm()); // Compute the square-root of each component. for (std::size_t i=0; i<error_per_cell.size(); i++) if (error_per_cell[i] != 0.) error_per_cell[i] = std::sqrt(error_per_cell[i]); if (this->scale_by_n_flux_faces) { // Sum the vector of flux face counts this->reduce_error(n_flux_faces, system.comm()); // Sanity check: Make sure the number of flux faces is // always an integer value #ifdef DEBUG for (unsigned int i=0; i<n_flux_faces.size(); ++i) libmesh_assert_equal_to (n_flux_faces[i], static_cast<float>(static_cast<unsigned int>(n_flux_faces[i])) ); #endif // Scale the error by the number of flux faces for each element for (unsigned int i=0; i<n_flux_faces.size(); ++i) { if (n_flux_faces[i] == 0.0) // inactive or non-local element continue; //libMesh::out << "Element " << i << " has " << n_flux_faces[i] << " flux faces." << std::endl; error_per_cell[i] /= static_cast<ErrorVectorReal>(n_flux_faces[i]); } } // If we used a non-standard solution before, now is the time to fix // the current_local_solution if (solution_vector && solution_vector != system.solution.get()) { NumericVector<Number>* newsol = const_cast<NumericVector<Number>*>(solution_vector); System &sys = const_cast<System&>(system); newsol->swap(*sys.solution); sys.update(); } STOP_LOG("estimate_error()", "JumpErrorEstimator"); }
void assemble_and_solve(MeshBase & mesh, EquationSystems & equation_systems) { mesh.print_info(); LinearImplicitSystem & system = equation_systems.add_system<LinearImplicitSystem> ("Poisson"); unsigned int u_var = system.add_variable("u", FIRST, LAGRANGE); system.attach_assemble_function (assemble_poisson); // the cube has boundaries IDs 0, 1, 2, 3, 4 and 5 std::set<boundary_id_type> boundary_ids; for (int j = 0; j<6; ++j) boundary_ids.insert(j); // Create a vector storing the variable numbers which the BC applies to std::vector<unsigned int> variables(1); variables[0] = u_var; ZeroFunction<> zf; DirichletBoundary dirichlet_bc(boundary_ids, variables, &zf); system.get_dof_map().add_dirichlet_boundary(dirichlet_bc); equation_systems.init(); equation_systems.print_info(); #ifdef LIBMESH_ENABLE_AMR MeshRefinement mesh_refinement(mesh); mesh_refinement.refine_fraction() = 0.7; mesh_refinement.coarsen_fraction() = 0.3; mesh_refinement.max_h_level() = 5; const unsigned int max_r_steps = 2; for (unsigned int r_step=0; r_step<=max_r_steps; r_step++) { system.solve(); if (r_step != max_r_steps) { ErrorVector error; KellyErrorEstimator error_estimator; error_estimator.estimate_error(system, error); libMesh::out << "Error estimate\nl2 norm = " << error.l2_norm() << "\nmaximum = " << error.maximum() << std::endl; mesh_refinement.flag_elements_by_error_fraction (error); mesh_refinement.refine_and_coarsen_elements(); equation_systems.reinit(); } } #else system.solve(); #endif }
int main(int argc, char** argv) { // Initialize the library. This is necessary because the library // may depend on a number of other libraries (i.e. MPI and PETSc) // that require initialization before use. When the LibMeshInit // object goes out of scope, other libraries and resources are // finalized. LibMeshInit init (argc, argv); // Skip adaptive examples on a non-adaptive libMesh build #ifndef LIBMESH_ENABLE_AMR libmesh_example_requires(false, "--enable-amr"); #else // Create a mesh, with dimension to be overridden later, on the // default MPI communicator. Mesh mesh(init.comm()); GetPot command_line (argc, argv); int n = 4; if ( command_line.search(1, "-n") ) n = command_line.next(n); // Build a 1D mesh with 4 elements from x=0 to x=1, using // EDGE3 (i.e. quadratic) 1D elements. They are called EDGE3 elements // because a quadratic element contains 3 nodes. MeshTools::Generation::build_line(mesh,n,0.,1.,EDGE3); // Define the equation systems object and the system we are going // to solve. See Introduction Example 2 for more details. EquationSystems equation_systems(mesh); LinearImplicitSystem& system = equation_systems.add_system <LinearImplicitSystem>("1D"); // Add a variable "u" to the system, using second-order approximation system.add_variable("u",SECOND); // Give the system a pointer to the matrix assembly function. This // will be called when needed by the library. system.attach_assemble_function(assemble_1D); // Define the mesh refinement object that takes care of adaptively // refining the mesh. MeshRefinement mesh_refinement(mesh); // These parameters determine the proportion of elements that will // be refined and coarsened. Any element within 30% of the maximum // error on any element will be refined, and any element within 30% // of the minimum error on any element might be coarsened mesh_refinement.refine_fraction() = 0.7; mesh_refinement.coarsen_fraction() = 0.3; // We won't refine any element more than 5 times in total mesh_refinement.max_h_level() = 5; // Initialize the data structures for the equation system. equation_systems.init(); // Refinement parameters const unsigned int max_r_steps = 5; // Refine the mesh 5 times // Define the refinement loop for(unsigned int r_step=0; r_step<=max_r_steps; r_step++) { // Solve the equation system equation_systems.get_system("1D").solve(); // We need to ensure that the mesh is not refined on the last iteration // of this loop, since we do not want to refine the mesh unless we are // going to solve the equation system for that refined mesh. if(r_step != max_r_steps) { // Error estimation objects, see Adaptivity Example 2 for details ErrorVector error; KellyErrorEstimator error_estimator; // Compute the error for each active element error_estimator.estimate_error(system, error); // Output error estimate magnitude libMesh::out << "Error estimate\nl2 norm = " << error.l2_norm() << "\nmaximum = " << error.maximum() << std::endl; // Flag elements to be refined and coarsened mesh_refinement.flag_elements_by_error_fraction (error); // Perform refinement and coarsening mesh_refinement.refine_and_coarsen_elements(); // Reinitialize the equation_systems object for the newly refined // mesh. One of the steps in this is project the solution onto the // new mesh equation_systems.reinit(); } } // Construct gnuplot plotting object, pass in mesh, title of plot // and boolean to indicate use of grid in plot. The grid is used to // show the edges of each element in the mesh. GnuPlotIO plot(mesh,"Adaptivity Example 1", GnuPlotIO::GRID_ON); // Write out script to be called from within gnuplot: // Load gnuplot, then type "call 'gnuplot_script'" from gnuplot prompt plot.write_equation_systems("gnuplot_script",equation_systems); #endif // #ifndef LIBMESH_ENABLE_AMR // All done. libMesh objects are destroyed here. Because the // LibMeshInit object was created first, its destruction occurs // last, and it's destructor finalizes any external libraries and // checks for leaked memory. return 0; }
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 // Parse the input file GetPot input_file("adaptivity_ex3.in"); // Read in parameters from the input file const unsigned int max_r_steps = input_file("max_r_steps", 3); const unsigned int max_r_level = input_file("max_r_level", 3); const Real refine_percentage = input_file("refine_percentage", 0.5); const Real coarsen_percentage = input_file("coarsen_percentage", 0.5); const unsigned int uniform_refine = input_file("uniform_refine",0); const std::string refine_type = input_file("refinement_type", "h"); const std::string approx_type = input_file("approx_type", "LAGRANGE"); const unsigned int approx_order = input_file("approx_order", 1); const std::string element_type = input_file("element_type", "tensor"); const int extra_error_quadrature = input_file("extra_error_quadrature", 0); const int max_linear_iterations = input_file("max_linear_iterations", 5000); const bool output_intermediate = input_file("output_intermediate", false); dim = input_file("dimension", 2); const std::string indicator_type = input_file("indicator_type", "kelly"); singularity = input_file("singularity", true); // Skip higher-dimensional examples on a lower-dimensional libMesh build libmesh_example_assert(dim <= LIBMESH_DIM, "2D/3D support"); // Output file for plotting the error as a function of // the number of degrees of freedom. std::string approx_name = ""; if (element_type == "tensor") approx_name += "bi"; if (approx_order == 1) approx_name += "linear"; else if (approx_order == 2) approx_name += "quadratic"; else if (approx_order == 3) approx_name += "cubic"; else if (approx_order == 4) approx_name += "quartic"; std::string output_file = approx_name; output_file += "_"; output_file += refine_type; if (uniform_refine == 0) output_file += "_adaptive.m"; else output_file += "_uniform.m"; std::ofstream out (output_file.c_str()); out << "% dofs L2-error H1-error" << std::endl; out << "e = [" << std::endl; // Create a mesh. Mesh mesh; // Read in the mesh if (dim == 1) MeshTools::Generation::build_line(mesh,1,-1.,0.); else if (dim == 2) mesh.read("lshaped.xda"); else mesh.read("lshaped3D.xda"); // Use triangles if the config file says so if (element_type == "simplex") MeshTools::Modification::all_tri(mesh); // We used first order elements to describe the geometry, // but we may need second order elements to hold the degrees // of freedom if (approx_order > 1 || refine_type != "h") mesh.all_second_order(); // Mesh Refinement object MeshRefinement mesh_refinement(mesh); mesh_refinement.refine_fraction() = refine_percentage; mesh_refinement.coarsen_fraction() = coarsen_percentage; mesh_refinement.max_h_level() = max_r_level; // Create an equation systems object. EquationSystems equation_systems (mesh); // Declare the system and its variables. // Creates a system named "Laplace" LinearImplicitSystem& system = equation_systems.add_system<LinearImplicitSystem> ("Laplace"); // Adds the variable "u" to "Laplace", using // the finite element type and order specified // in the config file system.add_variable("u", static_cast<Order>(approx_order), Utility::string_to_enum<FEFamily>(approx_type)); // Give the system a pointer to the matrix assembly // function. system.attach_assemble_function (assemble_laplace); // Initialize the data structures for the equation system. equation_systems.init(); // Set linear solver max iterations equation_systems.parameters.set<unsigned int>("linear solver maximum iterations") = max_linear_iterations; // Linear solver tolerance. equation_systems.parameters.set<Real>("linear solver tolerance") = std::pow(TOLERANCE, 2.5); // Prints information about the system to the screen. equation_systems.print_info(); // Construct ExactSolution object and attach solution functions ExactSolution exact_sol(equation_systems); exact_sol.attach_exact_value(exact_solution); exact_sol.attach_exact_deriv(exact_derivative); // Use higher quadrature order for more accurate error results exact_sol.extra_quadrature_order(extra_error_quadrature); // A refinement loop. for (unsigned int r_step=0; r_step<max_r_steps; r_step++) { std::cout << "Beginning Solve " << r_step << std::endl; // Solve the system "Laplace", just like example 2. system.solve(); std::cout << "System has: " << equation_systems.n_active_dofs() << " degrees of freedom." << std::endl; std::cout << "Linear solver converged at step: " << system.n_linear_iterations() << ", final residual: " << system.final_linear_residual() << std::endl; #ifdef LIBMESH_HAVE_EXODUS_API // After solving the system write the solution // to a ExodusII-formatted plot file. if (output_intermediate) { OStringStream outfile; outfile << "lshaped_" << r_step << ".e"; ExodusII_IO (mesh).write_equation_systems (outfile.str(), equation_systems); } #endif // #ifdef LIBMESH_HAVE_EXODUS_API // Compute the error. exact_sol.compute_error("Laplace", "u"); // Print out the error values std::cout << "L2-Error is: " << exact_sol.l2_error("Laplace", "u") << std::endl; std::cout << "H1-Error is: " << exact_sol.h1_error("Laplace", "u") << std::endl; // Print to output file out << equation_systems.n_active_dofs() << " " << exact_sol.l2_error("Laplace", "u") << " " << exact_sol.h1_error("Laplace", "u") << std::endl; // Possibly refine the mesh if (r_step+1 != max_r_steps) { std::cout << " Refining the mesh..." << std::endl; if (uniform_refine == 0) { // The \p ErrorVector is a particular \p StatisticsVector // for computing error information on a finite element mesh. ErrorVector error; if (indicator_type == "exact") { // The \p ErrorEstimator class interrogates a // finite element solution and assigns to each // element a positive error value. // This value is used for deciding which elements to // refine and which to coarsen. // For these simple test problems, we can use // numerical quadrature of the exact error between // the approximate and analytic solutions. // However, for real problems, we would need an error // indicator which only relies on the approximate // solution. ExactErrorEstimator error_estimator; error_estimator.attach_exact_value(exact_solution); error_estimator.attach_exact_deriv(exact_derivative); // We optimize in H1 norm, the default // error_estimator.error_norm = H1; // Compute the error for each active element using // the provided indicator. Note in general you // will need to provide an error estimator // specifically designed for your application. error_estimator.estimate_error (system, error); } else if (indicator_type == "patch") { // The patch recovery estimator should give a // good estimate of the solution interpolation // error. PatchRecoveryErrorEstimator error_estimator; error_estimator.estimate_error (system, error); } else if (indicator_type == "uniform") { // Error indication based on uniform refinement // is reliable, but very expensive. UniformRefinementEstimator error_estimator; error_estimator.estimate_error (system, error); } else { libmesh_assert_equal_to (indicator_type, "kelly"); // The Kelly error estimator is based on // an error bound for the Poisson problem // on linear elements, but is useful for // driving adaptive refinement in many problems KellyErrorEstimator error_estimator; error_estimator.estimate_error (system, error); } // Write out the error distribution OStringStream ss; ss << r_step; #ifdef LIBMESH_HAVE_EXODUS_API std::string error_output = "error_"+ss.str()+".e"; #else std::string error_output = "error_"+ss.str()+".gmv"; #endif error.plot_error( error_output, mesh ); // This takes the error in \p error and decides which elements // will be coarsened or refined. Any element within 20% of the // maximum error on any element will be refined, and any // element within 10% of the minimum error on any element might // be coarsened. Note that the elements flagged for refinement // will be refined, but those flagged for coarsening _might_ be // coarsened. mesh_refinement.flag_elements_by_error_fraction (error); // If we are doing adaptive p refinement, we want // elements flagged for that instead. if (refine_type == "p") mesh_refinement.switch_h_to_p_refinement(); // If we are doing "matched hp" refinement, we // flag elements for both h and p if (refine_type == "matchedhp") mesh_refinement.add_p_to_h_refinement(); // If we are doing hp refinement, we // try switching some elements from h to p if (refine_type == "hp") { HPCoarsenTest hpselector; hpselector.select_refinement(system); } // If we are doing "singular hp" refinement, we // try switching most elements from h to p if (refine_type == "singularhp") { // This only differs from p refinement for // the singular problem libmesh_assert (singularity); HPSingularity hpselector; // Our only singular point is at the origin hpselector.singular_points.push_back(Point()); hpselector.select_refinement(system); } // This call actually refines and coarsens the flagged // elements. mesh_refinement.refine_and_coarsen_elements(); } else if (uniform_refine == 1) { if (refine_type == "h" || refine_type == "hp" || refine_type == "matchedhp") mesh_refinement.uniformly_refine(1); if (refine_type == "p" || refine_type == "hp" || refine_type == "matchedhp") mesh_refinement.uniformly_p_refine(1); } // This call reinitializes the \p EquationSystems object for // the newly refined mesh. One of the steps in the // reinitialization is projecting the \p solution, // \p old_solution, etc... vectors from the old mesh to // the current one. equation_systems.reinit (); } } #ifdef LIBMESH_HAVE_EXODUS_API // Write out the solution // After solving the system write the solution // to a ExodusII-formatted plot file. ExodusII_IO (mesh).write_equation_systems ("lshaped.e", equation_systems); #endif // #ifdef LIBMESH_HAVE_EXODUS_API // Close up the output file. out << "];" << std::endl; out << "hold on" << std::endl; out << "plot(e(:,1), e(:,2), 'bo-');" << std::endl; out << "plot(e(:,1), e(:,3), 'ro-');" << std::endl; // out << "set(gca,'XScale', 'Log');" << std::endl; // out << "set(gca,'YScale', 'Log');" << std::endl; out << "xlabel('dofs');" << std::endl; out << "title('" << approx_name << " elements');" << std::endl; out << "legend('L2-error', 'H1-error');" << std::endl; // out << "disp('L2-error linear fit');" << std::endl; // out << "polyfit(log10(e(:,1)), log10(e(:,2)), 1)" << std::endl; // out << "disp('H1-error linear fit');" << std::endl; // out << "polyfit(log10(e(:,1)), log10(e(:,3)), 1)" << std::endl; #endif // #ifndef LIBMESH_ENABLE_AMR // All done. return 0; }