-
Dr. Felix Tobias Schindler authoredDr. Felix Tobias Schindler authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
base.hh 12.63 KiB
// 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 (2019)
#ifndef DUNE_GDT_TEST_STATIONARY_EOCSTUDIES_BASE_HH
#define DUNE_GDT_TEST_STATIONARY_EOCSTUDIES_BASE_HH
#include <cmath>
#include <functional>
#include <memory>
#include <dune/common/timer.hh>
#include <dune/grid/io/file/dgfparser.hh>
#include <dune/xt/common/convergence-study.hh>
#include <dune/xt/test/common.hh>
#include <dune/xt/common/fvector.hh>
#include <dune/xt/common/string.hh>
#include <dune/xt/common/timedlogging.hh>
#include <dune/xt/test/common.hh>
#include <dune/xt/test/gtest/gtest.h>
#include <dune/xt/la/container.hh>
#include <dune/xt/la/type_traits.hh>
#include <dune/xt/grid/gridprovider/provider.hh>
#include <dune/xt/grid/type_traits.hh>
#include <dune/xt/functions/generic/function.hh>
#include <dune/gdt/exceptions.hh>
#include <dune/gdt/functionals/localizable-functional.hh>
#include <dune/gdt/local/bilinear-forms/integrals.hh>
#include <dune/gdt/local/functionals/integrals.hh>
#include <dune/gdt/local/integrands/abs.hh>
#include <dune/gdt/local/integrands/laplace.hh>
#include <dune/gdt/local/integrands/identity.hh>
#include <dune/gdt/local/integrands/product.hh>
#include <dune/gdt/norms.hh>
#include <dune/gdt/operators/constant.hh>
#include <dune/gdt/operators/identity.hh>
#include <dune/gdt/operators/interfaces.hh>
#include <dune/gdt/operators/bilinear-form.hh>
#include <dune/gdt/operators/matrix.hh>
#include <dune/gdt/prolongations.hh>
#include <dune/gdt/spaces/interface.hh>
#include <dune/gdt/type_traits.hh>
namespace Dune {
namespace GDT {
namespace Test {
template <class GridView, size_t m_ = 1, XT::LA::Backends la = XT::LA::Backends::istl_sparse>
class StationaryEocStudy
: public XT::Common::ConvergenceStudy
, public ::testing::Test
{
static_assert(XT::Grid::is_view<GridView>::value, "");
protected:
using GV = GridView;
using G = typename GridView::Grid;
static constexpr size_t m = m_;
using D = double;
static constexpr size_t d = G::dimension;
using R = double;
using I = XT::Grid::extract_intersection_t<GV>;
using DomainType = XT::Common::FieldVector<D, d>;
using RangeType = XT::Common::FieldVector<D, m>;
using GP = XT::Grid::GridProvider<G>;
using S = SpaceInterface<GV, m>;
using M = typename XT::LA::Container<R, la>::MatrixType;
using V = XT::LA::vector_t<M>;
using DF = DiscreteFunction<V, GV, m>;
using O = OperatorInterface<GV, m, 1, m, 1, R, M>;
public:
using E = XT::Grid::extract_entity_t<GV>;
StationaryEocStudy(
std::function<void(const DF&, const std::string&)> visualizer =
[](const auto& solution, const auto& prefix) {
if (DXTC_TEST_CONFIG_GET("setup.visualize", false))
solution.visualize(prefix);
},
const size_t num_refinements = DXTC_TEST_CONFIG_GET("setup.num_refinements", 3),
const size_t num_additional_refinements_for_reference =
DXTC_TEST_CONFIG_GET("setup.num_additional_refinements_for_reference", 2))
: num_refinements_(num_refinements)
, num_additional_refinements_for_reference_(num_additional_refinements_for_reference)
, visualize_(visualizer)
, current_refinement_(std::numeric_limits<size_t>::max())
, current_data_()
, current_grid_(nullptr)
, current_space_(nullptr)
, reference_grid_(nullptr)
, reference_space_(nullptr)
, current_solution_on_reference_grid_(nullptr)
, reference_solution_on_reference_grid_(nullptr)
{}
size_t num_refinements() const override
{
return num_refinements_;
}
std::vector<std::string> targets() const override
{
return {"h"};
}
std::vector<std::string> norms() const override
{
// We currently support the following norms: L_1, L_2, H_1_semi
return {"L_2", "H_1_semi"};
}
std::vector<std::pair<std::string, std::string>> estimates() const override
{
return {};
}
std::vector<std::string> quantities() const override
{
return {"time to solution (s)"};
}
std::string discretization_info_title() const override
{
return " |grid| | #DoFs";
}
protected:
virtual GP make_initial_grid() = 0;
virtual std::unique_ptr<S> make_space(const GP& current_grid) = 0;
public:
std::string discretization_info(const size_t refinement_level) override
{
if (current_refinement_ != refinement_level) {
// clear the current state
current_grid_.reset();
current_space_.reset();
current_solution_on_reference_grid_.reset();
// compute on this refinement
current_grid_ = std::make_unique<GP>(make_initial_grid());
for (size_t ref = 0; ref < refinement_level; ++ref)
current_grid_->global_refine(DGFGridInfo<G>::refineStepsForHalf());
current_space_ = make_space(*current_grid_);
double grid_size = 0;
double grid_width = 0.;
for (auto&& grid_element : elements(current_space_->grid_view())) {
grid_size += 1;
grid_width = std::max(grid_width, XT::Grid::entity_diameter(grid_element));
}
current_refinement_ = refinement_level;
current_data_.clear();
current_data_["target"]["h"] = grid_width;
current_data_["quantity"]["num_grid_elements"] = grid_size;
current_data_["quantity"]["num_dofs"] = static_cast<double>(current_space_->mapper().size());
}
const auto lfill_nicely = [&](const auto& number, const auto& len) {
std::string ret;
using XT::Common::to_string;
if (std::log10(number) < len)
ret = this->lfill(to_string(number), len);
else {
std::stringstream ss;
ss << std::setprecision(2) << std::scientific << number;
ret = this->lfill(ss.str(), len);
}
return ret;
};
return lfill_nicely(current_data_["quantity"]["num_grid_elements"], 7) + " | "
+ lfill_nicely(current_data_["quantity"]["num_dofs"], 7);
} // ... discretization_info(...)
protected:
virtual std::unique_ptr<O> make_residual_operator(const S& space) = 0;
virtual V solve(const S& space)
{
auto op = make_residual_operator(space);
V zero(op->range_space().mapper().size(), 0.);
return op->apply_inverse(zero);
}
virtual void compute_reference_solution()
{
if (reference_solution_on_reference_grid_)
return;
reference_grid_ = std::make_unique<GP>(make_initial_grid());
for (size_t ref = 0; ref < num_refinements_ + num_additional_refinements_for_reference_; ++ref)
reference_grid_->global_refine(DGFGridInfo<G>::refineStepsForHalf());
reference_space_ = make_space(*reference_grid_);
reference_solution_on_reference_grid_ = std::make_unique<V>(std::move(solve(*reference_space_)));
// visualize
visualize_(make_discrete_function(*reference_space_, *reference_solution_on_reference_grid_),
"reference_solution_on_refinement_"
+ XT::Common::to_string(num_refinements_ + num_additional_refinements_for_reference_));
} // ... compute_reference_solution(...)
public:
virtual std::map<std::string, std::map<std::string, double>>
compute(const size_t refinement_level,
const std::vector<std::string>& actual_norms,
const std::vector<std::pair<std::string, std::string>>& actual_estimates,
const std::vector<std::string>& actual_quantities) override
{
auto& self = *this;
if (current_refinement_ != refinement_level)
self.discretization_info(refinement_level);
DUNE_THROW_IF(!current_space_, InvalidStateException, "");
// compute current solution
const auto& current_space = *current_space_;
Timer timer;
auto solution_on_current_grid = solve(current_space);
// only set time if this did not happen in solve()
if (current_data_["quantity"].count("time to solution (s)") == 0)
current_data_["quantity"]["time to solution (s)"] = timer.elapsed();
// visualize
visualize_(make_discrete_function(current_space, solution_on_current_grid),
"solution_on_refinement_" + XT::Common::to_string(refinement_level));
const auto coarse_solution = make_discrete_function(current_space, solution_on_current_grid);
// determine wether we need a reference solution
// compute statistics
auto norms_to_compute = actual_norms;
// - norms
if (!norms_to_compute.empty()) {
auto current_data_backup = current_data_;
self.compute_reference_solution();
current_data_ = current_data_backup;
DUNE_THROW_IF(!reference_space_, InvalidStateException, "");
const auto& reference_space = *reference_space_;
DUNE_THROW_IF(!reference_solution_on_reference_grid_, InvalidStateException, "");
auto& u = *reference_solution_on_reference_grid_;
// prolong
current_solution_on_reference_grid_ =
std::make_unique<V>(prolong<V>(coarse_solution, reference_space).dofs().vector());
// compute norm
auto& u_h = *current_solution_on_reference_grid_;
while (!norms_to_compute.empty()) {
const auto spatial_norm_id = norms_to_compute.back();
norms_to_compute.pop_back();
std::function<double(const DF&)> spatial_norm;
if (spatial_norm_id == "L_1") {
spatial_norm = [&](const DF& func) {
auto localizable_functional = make_localizable_functional(reference_space.grid_view(), func);
localizable_functional.append(LocalElementIntegralFunctional<E, m>(
LocalElementAbsIntegrand<E, m>(), DXTC_TEST_CONFIG_GET("setup.over_integrate", 3)));
localizable_functional.assemble(DXTC_TEST_CONFIG_GET("setup.use_tbb", true));
return localizable_functional.result();
};
} else if (spatial_norm_id == "L_2") {
spatial_norm = [&](const DF& func) {
return l2_norm(
reference_space.grid_view(), func, /*weight=*/1., DXTC_TEST_CONFIG_GET("setup.over_integrate", 3));
};
} else if (spatial_norm_id == "H_1_semi") {
spatial_norm = [&](const DF& func) {
return h1_semi_norm(
reference_space.grid_view(), func, /*weight=*/1., DXTC_TEST_CONFIG_GET("setup.over_integrate", 3));
};
} else
DUNE_THROW(XT::Common::Exceptions::wrong_input_given,
"I do not know how to compute the norm '" << spatial_norm_id << "'!");
current_data_["norm"][spatial_norm_id] = spatial_norm(make_discrete_function(reference_space, u - u_h));
}
DUNE_THROW_IF(!norms_to_compute.empty(),
XT::Common::Exceptions::wrong_input_given,
"I did not know how to compute the following norms: " << norms_to_compute);
}
// - estimates
auto estimats_to_compute = actual_estimates;
DUNE_THROW_IF(!estimats_to_compute.empty(),
XT::Common::Exceptions::wrong_input_given,
"I did not know how to compute the following estimates: " << estimats_to_compute);
// - quantities
auto quantities_to_compute = actual_quantities;
while (!quantities_to_compute.empty()) {
const auto id = quantities_to_compute.back();
quantities_to_compute.pop_back();
if (id == "time to solution (s)") {
DUNE_THROW_IF(current_data_["quantity"].find(id) == current_data_["quantity"].end(),
InvalidStateException,
"Could not find id " << id << "in current_data_ map");
} else
DUNE_THROW(XT::Common::Exceptions::wrong_input_given,
"I do not know how to compute the quantity '" << id << "'!");
}
DUNE_THROW_IF(!quantities_to_compute.empty(),
XT::Common::Exceptions::wrong_input_given,
"I did not know how to compute the following quantities: " << quantities_to_compute);
return current_data_;
} // ... compute_on_current_refinement(...)
protected:
size_t num_refinements_;
size_t num_additional_refinements_for_reference_;
const std::function<void(const DF&, const std::string&)> visualize_;
size_t current_refinement_;
std::map<std::string, std::map<std::string, double>> current_data_;
std::unique_ptr<GP> current_grid_;
std::unique_ptr<S> current_space_;
std::unique_ptr<GP> reference_grid_;
std::unique_ptr<S> reference_space_;
std::unique_ptr<V> current_solution_on_reference_grid_;
std::unique_ptr<V> reference_solution_on_reference_grid_;
}; // struct StationaryEocStudy
} // namespace Test
} // namespace GDT
} // namespace Dune
#endif // DUNE_GDT_TEST_STATIONARY_EOCSTUDIES_BASE_HH