Skip to content
This repository was archived by the owner on Oct 30, 2023. It is now read-only.

Example adaptive interpolation

Felix Schindler edited this page Oct 21, 2018 · 4 revisions

In this example we adaptively refine a grid to minimze the L^2 interpolation error when interpolating cosh in a piecewise constant finite volume space. This only serves to study how to keep data which is associated with the grid (say a DoF vector) across adaptation, that is: what is required for restriction/prolongation. (Thus it is a quite artificial example.) We simply use the local interpolation error as indicators to refine grid elements with a Dörfler strategy (and also coarsen, to have a reason to study restriction).

Update begins: parts of the original example have found their way into the library by now. The updated example can be found in the new-master branch (the old example is kept below for future reference). The relevant changes in the main are the use of the AdaptiveHelper, parts of the main now look like:

    // fake solution to demonstrate restriction/prolongation
    auto fv_space = GDT::make_finite_volume_space<1>(grid_view);
    auto current_solution = GDT::make_discrete_function<V>(fv_space);
    GDT::interpolate(cosh, current_solution);

    // the main adaptation loop
    auto helper = make_adaptation_helper(grid, current_solution);
    const double tolerance = DXTC_CONFIG_GET("tolerance", 1e-3);
    size_t counter = 0;
    while (true) {
      logger.info() << "step " << counter << ", space has " << fv_space.mapper().size() << " DoFs" << std::endl;
      current_solution.visualize("interpolated_function_" + XT::Common::to_string(counter));

      // compute local L^2 errors
      const auto local_errors = compute_local_l2_norms(current_solution - cosh.as_grid_function(grid_view), grid_view);
      logger.info() << "  relative interpolation error: " << local_errors.l2_norm() / reference_norm << std::endl;
      if (local_errors.l2_norm() / reference_norm < tolerance) {
        logger.info() << "target accuracy reached, terminating!" << std::endl;
        break;
      }

      // use these as indicators for grid refinement
      mark_elements(grid,
                    grid_view,
                    local_errors,
                    DXTC_CONFIG_GET("mark.refine_factor", 0.25),
                    DXTC_CONFIG_GET("mark.coarsen_factor", 0.01));

      // adapt the grid (restricts/prolongs the solution)
      helper.preAdapt();
      helper.adapt();
      helper.postAdapt();

      // now that the grid is adapted, interpolate the function on the new grid
      GDT::interpolate(cosh, current_solution);

      ++counter;
    }

update ends: below is the original code

The following code is simply the complete dune-gdt/examples/adaptive-integration.cc file, which currently resides somewhere in Felix' repository, but I copy it here for future reference. Whenever you encounter something like

DXTC_CONFIG_GET("key", value)

in the example, you can pass these as runtime arguments to the executable in the sense of

./examples__adaptive-interpolation -key value

See the very bottom for a possible output. This is the example:

// This file is part of the dune-gdt project:
//   https://github.com/dune-community/dune-gdt
// Copyright 2010-2018 dune-gdt developers and contributors. All rights reserved.
// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
//          with "runtime exception" (http://www.dune-project.org/license.html)
// Authors:
//   Felix Schindler (2018)

#include "config.h"

#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <exception>
#include <utility>

#include <dune/common/dynvector.hh>
#include <dune/common/exceptions.hh>
#include <dune/common/parallel/mpihelper.hh>
#include <dune/grid/common/rangegenerators.hh>
#include <dune/grid/utility/persistentcontainer.hh>

#include <dune/xt/common/float_cmp.hh>
#include <dune/xt/common/string.hh>
#include <dune/xt/common/timedlogging.hh>
#include <dune/xt/la/container/conversion.hh>
#include <dune/xt/la/container/common/vector/dense.hh>
#include <dune/xt/grid/entity.hh>
#include <dune/xt/grid/grids.hh>
#include <dune/xt/grid/gridprovider/cube.hh>
#include <dune/xt/grid/type_traits.hh>
#include <dune/xt/grid/walker.hh>
#include <dune/xt/functions/lambda/function.hh>

#include <dune/gdt/discretefunction/default.hh>
#include <dune/gdt/interpolations.hh>
#include <dune/gdt/local/bilinear-forms/integrals.hh>
#include <dune/gdt/local/integrands/product.hh>
#include <dune/gdt/spaces/l2/finite-volume.hh>

using namespace Dune;


// some global defines
using G = ONED_1D;
static const constexpr size_t d = G::dimension;
using GV = typename G::LeafGridView;
using E = XT::Grid::extract_entity_t<GV>;
using V = XT::LA::CommonDenseVector<double>;


V compute_local_l2_norms(const XT::Functions::GridFunctionInterface<E>& func, const GV& grid_view)
{
  // the best index set for a grid view of arbitrary elements is a scalar FV space ...
  auto fv_space = GDT::make_finite_volume_space<1>(grid_view);
  // ... and the best way to associate data with grid elements is a corresponding discrete function
  auto df = GDT::make_discrete_function<V>(fv_space);
  // compute local L^2 products
  auto walker = XT::Grid::make_walker(grid_view);
  walker.append(/*prepare nothing*/ []() {},
                /*apply local*/
                [&](const auto& element) {
                  auto local_func = func.local_function();
                  local_func->bind(element);
                  // models \int_element 1*phi*psi dx for any phi/psi
                  const GDT::LocalElementIntegralBilinearForm<E> local_l2_product(
                      GDT::LocalElementProductIntegrand<E>(1.));
                  // evaluate local product with phi = psi = local_func
                  const auto element_l2_error2 = local_l2_product.apply2(*local_func, *local_func)[0][0];
                  // store in entry for this element (we keep them unsquared, makes for easier computation of total)
                  df.local_discrete_function(element)->dofs()[0] = std::sqrt(element_l2_error2);
                },
                /*finalize*/ []() {});
  walker.walk(/*thread_parallel=*/true);
  return df.dofs().vector();
} // ... compute_local_l2_norms(...)


void mark_elements(
    G& grid, const GV& grid_view, const V& local_indicators, const double& refine_factor, const double& coarsen_factor)
{
  // mark all elements with a contribution above coarsen_threashhold for coarsening
  // * therefore: as above, use a scalar FV space as index mapper
  auto fv_space = GDT::make_finite_volume_space<1>(grid_view);
  //              ... and a corresponding discrete function to associate data with grid elements
  //  auto local_indicators_function = GDT::make_discrete_function(fv_space, local_indicators);
  //  auto local_indicators_local_function = local_indicators_function.local_discrete_function();
  // prepare dörfler marking
  std::vector<std::pair<double, size_t>> accumulated_sorted_local_indicators(local_indicators.size());
  for (size_t ii = 0; ii < local_indicators.size(); ++ii)
    accumulated_sorted_local_indicators[ii] = {std::pow(local_indicators[ii], 2), ii};
  std::sort(accumulated_sorted_local_indicators.begin(),
            accumulated_sorted_local_indicators.end(),
            [](const auto& a, const auto& b) { return a.first < b.first; });
  for (size_t ii = 1; ii < accumulated_sorted_local_indicators.size(); ++ii)
    accumulated_sorted_local_indicators[ii].first += accumulated_sorted_local_indicators[ii - 1].first;
  // select smallest coarsen_factor contributions for coarsening
  std::set<size_t> elements_to_be_coarsened;
  const double total_indicators = std::pow(local_indicators.l2_norm(), 2);
  for (const auto& indicator : accumulated_sorted_local_indicators)
    if (indicator.first < (coarsen_factor * total_indicators))
      elements_to_be_coarsened.insert(indicator.second);
  // select largest refine_factor contributions
  std::set<size_t> elements_to_be_refined;
  for (const auto& indicator : accumulated_sorted_local_indicators)
    if (indicator.first > ((1 - refine_factor) * total_indicators))
      elements_to_be_refined.insert(indicator.second);
  // mark elements
  size_t corsend_elements = 0;
  size_t refined_elements = 0;
  for (auto&& element : elements(grid_view)) {
    const size_t index = fv_space.mapper().global_indices(element)[0];
    bool coarsened = false;
    if (std::find(elements_to_be_coarsened.begin(), elements_to_be_coarsened.end(), index)
        != elements_to_be_coarsened.end()) {
      grid.mark(/*coarsen*/ -1, element);
      coarsened = true;
    }
    if (std::find(elements_to_be_refined.begin(), elements_to_be_refined.end(), index)
        != elements_to_be_refined.end()) {
      grid.mark(/*refine and overwrite coarsening if present*/ 2, element);
      refined_elements += 1;
      coarsened = false;
    }
    if (coarsened)
      ++corsend_elements;
  }
  auto logger = XT::Common::TimedLogger().get("mark_elements");
  logger.info() << "marked " << corsend_elements << "/" << fv_space.mapper().size() << " elements for coarsening and "
                << refined_elements << "/" << fv_space.mapper().size() << " elements for refinement" << std::endl;
} // ... mark_elements(...)


/**
 * \note At some point this functionality should find its way into the spaces.
 *
 * On the domain covered by the coarser element, this computes an L^2 projection of the function defined on the finer
 * elements (which corresponds to weighted averaging in the FV case).
 */
void compute_restriction(const E& element, PersistentContainer<G, DynamicVector<double>>& persistent_data)
{
  auto& element_restriction_data = persistent_data[element];
  if (element_restriction_data.size() == 0) {
    DUNE_THROW_IF(element.isLeaf(), InvalidStateException, "");
    for (auto&& child_element : descendantElements(element, element.level() + 1)) {
      // ensure we have data on all descendant elements of the next level
      compute_restriction(child_element, persistent_data);
      // compute restriction
      auto child_restriction_data = persistent_data[child_element];
      child_restriction_data *= child_element.geometry().volume();
      if (element_restriction_data.size() == 0)
        element_restriction_data = child_restriction_data; // initialize with child data
      else
        element_restriction_data += child_restriction_data;
    }
    // now we have assembled h*value
    element_restriction_data /= element.geometry().volume();
  }
} // ... compute_restriction(...)


int main(int argc, char* argv[])
{
  try {
    MPIHelper::instance(argc, argv);
    if (argc > 1)
      DXTC_CONFIG.read_options(argc, argv);
    XT::Common::TimedLogger().create(DXTC_CONFIG_GET("logger.info", 1), DXTC_CONFIG_GET("logger.debug", -1));
    auto logger = XT::Common::TimedLogger().get("main");

    // initial grid
    const double domain_length = 10;
    auto grid_ptr =
        XT::Grid::make_cube_grid<G>(/*lower_left=*/0., /*upper_right=*/domain_length, /*num_elements=*/1).grid_ptr();
    auto& grid = *grid_ptr;
    auto grid_view = grid.leafGridView();

    // funtion to interpolate
    const XT::Functions::LambdaFunction<d> cosh(/*approx_pol_order=*/10,
                                                [](const auto& x, const auto& /*param*/) { return std::cosh(x); });
    const auto reference_norm = compute_local_l2_norms(cosh.as_grid_function(grid_view), grid_view).l2_norm();

    // fake solution vector to demonstrate prolongation
    V current_solution_vector = GDT::interpolate<V>(cosh, GDT::make_finite_volume_space<1>(grid_view)).dofs().vector();

    // the main adaptation loop
    const double tolerance = DXTC_CONFIG_GET("tolerance", 1e-3);
    size_t counter = 0;
    while (true) {
      // interpret current_solution as a discrete FV function on the current grid
      auto fv_space = GDT::make_finite_volume_space<1>(grid_view);
      auto current_solution = GDT::make_discrete_function(fv_space, current_solution_vector);
      logger.info() << "step " << counter << ", space has " << fv_space.mapper().size() << " DoFs" << std::endl;
      current_solution.visualize("interpolated_function_" + XT::Common::to_string(counter));

      // compute local L^2 errors
      const auto local_errors = compute_local_l2_norms(current_solution - cosh.as_grid_function(grid_view), grid_view);
      logger.info() << "  relative interpolation error: " << local_errors.l2_norm() / reference_norm << std::endl;
      if (local_errors.l2_norm() / reference_norm < tolerance) {
        logger.info() << "target accuracy reached, terminating!" << std::endl;
        break;
      }

      // use these as indicators for grid refinement
      mark_elements(grid,
                    grid_view,
                    local_errors,
                    DXTC_CONFIG_GET("mark.refine_factor", 0.25),
                    DXTC_CONFIG_GET("mark.coarsen_factor", 0.01));

      // now the actual adaptation
      // * call preadapt, this will mark elements which might vanish due to coarsening
      const bool elements_may_be_coarsened = grid.preAdapt();
      //   - now we need some persistent storage to keep our data: all kept leaf elements might change their indices and
      //                                                           all coarsened elements might vanish
      //     this should keep local DoF vectors, which can be converted to DynamicVector<double>
      PersistentContainer<G, DynamicVector<double>> persistent_data(grid, 0);
      //   - we also need a container to recall those elements where we need to restrict to
      PersistentContainer<G, bool> restriction_required(grid, 0, false);
      //   - first of all, walk the current leaf of the grid ...
      auto current_local_solution = current_solution.local_discrete_function();
      for (auto&& element : elements(grid_view)) {
        current_local_solution->bind(element);
        //   ... to store the local DoFs ...
        persistent_data[element] = XT::LA::convert_to<DynamicVector<double>>(current_local_solution->dofs());
        //   ... and to mark father elements
        if (element.mightVanish())
          restriction_required[element.father()] = true;
      }
      //   - now walk the grid up all coarser levels ...
      if (elements_may_be_coarsened) {
        for (int level = grid.maxLevel() - 1; level >= 0; --level) {
          auto level_view = grid.levelGridView(level);
          for (auto&& element : elements(level_view)) {
            // ... to compute restrictions ...
            if (restriction_required[element])
              compute_restriction(element, persistent_data);
            // ... and to mark father elements
            if (element.mightVanish())
              restriction_required[element.father()] = true;
          }
        }
      }
      // from here on, restriction_required is not required any more
      // * next, we actually adapt the grid
      grid.adapt();
      //   - from now on, fv_space, current_solution and current_local_solution must not be used any more,
      //     this is not yet implemented!
      //   - clean up data structures
      persistent_data.resize();
      persistent_data.shrinkToFit();
      // * create a new space, resize the solution vector and create a new discrete function to hold the
      //   prolongated/restricted values
      auto new_fv_space = GDT::make_finite_volume_space<1>(grid_view);
      current_solution_vector.resize(new_fv_space.mapper().size());
      auto new_solution = GDT::make_discrete_function(new_fv_space, current_solution_vector);
      // * copy the persistent data back to the discrete function ...
      auto new_local_solution = new_solution.local_discrete_function();
      for (auto&& element : elements(grid_view)) {
        new_local_solution->bind(element);
        if (element.isNew()) {
          // ... by prolongation from the father element ...
          const auto& father_data = persistent_data[element.father()];
          DUNE_THROW_IF(father_data.size() == 0, InvalidStateException, "");
          new_local_solution->dofs().assign_from(father_data);
        } else {
          // ... or by copying the data from this unchanged element
          const auto& element_data = persistent_data[element];
          DUNE_THROW_IF(element_data.size() == 0, InvalidStateException, "");
          new_local_solution->dofs().assign_from(element_data);
        }
      }
      // * clean up the grid
      grid.postAdapt();
      // from here on, persistent_data is not required any more
      new_solution.visualize("interpolated_function_after_refinement_" + XT::Common::to_string(counter));

      // now that the grid is adapted, interpolate the function on the new grid
      GDT::interpolate(cosh, new_solution);

      ++counter;
    }

    double min_h = std::numeric_limits<double>::max();
    for (auto&& element : elements(grid_view))
      min_h = std::min(min_h, XT::Grid::diameter(element));

    logger.info() << "\nA uniformly refinement grid of similar accuracy would have roughly required "
                  << int(domain_length / min_h) << " DoFs" << std::endl;

  } catch (Exception& e) {
    std::cerr << "\nDUNE reported error: " << e.what() << std::endl;
    return EXIT_FAILURE;
  } catch (std::exception& e) {
    std::cerr << "\nstl reported error: " << e.what() << std::endl;
    return EXIT_FAILURE;
  } catch (...) {
    std::cerr << "Unknown error occured!" << std::endl;
    return EXIT_FAILURE;
  } // try
  return EXIT_SUCCESS;
} // ... main(...)

And this is the output on my machine:

00:00|main: step 0, space has 1 DoFs
00:00|main:   relative interpolation error: 0.986892
00:00|mark_elements: marked 0/1 elements for coarsening and 1/1 elements for refinement
00:00|main: step 1, space has 2 DoFs
00:00|main:   relative interpolation error: 0.860953
00:00|mark_elements: marked 1/2 elements for coarsening and 1/2 elements for refinement
00:00|main: step 2, space has 3 DoFs
00:00|main:   relative interpolation error: 0.595099
00:00|mark_elements: marked 2/3 elements for coarsening and 1/3 elements for refinement
00:00|main: step 3, space has 4 DoFs
00:00|main:   relative interpolation error: 0.343076
00:00|mark_elements: marked 1/4 elements for coarsening and 1/4 elements for refinement
00:00|main: step 4, space has 5 DoFs
00:00|main:   relative interpolation error: 0.200343
00:00|mark_elements: marked 1/5 elements for coarsening and 1/5 elements for refinement
00:00|main: step 5, space has 6 DoFs
00:00|main:   relative interpolation error: 0.152863
00:00|mark_elements: marked 1/6 elements for coarsening and 1/6 elements for refinement
00:00|main: step 6, space has 7 DoFs
00:00|main:   relative interpolation error: 0.130386
00:00|mark_elements: marked 1/7 elements for coarsening and 1/7 elements for refinement
00:00|main: step 7, space has 8 DoFs
00:00|main:   relative interpolation error: 0.110434
00:00|mark_elements: marked 1/8 elements for coarsening and 1/8 elements for refinement
00:00|main: step 8, space has 9 DoFs
00:00|main:   relative interpolation error: 0.0968832
00:00|mark_elements: marked 1/9 elements for coarsening and 1/9 elements for refinement
00:00|main: step 9, space has 10 DoFs
00:00|main:   relative interpolation error: 0.0882792
00:00|mark_elements: marked 1/10 elements for coarsening and 1/10 elements for refinement
00:00|main: step 10, space has 11 DoFs
00:00|main:   relative interpolation error: 0.0793062
00:00|mark_elements: marked 1/11 elements for coarsening and 1/11 elements for refinement
00:00|main: step 11, space has 12 DoFs
00:00|main:   relative interpolation error: 0.0700921
00:00|mark_elements: marked 1/12 elements for coarsening and 2/12 elements for refinement
00:00|main: step 12, space has 14 DoFs
00:00|main:   relative interpolation error: 0.0598781
00:00|mark_elements: marked 1/14 elements for coarsening and 2/14 elements for refinement
00:00|main: step 13, space has 16 DoFs
00:00|main:   relative interpolation error: 0.0524025
00:00|mark_elements: marked 0/16 elements for coarsening and 2/16 elements for refinement
00:00|main: step 14, space has 18 DoFs
00:00|main:   relative interpolation error: 0.0453071
00:00|mark_elements: marked 0/18 elements for coarsening and 2/18 elements for refinement
00:00|main: step 15, space has 20 DoFs
00:00|main:   relative interpolation error: 0.0400431
00:00|mark_elements: marked 0/20 elements for coarsening and 3/20 elements for refinement
00:00|main: step 16, space has 23 DoFs
00:00|main:   relative interpolation error: 0.0346841
00:00|mark_elements: marked 0/23 elements for coarsening and 3/23 elements for refinement
00:00|main: step 17, space has 26 DoFs
00:00|main:   relative interpolation error: 0.0306315
00:00|mark_elements: marked 1/26 elements for coarsening and 4/26 elements for refinement
00:00|main: step 18, space has 30 DoFs
00:00|main:   relative interpolation error: 0.0269718
00:00|mark_elements: marked 1/30 elements for coarsening and 4/30 elements for refinement
00:00|main: step 19, space has 34 DoFs
00:00|main:   relative interpolation error: 0.0239658
00:00|mark_elements: marked 1/34 elements for coarsening and 4/34 elements for refinement
00:00|main: step 20, space has 38 DoFs
00:00|main:   relative interpolation error: 0.0212999
00:00|mark_elements: marked 1/38 elements for coarsening and 4/38 elements for refinement
00:00|main: step 21, space has 42 DoFs
00:00|main:   relative interpolation error: 0.0192651
00:00|mark_elements: marked 2/42 elements for coarsening and 5/42 elements for refinement
00:00|main: step 22, space has 47 DoFs
00:00|main:   relative interpolation error: 0.017096
00:00|mark_elements: marked 2/47 elements for coarsening and 6/47 elements for refinement
00:00|main: step 23, space has 53 DoFs
00:00|main:   relative interpolation error: 0.0152015
00:00|mark_elements: marked 3/53 elements for coarsening and 7/53 elements for refinement
00:00|main: step 24, space has 60 DoFs
00:00|main:   relative interpolation error: 0.0135002
00:00|mark_elements: marked 3/60 elements for coarsening and 7/60 elements for refinement
00:00|main: step 25, space has 67 DoFs
00:00|main:   relative interpolation error: 0.0120876
00:00|mark_elements: marked 3/67 elements for coarsening and 8/67 elements for refinement
00:00|main: step 26, space has 75 DoFs
00:00|main:   relative interpolation error: 0.0107531
00:00|mark_elements: marked 3/75 elements for coarsening and 8/75 elements for refinement
00:00|main: step 27, space has 83 DoFs
00:00|main:   relative interpolation error: 0.00963154
00:00|mark_elements: marked 3/83 elements for coarsening and 9/83 elements for refinement
00:00|main: step 28, space has 92 DoFs
00:00|main:   relative interpolation error: 0.0086114
00:00|mark_elements: marked 3/92 elements for coarsening and 10/92 elements for refinement
00:00|main: step 29, space has 102 DoFs
00:00|main:   relative interpolation error: 0.00776004
00:00|mark_elements: marked 3/102 elements for coarsening and 13/102 elements for refinement
00:00|main: step 30, space has 114 DoFs
00:00|main:   relative interpolation error: 0.00703421
00:00|mark_elements: marked 4/114 elements for coarsening and 13/114 elements for refinement
00:00|main: step 31, space has 127 DoFs
00:00|main:   relative interpolation error: 0.00630862
00:00|mark_elements: marked 4/127 elements for coarsening and 14/127 elements for refinement
00:00|main: step 32, space has 141 DoFs
00:00|main:   relative interpolation error: 0.00567056
00:00|mark_elements: marked 4/141 elements for coarsening and 15/141 elements for refinement
00:00|main: step 33, space has 155 DoFs
00:00|main:   relative interpolation error: 0.00514459
00:00|mark_elements: marked 4/155 elements for coarsening and 16/155 elements for refinement
00:00|main: step 34, space has 170 DoFs
00:00|main:   relative interpolation error: 0.00466947
00:00|mark_elements: marked 5/170 elements for coarsening and 18/170 elements for refinement
00:00|main: step 35, space has 187 DoFs
00:00|main:   relative interpolation error: 0.00422839
00:00|mark_elements: marked 5/187 elements for coarsening and 21/187 elements for refinement
00:00|main: step 36, space has 208 DoFs
00:00|main:   relative interpolation error: 0.00380419
00:00|mark_elements: marked 6/208 elements for coarsening and 25/208 elements for refinement
00:00|main: step 37, space has 231 DoFs
00:00|main:   relative interpolation error: 0.00346587
00:00|mark_elements: marked 7/231 elements for coarsening and 26/231 elements for refinement
00:00|main: step 38, space has 255 DoFs
00:00|main:   relative interpolation error: 0.00314821
00:00|mark_elements: marked 7/255 elements for coarsening and 27/255 elements for refinement
00:00|main: step 39, space has 280 DoFs
00:00|main:   relative interpolation error: 0.00286403
00:00|mark_elements: marked 9/280 elements for coarsening and 29/280 elements for refinement
00:00|main: step 40, space has 308 DoFs
00:00|main:   relative interpolation error: 0.00258791
00:00|mark_elements: marked 9/308 elements for coarsening and 32/308 elements for refinement
00:00|main: step 41, space has 338 DoFs
00:00|main:   relative interpolation error: 0.00234796
00:00|mark_elements: marked 10/338 elements for coarsening and 35/338 elements for refinement
00:00|main: step 42, space has 371 DoFs
00:00|main:   relative interpolation error: 0.00213133
00:00|mark_elements: marked 11/371 elements for coarsening and 41/371 elements for refinement
00:00|main: step 43, space has 409 DoFs
00:00|main:   relative interpolation error: 0.00193855
00:00|mark_elements: marked 14/409 elements for coarsening and 48/409 elements for refinement
00:00|main: step 44, space has 454 DoFs
00:00|main:   relative interpolation error: 0.0017599
00:00|mark_elements: marked 15/454 elements for coarsening and 51/454 elements for refinement
00:00|main: step 45, space has 501 DoFs
00:00|main:   relative interpolation error: 0.00160077
00:00|mark_elements: marked 15/501 elements for coarsening and 53/501 elements for refinement
00:00|main: step 46, space has 550 DoFs
00:00|main:   relative interpolation error: 0.0014565
00:00|mark_elements: marked 16/550 elements for coarsening and 57/550 elements for refinement
00:00|main: step 47, space has 601 DoFs
00:00|main:   relative interpolation error: 0.00132909
00:00|mark_elements: marked 17/601 elements for coarsening and 60/601 elements for refinement
00:00|main: step 48, space has 654 DoFs
00:00|main:   relative interpolation error: 0.00121564
00:00|mark_elements: marked 19/654 elements for coarsening and 66/654 elements for refinement
00:00|main: step 49, space has 712 DoFs
00:00|main:   relative interpolation error: 0.00111226
00:00|mark_elements: marked 21/712 elements for coarsening and 75/712 elements for refinement
00:00|main: step 50, space has 778 DoFs
00:00|main:   relative interpolation error: 0.00101703
00:00|mark_elements: marked 24/778 elements for coarsening and 88/778 elements for refinement
00:00|main: step 51, space has 856 DoFs
00:00|main:   relative interpolation error: 0.000930272
00:00|main: target accuracy reached, terminating!
00:00|main: 
00:00|main: To reach the desired tolerance using uniform refinement would have roughly required 8192 DoFs

Clone this wiki locally