diff --git a/dune/gdt/local/numerical-fluxes/kinetic.hh b/dune/gdt/local/numerical-fluxes/kinetic.hh index c0c7962642ac314d354f9fc3dbd803fd0e324f34..9c1f339755ac231a3973934538b0518b205ec58d 100644 --- a/dune/gdt/local/numerical-fluxes/kinetic.hh +++ b/dune/gdt/local/numerical-fluxes/kinetic.hh @@ -22,7 +22,7 @@ namespace Dune { namespace GDT { -template <class GV, class MomentBasis> +template <class GV, class MomentBasis, class EntropyFluxImp = EntropyBasedFluxFunction<GV, MomentBasis>> class NumericalKineticFlux : public NumericalFluxInterface<XT::Grid::extract_intersection_t<GV>, MomentBasis::dimFlux, @@ -43,7 +43,7 @@ class NumericalKineticFlux using I = XT::Grid::extract_intersection_t<GV>; using ThisType = NumericalKineticFlux; using BaseType = NumericalFluxInterface<I, d, m, R>; - using EntropyFluxType = EntropyBasedFluxFunction<GV, MomentBasis>; + using EntropyFluxType = EntropyFluxImp; using SparseMatrixType = typename XT::LA::CommonSparseMatrix<R>; public: @@ -83,13 +83,22 @@ public: // find direction of unit outer normal (we assume an axis-aligned cube grid) size_t direction = intersection().indexInInside() / 2; if (dynamic_cast<const EntropyFluxType*>(&flux()) != nullptr) { - return dynamic_cast<const EntropyFluxType*>(&flux())->evaluate_kinetic_flux( + auto ret = dynamic_cast<const EntropyFluxType*>(&flux())->evaluate_kinetic_flux( intersection().inside(), intersection().neighbor() ? intersection().outside() : intersection().inside(), u, v, n, direction); + for (auto&& entry : ret) + if (std::isnan(entry) || std::isinf(entry)) { + // std::cout << XT::Common::to_string(ret) << std::endl; + // std::cout << XT::Common::to_string(u) << std::endl; + // std::cout << XT::Common::to_string(v) << std::endl; + // std::cout << this->intersection().geometry().center() << std::endl; + DUNE_THROW(Dune::MathError, "NaN or inf in kinetic flux"); + } + return ret; } else { static const auto flux_matrices = initialize_flux_matrices(basis_); StateType ret(0); diff --git a/dune/gdt/momentmodels/entropyflux.hh b/dune/gdt/momentmodels/entropyflux.hh index 02a5e54299be08362537565ad8d9c158ceb9f7b2..ca237ecb29b6c04a3fb0bf85db35d46ca27b2a4e 100644 --- a/dune/gdt/momentmodels/entropyflux.hh +++ b/dune/gdt/momentmodels/entropyflux.hh @@ -161,7 +161,8 @@ public: , use_entity_cache_(true) , entity_caches_(index_set_.size(0), LocalCacheType(cache_size)) , mutexes_(index_set_.size(0)) - , implementation_(basis_functions, tau, epsilon_gamma, chi, xi, r_sequence, k_0, k_max, epsilon) + , implementation_(std::make_shared<ImplementationType>( + basis_functions, tau, epsilon_gamma, chi, xi, r_sequence, k_0, k_max, epsilon)) {} void enable_thread_cache() @@ -303,13 +304,13 @@ public: virtual std::unique_ptr<LocalFunctionType> local_function() const override final { return std::make_unique<Localfunction>( - index_set_, entity_caches_, use_thread_cache_, use_entity_cache_, mutexes_, implementation_); + index_set_, entity_caches_, use_thread_cache_, use_entity_cache_, mutexes_, *implementation_); } virtual std::unique_ptr<Localfunction> derived_local_function() const { return std::make_unique<Localfunction>( - index_set_, entity_caches_, use_thread_cache_, use_entity_cache_, mutexes_, implementation_); + index_set_, entity_caches_, use_thread_cache_, use_entity_cache_, mutexes_, *implementation_); } StateType evaluate_kinetic_flux(const E& inside_entity, @@ -325,21 +326,20 @@ public: const auto alpha_i = local_func->get_alpha(u_i, true)->first; local_func->bind(outside_entity); const auto alpha_j = local_func->get_alpha(u_j, true)->first; - return implementation_.evaluate_kinetic_flux_with_alphas(alpha_i, alpha_j, n_ij, dd); + return implementation_->evaluate_kinetic_flux_with_alphas(alpha_i, alpha_j, n_ij, dd); } // StateType evaluate_kinetic_flux(...) const MomentBasis& basis_functions() const { - return implementation_.basis_functions(); + return implementation_->basis_functions(); } -private: const IndexSetType& index_set_; bool use_thread_cache_; bool use_entity_cache_; mutable std::vector<LocalCacheType> entity_caches_; mutable std::vector<std::mutex> mutexes_; - ImplementationType implementation_; + std::shared_ptr<ImplementationType> implementation_; }; template <class GridViewImp, class MomentBasisImp> diff --git a/dune/gdt/momentmodels/entropyflux_implementations.hh b/dune/gdt/momentmodels/entropyflux_implementations.hh index 2b8429f95fadec68675265deebfcd60685c07b43..5d3fdbc3052db4ea68092642b77154a05ff3baaa 100644 --- a/dune/gdt/momentmodels/entropyflux_implementations.hh +++ b/dune/gdt/momentmodels/entropyflux_implementations.hh @@ -105,6 +105,7 @@ public: using DynamicRangeType = DynamicVector<RangeFieldType>; using BasisValuesMatrixType = XT::LA::CommonDenseMatrix<RangeFieldType>; using AlphaReturnType = std::pair<VectorType, std::pair<DomainType, RangeFieldType>>; + using QuadraturePointsValuesType = std::vector<RangeFieldType>; explicit EntropyBasedFluxImplementationUnspecializedBase(const MomentBasis& basis_functions, const RangeFieldType tau, @@ -177,7 +178,7 @@ public: return evaluate_with_alpha(alpha); } - virtual RangeReturnType evaluate_with_alpha(const VectorType& alpha) const + virtual RangeReturnType evaluate_with_alpha(const DomainType& alpha) const { RangeReturnType ret(0.); auto& work_vecs = working_storage(); @@ -202,7 +203,7 @@ public: jacobian_with_alpha(alpha, result); } - virtual void jacobian_with_alpha(const VectorType& alpha, DynamicDerivativeRangeType& result) const + virtual void jacobian_with_alpha(const DomainType& alpha, DynamicDerivativeRangeType& result) const { thread_local auto H = XT::Common::make_unique<MatrixType>(); calculate_hessian(alpha, M_, *H); @@ -223,8 +224,8 @@ public: evaluate_kinetic_flux_with_alphas(alpha_i, alpha_j, n_ij, dd); } // DomainType evaluate_kinetic_flux(...) - DomainType evaluate_kinetic_flux_with_alphas(const VectorType& alpha_i, - const VectorType& alpha_j, + DomainType evaluate_kinetic_flux_with_alphas(const DomainType& alpha_i, + const DomainType& alpha_j, const FluxDomainType& n_ij, const size_t dd) const @@ -254,9 +255,8 @@ public: // returns (alpha, (actual_u, r)), where r is the regularization parameter and actual_u the regularized u virtual std::unique_ptr<AlphaReturnType> - get_alpha(const DomainType& u, const VectorType& alpha_in, const bool regularize) const = 0; + get_alpha(const DomainType& u, const DomainType& alpha_in, const bool regularize) const = 0; -protected: // get permutation instead of sorting directly to be able to sort two vectors the same way // see // https://stackoverflow.com/questions/17074324/how-can-i-sort-two-vectors-in-the-same-way-with-criteria-that-uses-only-one-of @@ -439,7 +439,7 @@ protected: return work_vec; } - void calculate_scalar_products(const VectorType& beta_in, + void calculate_scalar_products(const DomainType& beta_in, const BasisValuesMatrixType& M, std::vector<RangeFieldType>& scalar_products) const { @@ -472,7 +472,7 @@ protected: } // calculate ret = \int (exp(beta_in * m)) - RangeFieldType calculate_scalar_integral(const VectorType& beta_in, const BasisValuesMatrixType& M) const + RangeFieldType calculate_scalar_integral(const DomainType& beta_in, const BasisValuesMatrixType& M) const { auto& work_vec = working_storage(); calculate_scalar_products(beta_in, M, work_vec); @@ -481,10 +481,10 @@ protected: } // calculate ret = \int (m1 exp(beta_in * m2)) - void calculate_vector_integral(const VectorType& beta_in, + void calculate_vector_integral(const DomainType& beta_in, const BasisValuesMatrixType& M1, const BasisValuesMatrixType& M2, - VectorType& ret, + DomainType& ret, bool same_beta = false, bool only_first_component = false) const { @@ -571,7 +571,17 @@ protected: } // ii } // void calculate_A_Binv(...) - void calculate_hessian(const VectorType& alpha, + void apply_inverse_hessian(const DomainType& alpha, const DomainType& u, DomainType& Hinv_u) const + { + thread_local auto H = XT::Common::make_unique<MatrixType>(); + calculate_hessian(alpha, M_, *H); + XT::LA::cholesky(*H); + thread_local DomainType tmp_vec; + XT::LA::solve_lower_triangular(*H, tmp_vec, u); + XT::LA::solve_lower_triangular_transposed(*H, Hinv_u, tmp_vec); + } + + void calculate_hessian(const DomainType& alpha, const BasisValuesMatrixType& M, MatrixType& H, const bool use_work_vec_data = false) const @@ -629,12 +639,12 @@ protected: J_dd.set_entry(mm, nn, J_dd.get_entry(nn, mm)); } // void calculate_J(...) - void change_basis(const VectorType& beta_in, - VectorType& v_k, + void change_basis(const DomainType& beta_in, + DomainType& v_k, BasisValuesMatrixType& P_k, MatrixType& T_k, - VectorType& g_k, - VectorType& beta_out, + DomainType& g_k, + DomainType& beta_out, MatrixType& H) const { calculate_hessian(beta_in, P_k, H); @@ -683,12 +693,15 @@ class EntropyBasedFluxImplementation : public EntropyBasedFluxImplementationUnsp using ThisType = EntropyBasedFluxImplementation; public: + using BaseType::basis_dimRange; using BaseType::dimFlux; using typename BaseType::AlphaReturnType; + using typename BaseType::BasisDomainType; using typename BaseType::BasisValuesMatrixType; using typename BaseType::DomainType; using typename BaseType::MatrixType; using typename BaseType::MomentBasis; + using typename BaseType::QuadraturePointsValuesType; using typename BaseType::RangeFieldType; using typename BaseType::VectorType; @@ -707,9 +720,119 @@ public: XT::LA::eye_matrix(*T_minus_one_); } + std::unique_ptr<AlphaReturnType> get_alpha(const DomainType& u) const + { + return get_alpha(u, get_isotropic_alpha(u), true); + } + + DomainType get_u(const DomainType& alpha) const + { + DomainType ret; + calculate_vector_integral(alpha, M_, M_, ret); + return ret; + } + + static RangeFieldType minmod(const RangeFieldType& first_slope, const RangeFieldType& second_slope) + { + RangeFieldType ret = 0.; + if (std::signbit(first_slope) == std::signbit(second_slope)) // check for equal sign + ret = std::abs(first_slope) < std::abs(second_slope) ? first_slope : second_slope; + return ret; + } + + static RangeFieldType maxmod(const RangeFieldType first_slope, const RangeFieldType second_slope) + { + RangeFieldType ret(0.); + if (std::signbit(first_slope) == std::signbit(second_slope)) // check for equal sign + ret = (std::abs(first_slope) < std::abs(second_slope)) ? second_slope : first_slope; + return ret; + } + + static RangeFieldType superbee(const RangeFieldType first_slope, const RangeFieldType second_slope) + { + const RangeFieldType first_minmod = minmod(first_slope, second_slope * 2.); + const RangeFieldType second_minmod = minmod(first_slope * 2., second_slope); + return maxmod(first_minmod, second_minmod); + } + + template <class IntersectionType> + DomainType calculate_boundary_flux(const DomainType& alpha, const IntersectionType& intersection) + { + // evaluate exp(alpha^T b(v_i)) at all quadratures points v_i + auto& work_vecs = this->working_storage(); + this->calculate_scalar_products(alpha, M_, work_vecs); + this->apply_exponential(work_vecs); + const auto dd = intersection.indexInInside() / 2; + const auto& n_ij = intersection.centerUnitOuterNormal(); + DomainType ret(0.); + for (size_t ll = 0; ll < quad_points_.size(); ++ll) { + const auto position = quad_points_[ll][dd]; + if (position * n_ij[dd] < 0.) { + RangeFieldType factor = work_vecs[ll] * quad_weights_[ll] * position; + const auto* basis_ll = &(M_.get_entry_ref(ll, 0.)); + for (size_t ii = 0; ii < basis_dimRange; ++ii) + ret[ii] += basis_ll[ii] * factor; + } + } // ll + return ret; + } + + // TODO: Calculates exp(alpha^T b) 2*dim times for each alpha (as it is contained in 2*dim stencils) at the moment. + template <class FluxesMapType> + void calculate_minmod_density_reconstruction(const FieldVector<DomainType, 3>& alphas, + FluxesMapType& flux_values, + const size_t dd) const + { + // evaluate exp(alpha^T b(v_i)) at all quadratures points v_i for all three alphas + thread_local XT::Common::FieldVector<QuadraturePointsValuesType, 3> density_evaluations( + QuadraturePointsValuesType(quad_points_.size())); + thread_local XT::Common::FieldVector<QuadraturePointsValuesType, 2> reconstructed_values( + QuadraturePointsValuesType(quad_points_.size())); + for (size_t ii = 0; ii < 3; ++ii) { + this->calculate_scalar_products(alphas[ii], M_, density_evaluations[ii]); + this->apply_exponential(density_evaluations[ii]); + for (size_t ll = 0; ll < quad_points_.size(); ++ll) { + if (std::isnan(density_evaluations[ii][ll]) || std::isnan(density_evaluations[ii][ll])) { + std::cout << "Wrong val: " << density_evaluations[ii][ll] << " for alpha " << alphas[ii] << std::endl; + DUNE_THROW(Dune::MathError, ""); + } + } + } + // get left and right reconstructed values for each quadrature point v_i + auto& vals_left = reconstructed_values[0]; + auto& vals_right = reconstructed_values[1]; + for (size_t ll = 0; ll < quad_points_.size(); ++ll) { + const auto slope = minmod(density_evaluations[1][ll] - density_evaluations[0][ll], + density_evaluations[2][ll] - density_evaluations[1][ll]); + // const auto slope = superbee(density_evaluations[1][ll] - density_evaluations[0][ll], + // density_evaluations[2][ll] - density_evaluations[1][ll]); + vals_left[ll] = density_evaluations[1][ll] - 0.5 * slope; + vals_right[ll] = density_evaluations[1][ll] + 0.5 * slope; + } // ll + + BasisDomainType coord(0.5); + coord[dd] = 0; + auto& left_flux_value = flux_values[coord]; + coord[dd] = 1; + auto& right_flux_value = flux_values[coord]; + right_flux_value = left_flux_value = DomainType(0.); + + for (size_t ll = 0; ll < quad_points_.size(); ++ll) { + const auto position = quad_points_[ll][dd]; + RangeFieldType factor = position > 0. ? vals_right[ll] : vals_left[ll]; + factor *= quad_weights_[ll] * position; + auto& val = position > 0. ? right_flux_value : left_flux_value; + const auto* basis_ll = &(M_.get_entry_ref(ll, 0.)); + for (size_t ii = 0; ii < basis_dimRange; ++ii) + val[ii] += basis_ll[ii] * factor; + } // ll + // std::cout << "right_flux_value, left_flux_value = " << right_flux_value << ", " << left_flux_value << + // std::endl; + } // void calculate_minmod_density_reconstruction(...) + // returns (alpha, (actual_u, r)), where r is the regularization parameter and actual_u the regularized u virtual std::unique_ptr<AlphaReturnType> - get_alpha(const DomainType& u, const VectorType& alpha_in, const bool regularize) const override final + get_alpha(const DomainType& u, const DomainType& alpha_in, const bool regularize) const override final { auto ret = std::make_unique<AlphaReturnType>(); @@ -841,12 +964,12 @@ private: using BaseType::calculate_vector_integral; using BaseType::get_isotropic_alpha; - void change_basis(const VectorType& beta_in, - VectorType& v_k, + void change_basis(const DomainType& beta_in, + DomainType& v_k, BasisValuesMatrixType& P_k, MatrixType& T_k, - VectorType& g_k, - VectorType& beta_out, + DomainType& g_k, + DomainType& beta_out, MatrixType& H) const { calculate_hessian(beta_in, P_k, H); @@ -913,6 +1036,16 @@ public: : BaseType(basis_functions, tau, epsilon_gamma, chi, xi, r_sequence, k_0, k_max, epsilon) {} + std::unique_ptr<AlphaReturnType> get_alpha(const DomainType& u) const + { + return get_alpha(u, get_isotropic_alpha(u), true); + } + + DomainType get_u(const DomainType& alpha) const + { + return calculate_vector_integral(alpha, M_, M_); + } + // returns (alpha, (actual_u, r)), where r is the regularization parameter and actual_u the regularized u virtual std::unique_ptr<AlphaReturnType> get_alpha(const DomainType& u, const VectorType& alpha_in, const bool regularize) const override final @@ -1233,6 +1366,18 @@ public: return basis_functions_; } + std::unique_ptr<AlphaReturnType> get_alpha(const DomainType& u) const + { + return get_alpha(u, *get_isotropic_alpha(u), true); + } + + DomainType get_u(const DomainType& alpha) const + { + BlockVectorType u_block; + calculate_vector_integral(alpha, M_, M_, u_block); + return u_block; + } + std::unique_ptr<AlphaReturnType> get_alpha(const DomainType& u, const VectorType& alpha_in, const bool regularize) const { @@ -1372,7 +1517,6 @@ public: return ret; } -private: // temporary vectors to store inner products and exponentials TemporaryVectorsType& working_storage() const { @@ -1628,6 +1772,26 @@ private: } // jj } // void calculate_A_Binv(...) + void apply_inverse_hessian(const DomainType& alpha, const DomainType& u, DomainType& Hinv_u) const + { + thread_local auto H = XT::Common::make_unique<BlockMatrixType>(); + calculate_hessian(alpha, M_, *H); + for (size_t jj = 0; jj < num_blocks; ++jj) + XT::LA::cholesky(H->block(jj)); + FieldVector<RangeFieldType, block_size> tmp_vec; + FieldVector<RangeFieldType, block_size> block_u; + for (size_t jj = 0; jj < num_blocks; ++jj) { + // calculate C = A (L^T)^{-1} + const auto offset = block_size * jj; + for (size_t ii = 0; ii < block_size; ++ii) + block_u[ii] = u[offset + ii]; + XT::LA::solve_lower_triangular(H->block(jj), tmp_vec, block_u); + XT::LA::solve_lower_triangular_transposed(H->block(jj), block_u, tmp_vec); + for (size_t ii = 0; ii < block_size; ++ii) + Hinv_u[offset + ii] = block_u[ii]; + } // jj + } + void calculate_hessian(const BlockVectorType& alpha, const BasisValuesMatrixType& M, BlockMatrixType& H) const { auto& work_vec = working_storage(); @@ -1858,6 +2022,13 @@ public: return evaluate_with_alpha(alpha); } + RangeReturnType evaluate_with_alpha(const DomainType& alpha) const + { + VectorType alpha_vec(basis_dimRange, 0., 0); + std::copy(alpha.begin(), alpha.end(), alpha_vec.begin()); + return evaluate_with_alpha(alpha_vec); + } + virtual RangeReturnType evaluate_with_alpha(const VectorType& alpha) const { RangeReturnType ret(0.); @@ -1893,7 +2064,14 @@ public: jacobian_with_alpha(alpha, result); } - virtual void jacobian_with_alpha(const VectorType& alpha, DynamicDerivativeRangeType& result) const + void jacobian_with_alpha(const DomainType& alpha, DynamicDerivativeRangeType& result) const + { + VectorType alpha_vec(basis_dimRange, 0., 0); + std::copy(alpha.begin(), alpha.end(), alpha_vec.begin()); + jacobian_with_alpha(alpha_vec, result); + } + + void jacobian_with_alpha(const VectorType& alpha, DynamicDerivativeRangeType& result) const { thread_local SparseMatrixType H(basis_dimRange, basis_dimRange, pattern_, 0); thread_local SparseMatrixType J(basis_dimRange, basis_dimRange, pattern_, 0); @@ -1912,6 +2090,18 @@ public: evaluate_kinetic_flux_with_alphas(alpha_i, alpha_j, n_ij, dd); } // DomainType evaluate_kinetic_flux(...) + DomainType evaluate_kinetic_flux_with_alphas(const DomainType& alpha_i, + const DomainType& alpha_j, + const FluxDomainType& n_ij, + const size_t dd) const + { + VectorType alpha_i_vec(basis_dimRange); + VectorType alpha_j_vec(basis_dimRange); + std::copy(alpha_i.begin(), alpha_i.end(), alpha_i_vec.begin()); + std::copy(alpha_j.begin(), alpha_j.end(), alpha_j_vec.begin()); + return evaluate_kinetic_flux_with_alphas(alpha_i_vec, alpha_j_vec, n_ij, dd); + } + DomainType evaluate_kinetic_flux_with_alphas(const VectorType& alpha_i, const VectorType& alpha_j, const FluxDomainType& n_ij, @@ -1951,6 +2141,21 @@ public: return basis_functions_; } + std::unique_ptr<AlphaReturnType> get_alpha(const DomainType& u) const + { + return get_alpha(u, get_isotropic_alpha(u), true); + } + + DomainType get_u(const DomainType& alpha) const + { + VectorType u_vec(basis_dimRange), alpha_vec(basis_dimRange); + std::copy(alpha.begin(), alpha.end(), alpha_vec.begin()); + calculate_u(alpha_vec, u_vec); + DomainType u; + std::copy(u_vec.begin(), u_vec.end(), u.begin()); + return u; + } + std::unique_ptr<AlphaReturnType> get_alpha(const DomainType& u, const VectorType& alpha_in, const bool regularize) const { @@ -2087,7 +2292,6 @@ public: return ret; } // ... get_alpha(...) -private: static bool is_realizable(const VectorType& u) { for (const auto& u_i : u) @@ -2108,7 +2312,6 @@ private: return work_vecs; } -private: // calculates ret = J H^{-1}. H is assumed to be symmetric positive definite, which gives ret^T = H^{-T} J^T = // H^{-1} J^T, so we just have to solve y = H^{-1} x for each row x of J void calculate_J_Hinv(SparseMatrixType& J, const SparseMatrixType& H, DynamicRowDerivativeRangeType& ret) const @@ -2140,6 +2343,29 @@ private: } // ii } // void calculate_J_Hinv(...) + void apply_inverse_hessian(const DomainType& alpha, const DomainType& u, DomainType& Hinv_u) const + { + thread_local SparseMatrixType H(basis_dimRange, basis_dimRange, pattern_, 0); + thread_local VectorType alpha_vec(basis_dimRange, 0., 0), u_vec(basis_dimRange, 0., 0), + Hinv_u_vec(basis_dimRange, 0., 0); + std::copy(alpha.begin(), alpha.end(), alpha_vec.begin()); + std::copy(u.begin(), u.end(), u_vec.begin()); + calculate_hessian(alpha_vec, M_, H); +# if HAVE_EIGEN + typedef ::Eigen::SparseMatrix<RangeFieldType, ::Eigen::ColMajor> ColMajorBackendType; + ColMajorBackendType colmajor_copy(H.backend()); + typedef ::Eigen::SimplicialLDLT<ColMajorBackendType> SolverType; + SolverType solver; + solver.analyzePattern(colmajor_copy); + solver.factorize(colmajor_copy); + Hinv_u_vec.backend() = solver.solve(u_vec.backend()); + std::copy(Hinv_u_vec.begin(), Hinv_u_vec.end(), Hinv_u.begin()); +# else // HAVE_EIGEN + auto solver = XT::LA::make_solver(H); + solver.apply(u, Hinv_u); +# endif + } // void apply_inverse_hessian(..) + RangeFieldType calculate_f(const VectorType& alpha, const VectorType& v) const { RangeFieldType ret(0.); @@ -2598,6 +2824,15 @@ public: return ret; } // DomainType evaluate_kinetic_flux(...) + std::unique_ptr<AlphaReturnType> get_alpha(const DomainType& u) const + { + return get_alpha(u, get_isotropic_alpha(u), true); + } + + DomainType get_u(const DomainType& alpha) const + { + return calculate_vector_integral(alpha, M_, M_); + } // returns (alpha, (actual_u, r)), where r is the regularization parameter and actual_u the regularized u std::unique_ptr<AlphaReturnType> @@ -3167,6 +3402,120 @@ public: return ret; } // ... evaluate_kinetic_flux(...) + std::unique_ptr<AlphaReturnType> get_alpha(const DomainType& u) const + { + return get_alpha(u, get_isotropic_alpha(u), true); + } + + DomainType get_u(const DomainType& alpha) const + { + DomainType ret; + calculate_u(alpha, ret); + return ret; + } + + static RangeFieldType minmod(const RangeFieldType& first_slope, const RangeFieldType& second_slope) + { + RangeFieldType ret = 0.; + if (std::signbit(first_slope) == std::signbit(second_slope)) // check for equal sign + ret = std::abs(first_slope) < std::abs(second_slope) ? first_slope : second_slope; + return ret; + } + + static RangeFieldType maxmod(const RangeFieldType first_slope, const RangeFieldType second_slope) + { + RangeFieldType ret(0.); + if (std::signbit(first_slope) == std::signbit(second_slope)) // check for equal sign + ret = (std::abs(first_slope) < std::abs(second_slope)) ? second_slope : first_slope; + return ret; + } + + static RangeFieldType superbee(const RangeFieldType first_slope, const RangeFieldType second_slope) + { + const RangeFieldType first_minmod = minmod(first_slope, second_slope * 2.); + const RangeFieldType second_minmod = minmod(first_slope * 2., second_slope); + return maxmod(first_minmod, second_minmod); + } + + template <class IntersectionType> + DomainType calculate_boundary_flux(const DomainType& alpha, const IntersectionType& intersection) + { + // evaluate exp(alpha^T b(v_i)) at all quadratures points v_i + const auto dd = intersection.indexInInside() / 2; + const auto& n_ij = intersection.centerUnitOuterNormal(); + DomainType ret(0.); + for (size_t jj = 0; jj < num_intervals; ++jj) { + for (size_t ll = 0; ll < quad_points_[jj].size(); ++ll) { + const auto position = quad_points_[jj][ll]; + if (position * n_ij[dd] < 0.) { + RangeFieldType factor = + std::exp(alpha[jj] * M_[jj][ll][0] + alpha[jj + 1] * M_[jj][ll][1]) * quad_weights_[jj][ll] * position; + const auto& basis_ll = M_[jj][ll]; + for (size_t mm = 0; mm < 2; ++mm) + ret[jj + mm] += basis_ll[mm] * factor; + } + } // ll + } // jj + return ret; + } + + // TODO: Calculates exp(alpha^T b) 2*dim times for each alpha (as it is contained in 2*dim stencils) at the moment. + template <class FluxesMapType> + void calculate_minmod_density_reconstruction(const FieldVector<DomainType, 3>& alphas, + FluxesMapType& flux_values, + const size_t dd) const + { + // evaluate exp(alpha^T b(v_i)) at all quadratures points v_i for all three alphas + thread_local XT::Common::FieldVector<std::vector<RangeFieldType>, 3> density_evaluations( + std::vector<RangeFieldType>(quad_points_[0].size())); + thread_local XT::Common::FieldVector<std::vector<RangeFieldType>, 2> reconstructed_values( + std::vector<RangeFieldType>(quad_points_[0].size())); + thread_local XT::Common::FieldVector<LocalVectorType, 3> local_alphas; + + // get flux storage + BasisDomainType coord(0.5); + coord[dd] = 0; + auto& left_flux_value = flux_values[coord]; + coord[dd] = 1; + auto& right_flux_value = flux_values[coord]; + right_flux_value = left_flux_value = DomainType(0.); + + for (size_t jj = 0; jj < num_intervals; ++jj) { + // get local alphas + for (size_t ii = 0; ii < 3; ++ii) + for (size_t mm = 0; mm < 2; ++mm) + local_alphas[ii][mm] = alphas[ii][jj + mm]; + + // evaluate densitys + for (size_t ii = 0; ii < 3; ++ii) + for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll) + density_evaluations[ii][ll] = std::exp(local_alphas[ii] * M_[jj][ll]); + + // reconstruct densities + auto& vals_left = reconstructed_values[0]; + auto& vals_right = reconstructed_values[1]; + for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll) { + const auto slope = minmod(density_evaluations[1][ll] - density_evaluations[0][ll], + density_evaluations[2][ll] - density_evaluations[1][ll]); + // const auto slope = superbee(density_evaluations[1][ll] - density_evaluations[0][ll], + // density_evaluations[2][ll] - density_evaluations[1][ll]); + vals_left[ll] = density_evaluations[1][ll] - 0.5 * slope; + vals_right[ll] = density_evaluations[1][ll] + 0.5 * slope; + } // ll (quad points) + + // calculate fluxes + for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll) { + const auto position = quad_points_[jj][ll]; + RangeFieldType factor = position > 0. ? vals_right[ll] : vals_left[ll]; + factor *= quad_weights_[jj][ll] * position; + auto& val = position > 0. ? right_flux_value : left_flux_value; + const auto& basis_ll = M_[jj][ll]; + for (size_t mm = 0; mm < 2; ++mm) + val[jj + mm] += basis_ll[mm] * factor; + } // ll (quad points) + } // jj (intervals) + } // void calculate_minmod_density_reconstruction(...) + // returns (alpha, (actual_u, r)), where r is the regularization parameter and actual_u the regularized u std::unique_ptr<AlphaReturnType> get_alpha(const DomainType& u, const VectorType& alpha_in, const bool regularize) const @@ -3277,7 +3626,6 @@ public: return basis_functions_; } -private: static bool is_realizable(const DomainType& u) { for (const auto& u_i : u) @@ -3309,7 +3657,7 @@ private: return ret; } // void calculate_u(...) - void calculate_u(const VectorType& alpha, VectorType& u) const + void calculate_u(const DomainType& alpha, DomainType& u) const { std::fill(u.begin(), u.end(), 0.); LocalVectorType local_alpha; @@ -3331,7 +3679,7 @@ private: g_k -= v; } - void calculate_hessian(const VectorType& alpha, + void calculate_hessian(const DomainType& alpha, const BasisValuesMatrixType& M, VectorType& H_diag, FieldVector<RangeFieldType, dimRange - 1>& H_subdiag) const @@ -3377,6 +3725,21 @@ private: } // jj (intervals) } // void calculate_J(...) + void apply_inverse_hessian(const DomainType& alpha, const DomainType& u, DomainType& Hinv_u) const + { + thread_local VectorType H_diag; + thread_local FieldVector<RangeFieldType, dimRange - 1> H_subdiag; + calculate_hessian(alpha, M_, H_diag, H_subdiag); + // std::cout << "H_diag: " << H_diag << ", H_subdiag: " << H_subdiag << std::endl; + // factorize H = LDL^T, where L is unit lower bidiagonal and D is diagonal + // H_diag is overwritten by the diagonal elements of D + // H_subdiag is overwritten by the subdiagonal elements of L + XT::LA::tridiagonal_ldlt(H_diag, H_subdiag); + // Solve H ret = u + Hinv_u = u; + XT::LA::solve_tridiagonal_ldlt_factorized(H_diag, H_subdiag, Hinv_u); + } + // calculates ret = J H^{-1}. Both J and H are symmetric tridiagonal, H is positive definite. static void calculate_J_Hinv(DynamicRowDerivativeRangeType& ret, const VectorType& J_diag, diff --git a/dune/gdt/momentmodels/entropyflux_kineticcoords.hh b/dune/gdt/momentmodels/entropyflux_kineticcoords.hh new file mode 100644 index 0000000000000000000000000000000000000000..4dafa99a3ef22cd762e1e9904bf4238eac80586e --- /dev/null +++ b/dune/gdt/momentmodels/entropyflux_kineticcoords.hh @@ -0,0 +1,195 @@ +// 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: +// Tobias Leibner (2019) + +#ifndef DUNE_GDT_LOCAL_FLUXES_ENTROPYBASED_KINETICCOORDS_HH +#define DUNE_GDT_LOCAL_FLUXES_ENTROPYBASED_KINETICCOORDS_HH + +#include <list> +#include <memory> + +#include <dune/xt/common/float_cmp.hh> +#include <dune/xt/common/vector_less.hh> + +#include <dune/xt/functions/interfaces/flux-function.hh> + +#include <dune/gdt/momentmodels/basisfunctions.hh> +#include <dune/gdt/momentmodels/entropyflux_implementations.hh> +#include <dune/gdt/momentmodels/entropyflux.hh> + +namespace Dune { +namespace GDT { + + +template <class GridViewImp, class MomentBasisImp> +class EntropyBasedFluxEntropyCoordsFunction + : public XT::Functions::FluxFunctionInterface<XT::Grid::extract_entity_t<GridViewImp>, + MomentBasisImp::dimRange, + MomentBasisImp::dimFlux, + MomentBasisImp::dimRange, + typename MomentBasisImp::R> +{ + using BaseType = typename XT::Functions::FluxFunctionInterface<XT::Grid::extract_entity_t<GridViewImp>, + MomentBasisImp::dimRange, + MomentBasisImp::dimFlux, + MomentBasisImp::dimRange, + typename MomentBasisImp::R>; + using ThisType = EntropyBasedFluxEntropyCoordsFunction; + +public: + using GridViewType = GridViewImp; + using MomentBasis = MomentBasisImp; + using IndexSetType = typename GridViewType::IndexSet; + static const size_t dimFlux = MomentBasis::dimFlux; + static const size_t basis_dimRange = MomentBasis::dimRange; + using typename BaseType::DomainType; + using typename BaseType::E; + using typename BaseType::LocalFunctionType; + using typename BaseType::RangeFieldType; + using typename BaseType::StateType; + using ImplementationType = EntropyBasedFluxImplementation<MomentBasis>; + using AlphaReturnType = typename ImplementationType::AlphaReturnType; + using VectorType = typename ImplementationType::VectorType; + using I = XT::Grid::extract_intersection_t<GridViewType>; + + explicit EntropyBasedFluxEntropyCoordsFunction( + const MomentBasis& basis_functions, + const RangeFieldType tau = 1e-9, + const RangeFieldType epsilon_gamma = 0.01, + const RangeFieldType chi = 0.5, + const RangeFieldType xi = 1e-3, + const std::vector<RangeFieldType> r_sequence = {0, 1e-8, 1e-6, 1e-4, 1e-3, 1e-2, 5e-2, 0.1, 0.5, 1}, + const size_t k_0 = 500, + const size_t k_max = 1000, + const RangeFieldType epsilon = std::pow(2, -52)) + : implementation_(std::make_shared<ImplementationType>( + basis_functions, tau, epsilon_gamma, chi, xi, r_sequence, k_0, k_max, epsilon)) + {} + + explicit EntropyBasedFluxEntropyCoordsFunction(EntropyBasedFluxFunction<GridViewType, MomentBasis>& other) + : implementation_(other.implementation_) + {} + + + static const constexpr bool available = true; + + class Localfunction : public LocalFunctionType + { + using BaseType = LocalFunctionType; + + public: + using typename BaseType::DynamicJacobianRangeType; + using typename BaseType::E; + using typename BaseType::RangeReturnType; + + Localfunction(const ImplementationType& implementation) + : implementation_(implementation) + {} + + virtual int order(const XT::Common::Parameter&) const override final + { + return 1.; + } + + virtual RangeReturnType evaluate(const DomainType& /*point_in_reference_element*/, + const StateType& alpha, + const XT::Common::Parameter& /*param*/ = {}) const override final + { + return implementation_.evaluate_with_alpha(alpha); + } + + virtual void jacobian(const DomainType& /*point_in_reference_element*/, + const StateType& alpha, + DynamicJacobianRangeType& result, + const XT::Common::Parameter& /*param*/ = {}) const override final + { + implementation_.jacobian_with_alpha(alpha, result); + } // ... jacobian(...) + + private: + const ImplementationType& implementation_; + }; // class Localfunction + + virtual bool x_dependent() const override final + { + return false; + } + + virtual std::unique_ptr<LocalFunctionType> local_function() const override final + { + return std::make_unique<Localfunction>(*implementation_); + } + + virtual std::unique_ptr<Localfunction> derived_local_function() const + { + return std::make_unique<Localfunction>(*implementation_); + } + + StateType evaluate_kinetic_flux_precomputed(const StateType& flux_1, + const StateType& flux_2, + const DomainType& n_ij, + const size_t dd) const + { + return (flux_1 + flux_2) * n_ij[dd]; + } // StateType evaluate_kinetic_flux(...) + + StateType evaluate_kinetic_flux(const E& /*inside_entity*/, + const E& /*outside_entity*/, + const StateType& alpha_i, + const StateType& alpha_j, + const DomainType& n_ij, + const size_t dd) const + { + return implementation_->evaluate_kinetic_flux_with_alphas(alpha_i, alpha_j, n_ij, dd); + } // StateType evaluate_kinetic_flux(...) + + const MomentBasis& basis_functions() const + { + return implementation_->basis_functions(); + } + + StateType get_u(const StateType& alpha) const + { + return implementation_->get_u(alpha); + } + + StateType get_alpha(const StateType& u) const + { + const auto alpha = implementation_->get_alpha(u)->first; + StateType ret; + std::copy(alpha.begin(), alpha.end(), ret.begin()); + return ret; + } + + template <class FluxesMapType> + void calculate_minmod_density_reconstruction(const FieldVector<StateType, 3>& alphas, + FluxesMapType& precomputed_fluxes, + const size_t dd) const + { + implementation_->calculate_minmod_density_reconstruction(alphas, precomputed_fluxes, dd); + } + + StateType calculate_boundary_flux(const StateType& alpha, const I& intersection) + { + return implementation_->calculate_boundary_flux(alpha, intersection); + } + + void apply_inverse_hessian(const StateType& alpha, const StateType& u, StateType& Hinv_u) const + { + implementation_->apply_inverse_hessian(alpha, u, Hinv_u); + } + +private: + std::shared_ptr<ImplementationType> implementation_; +}; + + +} // namespace GDT +} // namespace Dune + +#endif // DUNE_GDT_LOCAL_FLUXES_ENTROPYBASED_KINETICCOORDS_HH diff --git a/dune/gdt/momentmodels/hessianinverter.hh b/dune/gdt/momentmodels/hessianinverter.hh new file mode 100644 index 0000000000000000000000000000000000000000..12f49ea7d406ba994a51b30dded5a4921ec347a6 --- /dev/null +++ b/dune/gdt/momentmodels/hessianinverter.hh @@ -0,0 +1,199 @@ +// 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: +// Tobias Leibner (2018) + +#ifndef DUNE_GDT_MOMENTMODELS_HESSIANINVERTER_HH +#define DUNE_GDT_MOMENTMODELS_HESSIANINVERTER_HH + +#include <string> + +#include <dune/xt/grid/functors/interfaces.hh> +#include <dune/xt/common/parameter.hh> + +#include <dune/gdt/discretefunction/default.hh> +#include <dune/gdt/momentmodels/entropyflux_kineticcoords.hh> +#include <dune/gdt/operators/interfaces.hh> +#include <dune/gdt/type_traits.hh> + +namespace Dune { +namespace GDT { + + +template <class SpaceType, class VectorType, class MomentBasis> +class LocalEntropicHessianInverter : public XT::Grid::ElementFunctor<typename SpaceType::GridViewType> +{ + using GridViewType = typename SpaceType::GridViewType; + using BaseType = XT::Grid::ElementFunctor<GridViewType>; + using EntityType = typename GridViewType::template Codim<0>::Entity; + using IndexSetType = typename GridViewType::IndexSet; + using EntropyFluxType = EntropyBasedFluxEntropyCoordsFunction<GridViewType, MomentBasis>; + using RangeFieldType = typename EntropyFluxType::RangeFieldType; + using LocalVectorType = typename EntropyFluxType::VectorType; + static const size_t dimFlux = EntropyFluxType::dimFlux; + static const size_t dimRange = EntropyFluxType::basis_dimRange; + using DiscreteFunctionType = DiscreteFunction<VectorType, GridViewType, dimRange, 1, RangeFieldType>; + using ConstDiscreteFunctionType = ConstDiscreteFunction<VectorType, GridViewType, dimRange, 1, RangeFieldType>; + +public: + explicit LocalEntropicHessianInverter(const SpaceType& space, + const VectorType& alpha_dofs, + const VectorType& u_update_dofs, + VectorType& alpha_range_dofs, + const EntropyFluxType& analytical_flux, + const RangeFieldType min_acceptable_density, + const XT::Common::Parameter& param, + const std::string filename = "") + : space_(space) + , alpha_(space_, alpha_dofs, "alpha") + , u_update_(space_, u_update_dofs, "u_update") + , local_alpha_(alpha_.local_discrete_function()) + , local_u_update_(u_update_.local_discrete_function()) + , range_(space_, alpha_range_dofs, "range") + , local_range_(range_.local_discrete_function()) + , analytical_flux_(analytical_flux) + , local_flux_(analytical_flux_.derived_local_function()) + , min_acceptable_density_(min_acceptable_density) + , param_(param) + , filename_(filename.empty() ? "" : (filename + "_regularization.txt")) + , index_set_(space_.grid_view().indexSet()) + {} + + explicit LocalEntropicHessianInverter(LocalEntropicHessianInverter& other) + : BaseType(other) + , space_(other.space_) + , alpha_(space_, other.alpha_.dofs().vector(), "source") + , u_update_(space_, other.u_update_.dofs().vector(), "source") + , local_alpha_(alpha_.local_discrete_function()) + , local_u_update_(u_update_.local_discrete_function()) + , range_(space_, other.range_.dofs().vector(), "range") + , local_range_(range_.local_discrete_function()) + , analytical_flux_(other.analytical_flux_) + , local_flux_(analytical_flux_.derived_local_function()) + , min_acceptable_density_(other.min_acceptable_density_) + , param_(other.param_) + , filename_(other.filename_) + , index_set_(space_.grid_view().indexSet()) + {} + + virtual XT::Grid::ElementFunctor<GridViewType>* copy() override final + { + return new LocalEntropicHessianInverter(*this); + } + + void apply_local(const EntityType& entity) override final + { + local_alpha_->bind(entity); + local_u_update_->bind(entity); + local_range_->bind(entity); + XT::Common::FieldVector<RangeFieldType, dimRange> u, Hinv_u, alpha; + for (size_t ii = 0; ii < dimRange; ++ii) { + u[ii] = local_u_update_->dofs().get_entry(ii); + alpha[ii] = local_alpha_->dofs().get_entry(ii); + } + analytical_flux_.apply_inverse_hessian(alpha, u, Hinv_u); + for (auto&& entry : Hinv_u) + if (std::isnan(entry) || std::isinf(entry)) { + // std::cout << "x: " << entity.geometry().center() << "u: " << u << ", alpha: " << alpha << ", Hinv_u: " + // << Hinv_u << std::endl; + DUNE_THROW(Dune::MathError, "Hessian"); + } + + for (size_t ii = 0; ii < dimRange; ++ii) + local_range_->dofs().set_entry(ii, Hinv_u[ii]); + } // void apply_local(...) + +private: + const SpaceType& space_; + const ConstDiscreteFunctionType alpha_; + const ConstDiscreteFunctionType u_update_; + std::unique_ptr<typename ConstDiscreteFunctionType::ConstLocalDiscreteFunctionType> local_alpha_; + std::unique_ptr<typename ConstDiscreteFunctionType::ConstLocalDiscreteFunctionType> local_u_update_; + DiscreteFunctionType range_; + std::unique_ptr<typename DiscreteFunctionType::LocalDiscreteFunctionType> local_range_; + const EntropyFluxType& analytical_flux_; + std::unique_ptr<typename EntropyFluxType::Localfunction> local_flux_; + const RangeFieldType min_acceptable_density_; + const XT::Common::Parameter& param_; + const std::string filename_; + const typename SpaceType::GridViewType::IndexSet& index_set_; +}; // class LocalEntropicHessianInverter<...> + +template <class MomentBasisImp, + class SpaceImp, + class MatrixType = typename XT::LA::Container<typename MomentBasisImp::RangeFieldType>::MatrixType> +class EntropicHessianInverter + : public OperatorInterface<MatrixType, typename SpaceImp::GridViewType, MomentBasisImp::dimRange, 1> +{ + using BaseType = OperatorInterface<MatrixType, typename SpaceImp::GridViewType, MomentBasisImp::dimRange, 1>; + +public: + using typename BaseType::VectorType; + using MomentBasis = MomentBasisImp; + using SpaceType = SpaceImp; + using SourceSpaceType = SpaceImp; + using RangeSpaceType = SpaceImp; + using EntropyFluxType = EntropyBasedFluxEntropyCoordsFunction<typename SpaceType::GridViewType, MomentBasis>; + using RangeFieldType = typename MomentBasis::RangeFieldType; + using LocalVectorType = typename EntropyFluxType::VectorType; + + EntropicHessianInverter(const EntropyFluxType& analytical_flux, + const SpaceType& space, + const RangeFieldType min_acceptable_density, + const std::string filename = "") + : analytical_flux_(analytical_flux) + , space_(space) + , min_acceptable_density_(min_acceptable_density) + , filename_(filename) + {} + + virtual bool linear() const override final + { + return false; + } + + virtual const SourceSpaceType& source_space() const override final + { + return space_; + } + + virtual const RangeSpaceType& range_space() const override final + { + return space_; + } + + void apply(const VectorType& /*source*/, + VectorType& /*range*/, + const XT::Common::Parameter& /*param*/) const override final + { + DUNE_THROW(Dune::NotImplemented, "Use apply_inverse_hessian!"); + } // void apply(...) + + void apply_inverse_hessian(const VectorType& alpha, + const VectorType& u_update, + VectorType& alpha_update, + const XT::Common::Parameter& param) const + { + LocalEntropicHessianInverter<SpaceType, VectorType, MomentBasis> local_hessian_inverter( + space_, alpha, u_update, alpha_update, analytical_flux_, min_acceptable_density_, param, filename_); + auto walker = XT::Grid::Walker<typename SpaceType::GridViewType>(space_.grid_view()); + walker.append(local_hessian_inverter); + walker.walk(true); + } // void apply(...) + +private: + const EntropyFluxType& analytical_flux_; + const SpaceType& space_; + const RangeFieldType min_acceptable_density_; + const std::string filename_; +}; // class EntropicHessianInverter<...> + + +} // namespace GDT +} // namespace Dune + +#endif // DUNE_GDT_MOMENTMODELS_HESSIANINVERTER_HH diff --git a/dune/gdt/operators/advection-fv-entropybased.hh b/dune/gdt/operators/advection-fv-entropybased.hh index 98392af3b1d0dcb8b2f5e076b86bcde65901b06a..e9a5b6fe1e09df3d279675dab5175645a3f52be0 100644 --- a/dune/gdt/operators/advection-fv-entropybased.hh +++ b/dune/gdt/operators/advection-fv-entropybased.hh @@ -17,6 +17,111 @@ namespace Dune { namespace GDT { +template <class OperatorImp, class InverseHessianOperatorImp> +class EntropicCoordinatesOperator + : public OperatorInterface<typename OperatorImp::MatrixType, typename OperatorImp::SGV, OperatorImp::s_r> +{ + using BaseType = OperatorInterface<typename OperatorImp::MatrixType, typename OperatorImp::SGV, OperatorImp::s_r>; + +public: + using typename BaseType::RangeSpaceType; + using typename BaseType::SourceSpaceType; + using typename BaseType::VectorType; + + using OperatorType = OperatorImp; + using InverseHessianOperatorType = InverseHessianOperatorImp; + + EntropicCoordinatesOperator(const OperatorType& operator_in, + const InverseHessianOperatorType& inverse_hessian_operator) + : operator_(operator_in) + , inverse_hessian_operator_(inverse_hessian_operator) + {} + + virtual bool linear() const override final + { + return false; + } + + virtual const SourceSpaceType& source_space() const override final + { + return operator_.source_space(); + } + + virtual const RangeSpaceType& range_space() const override final + { + return operator_.range_space(); + } + + void apply(const VectorType& source, VectorType& range, const XT::Common::Parameter& param) const override final + { + VectorType u_update = range; + std::fill(u_update.begin(), u_update.end(), 0.); + operator_.apply(source, u_update, param); + inverse_hessian_operator_.apply_inverse_hessian(source, u_update, range, param); + } + + const OperatorType& operator_; + const InverseHessianOperatorType& inverse_hessian_operator_; +}; // class EntropicCoordinatesOperator<...> + +template <class AdvectionOperatorImp, class RhsOperatorImp, class InverseHessianOperatorImp> +class EntropicCoordinatesCombinedOperator + : public OperatorInterface<typename AdvectionOperatorImp::MatrixType, + typename AdvectionOperatorImp::SGV, + AdvectionOperatorImp::s_r> +{ + using BaseType = OperatorInterface<typename AdvectionOperatorImp::MatrixType, + typename AdvectionOperatorImp::SGV, + AdvectionOperatorImp::s_r>; + +public: + using typename BaseType::RangeSpaceType; + using typename BaseType::SourceSpaceType; + using typename BaseType::VectorType; + + using AdvectionOperatorType = AdvectionOperatorImp; + using RhsOperatorType = RhsOperatorImp; + using InverseHessianOperatorType = InverseHessianOperatorImp; + + EntropicCoordinatesCombinedOperator(const AdvectionOperatorType& advection_op, + const RhsOperatorType& rhs_op, + const InverseHessianOperatorType& inverse_hessian_operator) + : advection_op_(advection_op) + , rhs_op_(rhs_op) + , inverse_hessian_operator_(inverse_hessian_operator) + {} + + virtual bool linear() const override final + { + return false; + } + + virtual const SourceSpaceType& source_space() const override final + { + return advection_op_.source_space(); + } + + virtual const RangeSpaceType& range_space() const override final + { + return advection_op_.range_space(); + } + + void apply(const VectorType& source, VectorType& range, const XT::Common::Parameter& param) const override final + { + VectorType u_update = range; + std::fill(u_update.begin(), u_update.end(), 0.); + advection_op_.apply(source, u_update, param); + u_update *= -1.; + rhs_op_.apply(source, u_update, param); + inverse_hessian_operator_.apply_inverse_hessian(source, u_update, range, param); + } + + const AdvectionOperatorType& advection_op_; + const RhsOperatorType& rhs_op_; + const InverseHessianOperatorType& inverse_hessian_operator_; +}; // class EntropicCoordinatesOperator<...> + + template <class AdvectionOperatorImp, class EntropySolverImp> class EntropyBasedMomentFvOperator : public OperatorInterface<typename AdvectionOperatorImp::MatrixType, diff --git a/dune/gdt/operators/reconstruction/internal.hh b/dune/gdt/operators/reconstruction/internal.hh index 2df45463dd9a330fda0ee4f40a56fa50d65ad630..850095c7c27f7f4bf32e943b678212879629bfd0 100644 --- a/dune/gdt/operators/reconstruction/internal.hh +++ b/dune/gdt/operators/reconstruction/internal.hh @@ -117,6 +117,55 @@ constexpr size_t EigenvectorWrapperBase<AnalyticalFluxType, MatrixImp, VectorImp template <class AnalyticalFluxType, class MatrixImp, class VectorImp> constexpr size_t EigenvectorWrapperBase<AnalyticalFluxType, MatrixImp, VectorImp>::dimRange; +// This class does not perform any computations, use this class if you want to reconstruct in ordinary coordinates +// instead of characteristic coordinates +template <class AnalyticalFluxType, class VectorImp> +class DummyEigenVectorWrapper : public EigenvectorWrapperBase<AnalyticalFluxType, int, VectorImp> +{ + using BaseType = EigenvectorWrapperBase<AnalyticalFluxType, int, VectorImp>; + +public: + using typename BaseType::DomainType; + using typename BaseType::E; + using typename BaseType::MatrixType; + using typename BaseType::VectorType; + + DummyEigenVectorWrapper(const AnalyticalFluxType& analytical_flux, const bool flux_is_affine) + : BaseType(analytical_flux, flux_is_affine) + {} + + virtual void apply_eigenvectors(const size_t /*dd*/, const VectorType& u, VectorType& ret) const override final + { + ret = u; + } + virtual void + apply_inverse_eigenvectors(const size_t /*dd*/, const VectorType& u, VectorType& ret) const override final + { + ret = u; + } + + virtual void compute_eigenvectors(const E& /*entity*/, + const DomainType& /*x_local*/, + const VectorType& /*u*/, + const XT::Common::Parameter& /*param*/) const override final + {} + + virtual void compute_eigenvectors_impl(const E& /*entity*/, + const DomainType& /*x_local*/, + const VectorType& /*u*/, + const XT::Common::Parameter& /*param*/) const override final + {} + + virtual const MatrixType& eigenvectors(const size_t /*dd*/) const override final + { + DUNE_THROW(Dune::NotImplemented, "This class does not provide eigenvectors!"); + return zero_; + } + +private: + static const MatrixType zero_ = 0.; +}; + template <class AnalyticalFluxType, class MatrixType = XT::LA::CommonDenseMatrix<typename AnalyticalFluxType::R>, class VectorType = FieldVector<typename AnalyticalFluxType::RangeFieldType, AnalyticalFluxType::rC>> diff --git a/dune/gdt/operators/reconstruction/linear.hh b/dune/gdt/operators/reconstruction/linear.hh index 375214d16ad36f5fcb2236124d5350547ae3aea6..580d34b819df7fe673077bbbed8ce96a83c48b73 100644 --- a/dune/gdt/operators/reconstruction/linear.hh +++ b/dune/gdt/operators/reconstruction/linear.hh @@ -127,7 +127,7 @@ private: const size_t dd = intersection.indexInInside() / 2; const size_t index = (intersection.indexInInside() % 2) * 2; if (intersection.boundary() && !intersection.neighbor()) // boundary intersections - stencils_[dd][index] = boundary_values_.evaluate(entity.geometry().local(intersection.geometry().center())); + stencils_[dd][index] = boundary_values_.evaluate(intersection.geometry().center()); else if (intersection.neighbor()) // inner and periodic intersections stencils_[dd][index] = source_values_[grid_view_.indexSet().index(intersection.outside())]; else if (!intersection.neighbor() && !intersection.boundary()) // processor boundary diff --git a/dune/gdt/operators/reconstruction/linear_kinetic.hh b/dune/gdt/operators/reconstruction/linear_kinetic.hh new file mode 100644 index 0000000000000000000000000000000000000000..30ec756c78498ad4d46daedb1e154ffbee83ffad --- /dev/null +++ b/dune/gdt/operators/reconstruction/linear_kinetic.hh @@ -0,0 +1,190 @@ +// 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: +// Tobias Leibner (2019) + +#ifndef DUNE_GDT_OPERATORS_FV_RECONSTRUCTION_LINEAR_KINETIC_HH +#define DUNE_GDT_OPERATORS_FV_RECONSTRUCTION_LINEAR_KINETIC_HH + +#include <dune/geometry/quadraturerules.hh> + +#include <dune/xt/common/fvector.hh> +#include <dune/xt/common/parameter.hh> + +#include <dune/xt/grid/walker.hh> + +#include <dune/gdt/operators/interfaces.hh> +#include <dune/gdt/spaces/l2/discontinuous-lagrange.hh> +#include <dune/gdt/tools/discretevalued-grid-function.hh> + +#include "slopes.hh" +#include "internal.hh" + +namespace Dune { +namespace GDT { + + +template <class GV, class AnalyticalFluxType, class BoundaryValueType, class LocalVectorType> +class LocalPointwiseLinearKineticReconstructionOperator : public XT::Grid::ElementFunctor<GV> +{ + using ThisType = LocalPointwiseLinearKineticReconstructionOperator; + using BaseType = XT::Grid::ElementFunctor<GV>; + static constexpr size_t dimDomain = BoundaryValueType::d; + static constexpr size_t dimRange = BoundaryValueType::r; + using EntityType = typename GV::template Codim<0>::Entity; + using RangeFieldType = typename BoundaryValueType::RangeFieldType; + using StencilType = DynamicVector<LocalVectorType>; + using StencilsType = std::vector<StencilType>; + using DomainType = typename BoundaryValueType::DomainType; + using RangeType = typename BoundaryValueType::RangeReturnType; + static constexpr size_t stencil_size = 3; + using ReconstructedFunctionType = DiscreteValuedGridFunction<GV, dimRange, 1, RangeFieldType>; + +public: + explicit LocalPointwiseLinearKineticReconstructionOperator(ReconstructedFunctionType& reconstructed_function, + const GV& grid_view, + const AnalyticalFluxType& analytical_flux, + const std::vector<LocalVectorType>& source_values, + const BoundaryValueType& boundary_values, + const XT::Common::Parameter& param) + : reconstructed_function_(reconstructed_function) + , grid_view_(grid_view) + , analytical_flux_(analytical_flux) + , source_values_(source_values) + , boundary_values_(boundary_values) + , param_(param) + , stencils_(dimDomain, StencilType(stencil_size)) + {} + + LocalPointwiseLinearKineticReconstructionOperator(const ThisType& other) + : BaseType(other) + , reconstructed_function_(other.reconstructed_function_) + , grid_view_(other.grid_view_) + , analytical_flux_(other.analytical_flux_) + , source_values_(other.source_values_) + , boundary_values_(other.boundary_values_) + , param_(other.param_) + , stencils_(dimDomain, StencilType(stencil_size)) + {} + + virtual XT::Grid::ElementFunctor<GV>* copy() override final + { + return new ThisType(*this); + } + + virtual void apply_local(const EntityType& entity) override final + { + // In a MPI parallel run, if entity is on boundary of overlap, we do not have to reconstruct + if (!fill_stencils(entity)) + return; + + auto& local_reconstructed_values = reconstructed_function_.local_values(entity); + for (size_t dd = 0; dd < dimDomain; ++dd) + analytical_flux_.calculate_minmod_density_reconstruction(stencils_[dd], local_reconstructed_values, dd); + } // void apply_local(...) + +private: + bool fill_stencils(const EntityType& entity) + { + const auto entity_index = grid_view_.indexSet().index(entity); + for (size_t dd = 0; dd < dimDomain; ++dd) + stencils_[dd][1] = source_values_[entity_index]; + for (const auto& intersection : Dune::intersections(grid_view_, entity)) { + const size_t dd = intersection.indexInInside() / 2; + const size_t index = (intersection.indexInInside() % 2) * 2; + if (intersection.boundary() && !intersection.neighbor()) { // boundary intersections + stencils_[dd][index] = boundary_values_.evaluate(intersection.geometry().center()); + } else if (intersection.neighbor()) { // inner and periodic intersections { + stencils_[dd][index] = source_values_[grid_view_.indexSet().index(intersection.outside())]; + } else if (!intersection.neighbor() && !intersection.boundary()) { // processor boundary + return false; + } else { + DUNE_THROW(Dune::NotImplemented, "This should not happen!"); + } + } // intersections + return true; + } // void fill_stencils(...) + + ReconstructedFunctionType& reconstructed_function_; + const GV& grid_view_; + const AnalyticalFluxType& analytical_flux_; + const std::vector<LocalVectorType>& source_values_; + const BoundaryValueType& boundary_values_; + const XT::Common::Parameter& param_; + StencilsType stencils_; +}; // class LocalPointwiseLinearKineticReconstructionOperator + +// Does not reconstruct a full first-order DG function, but only stores the reconstructed values at the intersection +// centers. This avoids the interpolation in this operator and the evaluation of the reconstructed function in the +// finite volume operator which are both quite expensive for large dimRange. +template <class GV, class BoundaryValueImp, class AnalyticalFluxType, class VectorType, class LocalVectorType> +class PointwiseLinearKineticReconstructionOperator +{ +public: + using BoundaryValueType = BoundaryValueImp; + using E = XT::Grid::extract_entity_t<GV>; + using EigenVectorWrapperType = DummyEigenVectorWrapper<LocalVectorType>; + using R = typename BoundaryValueType::R; + static constexpr size_t dimDomain = BoundaryValueType::d; + static constexpr size_t dimRange = BoundaryValueType::r; + using SpaceType = SpaceInterface<GV, dimRange, 1, R>; + using ReconstructedFunctionType = DiscreteValuedGridFunction<GV, dimRange, 1, R>; + using ReconstructedValuesType = std::vector<typename ReconstructedFunctionType::LocalFunctionValuesType>; + + PointwiseLinearKineticReconstructionOperator(const BoundaryValueType& boundary_values, + const SpaceType& space, + const AnalyticalFluxType& analytical_flux) + : boundary_values_(boundary_values) + , space_(space) + , analytical_flux_(analytical_flux) + {} + + bool linear() const + { + return false; + } + + const SpaceType& space() const + { + return space_; + } + + void apply(const VectorType& source, ReconstructedFunctionType& range, const XT::Common::Parameter& param) const + { + // evaluate cell averages + const auto& grid_view = space_.grid_view(); + const auto& index_set = grid_view.indexSet(); + std::vector<LocalVectorType> source_values(index_set.size(0)); + // Contains the evaluations of exp(alpha^T b(v_i)) at each quadrature point v_i + auto source_func = make_discrete_function(space_, source); + const auto local_source = source_func.local_function(); + for (const auto& entity : Dune::elements(grid_view)) { + local_source->bind(entity); + const auto entity_index = index_set.index(entity); + source_values[entity_index] = local_source->evaluate(entity.geometry().local(entity.geometry().center())); + } + + // do reconstruction + auto local_reconstruction_operator = + LocalPointwiseLinearKineticReconstructionOperator<GV, AnalyticalFluxType, BoundaryValueType, LocalVectorType>( + range, grid_view, analytical_flux_, source_values, boundary_values_, param); + auto walker = XT::Grid::Walker<GV>(grid_view); + walker.append(local_reconstruction_operator); + walker.walk(true); + } // void apply(...) + +private: + const BoundaryValueType& boundary_values_; + const SpaceType& space_; + const AnalyticalFluxType& analytical_flux_; +}; // class PointwiseLinearKineticReconstructionOperator<...> + + +} // namespace GDT +} // namespace Dune + +#endif // DUNE_GDT_OPERATORS_FV_RECONSTRUCTION_LINEAR_KINETIC_HH diff --git a/dune/gdt/operators/reconstruction/slopes.hh b/dune/gdt/operators/reconstruction/slopes.hh index 6a35099e8e4ed39dec396107e418dc940cfe38fd..a840a2aa7afe8fb16fa298255037b8cdeba68728 100644 --- a/dune/gdt/operators/reconstruction/slopes.hh +++ b/dune/gdt/operators/reconstruction/slopes.hh @@ -213,6 +213,16 @@ public: return minmod(slope_left_char, slope_right_char); } + virtual VectorType get(const E& /*entity*/, + const StencilType& stencil, + const EigenVectorWrapperType& /*eigenvectors*/, + const size_t /*dd*/) const override final + { + const VectorType slope_left = stencil[1] - stencil[0]; + const VectorType slope_right = stencil[2] - stencil[1]; + return minmod(slope_left, slope_right); + } + static VectorType minmod(const VectorType& first_slope, const VectorType& second_slope) { VectorType ret(0.); diff --git a/dune/gdt/test/entropic-coords-mn-discretization.hh b/dune/gdt/test/entropic-coords-mn-discretization.hh new file mode 100644 index 0000000000000000000000000000000000000000..b6b24fa666dffd0dd524f3a73f195339bf0efafc --- /dev/null +++ b/dune/gdt/test/entropic-coords-mn-discretization.hh @@ -0,0 +1,384 @@ +// This file is part of the dune-gdt project: +// https://github.com/dune-community/dune-gdt +// Copyright 2010-2016 dune-gdt developers and contributors. All rights reserved. +// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause) +// Authors: +// Tobias Leibner (2016) + +#ifndef DUNE_GDT_TEST_HYPERBOLIC_ENTROPIC_COORDS_MN_DISCRETIZATION_HH +#define DUNE_GDT_TEST_HYPERBOLIC_ENTROPIC_COORDS_MN_DISCRETIZATION_HH + +#include <chrono> + +#include <dune/xt/common/parallel/threadmanager.hh> +#include <dune/xt/common/string.hh> +#include <dune/xt/common/test/gtest/gtest.h> + +#include <dune/xt/grid/information.hh> +#include <dune/xt/grid/gridprovider.hh> + +#include <dune/xt/la/container.hh> + +#include <dune/gdt/discretefunction/default.hh> +#include <dune/gdt/operators/advection-fv-entropybased.hh> +#include <dune/gdt/operators/advection-fv.hh> +#include <dune/gdt/operators/generic.hh> +#include <dune/gdt/operators/reconstruction/linear_nocharacteristiccoords.hh> +#include <dune/gdt/operators/reconstruction/linear_kinetic.hh> +#include <dune/gdt/interpolations/default.hh> +#include <dune/gdt/momentmodels/hessianinverter.hh> +#include <dune/gdt/momentmodels/entropyflux_kineticcoords.hh> +#include <dune/gdt/momentmodels/entropyflux.hh> +#include <dune/gdt/local/numerical-fluxes/kinetic.hh> +#include <dune/gdt/local/operators/advection-fv.hh> +#include <dune/gdt/spaces/l2/finite-volume.hh> +#include <dune/gdt/tools/timestepper/fractional-step.hh> +#include <dune/gdt/tools/timestepper/explicit-rungekutta.hh> +#include <dune/gdt/tools/timestepper/adaptive-rungekutta.hh> +#include <dune/gdt/tools/timestepper/matrix-exponential-kinetic-isotropic.hh> + +#include <dune/gdt/test/momentmodels/kineticequation.hh> + +#include "pn-discretization.hh" + +template <class TestCaseType> +struct HyperbolicEntropicCoordsMnDiscretization +{ + // returns: (l1norm, l2norm, linfnorm, MPI rank) + static std::pair<Dune::FieldVector<double, 3>, int> run(size_t num_save_steps = 1, + size_t num_output_steps = 0, + size_t quad_order = size_t(-1), + size_t quad_refinements = size_t(-1), + std::string grid_size = "", + size_t overlap_size = 2, + double t_end = 0., + std::string filename = "", + bool /*disable_thread_cache*/ = false) + { + using namespace Dune; + using namespace Dune::GDT; + + //******************* get typedefs and constants from ProblemType **********************// + using MomentBasis = typename TestCaseType::MomentBasis; + using DiscreteFunctionType = typename TestCaseType::DiscreteFunctionType; + using GridType = typename TestCaseType::GridType; + using SpaceType = typename TestCaseType::SpaceType; + using AdvectionSourceSpaceType = typename TestCaseType::AdvectionSourceSpaceType; + using GV = typename TestCaseType::GridViewType; + // using E = XT::Grid::extract_entity_t<GV>; + using I = XT::Grid::extract_intersection_t<GV>; + using ProblemType = typename TestCaseType::ProblemType; + using RangeFieldType = typename MomentBasis::RangeFieldType; + using BoundaryValueType = typename ProblemType::BoundaryValueType; + static constexpr size_t dimDomain = MomentBasis::dimDomain; + static constexpr size_t dimRange = MomentBasis::dimRange; + using MatrixType = typename XT::LA::Container<RangeFieldType>::MatrixType; + using VectorType = typename XT::LA::Container<RangeFieldType>::VectorType; + using GenericFunctionType = XT::Functions::GenericFunction<dimDomain, dimRange, 1, RangeFieldType>; + using DomainType = FieldVector<RangeFieldType, dimDomain>; + using RangeType = FieldVector<RangeFieldType, dimRange>; + // static const RangeFieldType scale_factor = 1e2; + + //******************* create grid and FV space *************************************** + auto grid_config = ProblemType::default_grid_cfg(); + if (!grid_size.empty()) + grid_config["num_elements"] = grid_size; + grid_config["overlap_size"] = XT::Common::to_string(overlap_size); + const auto grid_ptr = + Dune::XT::Grid::CubeGridProviderFactory<GridType>::create(grid_config, MPIHelper::getCommunicator()).grid_ptr(); + assert(grid_ptr->comm().size() == 1 || grid_ptr->overlapSize(0) > 0); + const GV grid_view(grid_ptr->leafGridView()); + const SpaceType fv_space(grid_view); + const AdvectionSourceSpaceType advection_source_space(grid_view, 1); + + //******************* create EquationType object *************************************** + std::shared_ptr<const MomentBasis> basis_functions = std::make_shared<const MomentBasis>( + quad_order == size_t(-1) ? MomentBasis::default_quad_order() : quad_order, + quad_refinements == size_t(-1) ? MomentBasis::default_quad_refinements() : quad_refinements); + const std::unique_ptr<ProblemType> problem_ptr = + XT::Common::make_unique<ProblemType>(*basis_functions, grid_view, grid_config); + const auto& problem = *problem_ptr; + const auto initial_values_u = problem.initial_values(); + const auto boundary_values_u = problem.boundary_values(); + + using AnalyticalFluxType = typename ProblemType::FluxType; + using EntropyFluxType = EntropyBasedFluxEntropyCoordsFunction<GV, MomentBasis>; + using OldEntropyFluxType = EntropyBasedFluxFunction<GV, MomentBasis>; + auto flux = problem.flux(); + auto* entropy_flux = dynamic_cast<OldEntropyFluxType*>(flux.get()); + auto analytical_flux = std::make_unique<EntropyFluxType>(*entropy_flux); + // for Legendre polynomials and real spherical harmonics, the results are sensitive to the initial guess in the + // Newton algorithm. If the thread cache is enabled, the guess is different dependent on how many threads we are + // using, so for the tests we disable this cache to get reproducible results. + const RangeFieldType CFL = problem.CFL(); + + // calculate boundary values for alpha + std::map<DomainType, RangeType, XT::Common::FieldVectorFloatLess> alpha_boundary_vals; + for (const auto& element : Dune::elements(grid_view)) + for (const auto& intersection : Dune::intersections(grid_view, element)) + if (intersection.boundary()) { + const auto x = intersection.geometry().center(); + const auto u = boundary_values_u->evaluate(x); + alpha_boundary_vals.insert(std::make_pair(x, analytical_flux->get_alpha(u))); + } + GenericFunctionType boundary_values_alpha( + 1, [&](const DomainType& x, const XT::Common::Parameter&) { return alpha_boundary_vals[x]; }); + + // calculate boundary kinetic fluxes + std::map<DomainType, RangeType, XT::Common::FieldVectorFloatLess> boundary_fluxes; + for (const auto& element : Dune::elements(grid_view)) + for (const auto& intersection : Dune::intersections(grid_view, element)) + if (intersection.boundary()) { + const auto x = intersection.geometry().center(); + const auto boundary_flux = analytical_flux->calculate_boundary_flux(alpha_boundary_vals[x], intersection); + boundary_fluxes.insert(std::make_pair(x, boundary_flux)); + } + GenericFunctionType boundary_kinetic_fluxes( + 1, [&](const DomainType& x, const XT::Common::Parameter&) { return boundary_fluxes[x]; }); + + + // ***************** project initial values to discrete function ********************* + // create a discrete function for the solution + DiscreteFunctionType u(fv_space, "solution"); + DiscreteFunctionType alpha(fv_space, "solution"); + // project initial values + default_interpolation(*initial_values_u, u, grid_view); + + // convert initial values to alpha + const auto u_local_func = u.local_discrete_function(); + const auto alpha_local_func = alpha.local_discrete_function(); + XT::Common::FieldVector<RangeFieldType, dimRange> u_local; + for (auto&& element : Dune::elements(grid_view)) { + u_local_func->bind(element); + alpha_local_func->bind(element); + for (size_t ii = 0; ii < dimRange; ++ii) + u_local[ii] = u_local_func->dofs().get_entry(ii); + const auto alpha_local = analytical_flux->get_alpha(u_local); + for (auto&& entry : alpha_local) + if (entry > 1) { + std::cout << XT::Common::to_string(element.geometry().center()) << ", " << XT::Common::to_string(u_local) + << ", " << XT::Common::to_string(alpha_local) << std::endl; + break; + } + for (size_t ii = 0; ii < dimRange; ++ii) + alpha_local_func->dofs().set_entry(ii, alpha_local[ii]); + } + + // ******************** choose flux and rhs operator and timestepper ****************************************** + + using AdvectionOperatorType = AdvectionFvOperator<MatrixType, GV, dimRange>; + using HessianInverterType = EntropicHessianInverter<MomentBasis, SpaceType>; +#if 0 + using ReconstructionOperatorType = PointwiseLinearReconstructionNoCharOperator< + GV, + BoundaryValueType, + EntropyFluxType, + VectorType, + RangeType>; +#else + using ReconstructionOperatorType = + PointwiseLinearKineticReconstructionOperator<GV, BoundaryValueType, EntropyFluxType, VectorType, RangeType>; +#endif + using ReconstructionAdvectionOperatorType = + AdvectionWithPointwiseReconstructionOperator<AdvectionOperatorType, ReconstructionOperatorType>; + using NonEntropicAdvectionOperatorType = + std::conditional_t<TestCaseType::reconstruction, ReconstructionAdvectionOperatorType, AdvectionOperatorType>; + // using FvOperatorType = EntropicCoordinatesOperator< + // NonEntropicAdvectionOperatorType, + // HessianInverterType>; + using NonEntropicRhsOperatorType = GenericOperator<MatrixType, GV, dimRange>; + // using RhsOperatorType = EntropicCoordinatesOperator<NonEntropicRhsOperatorType, HessianInverterType>; + using CombinedOperatorType = EntropicCoordinatesCombinedOperator<NonEntropicAdvectionOperatorType, + NonEntropicRhsOperatorType, + HessianInverterType>; + + // using OperatorTimeStepperType = + // ExplicitRungeKuttaTimeStepper<FvOperatorType, + // DiscreteFunctionType, + // TimeStepperMethods::explicit_euler>; + // using RhsTimeStepperType = + // ExplicitRungeKuttaTimeStepper<RhsOperatorType, + // DiscreteFunctionType, + // TimeStepperMethods::explicit_euler>; + + // using OperatorTimeStepperType = + // AdaptiveRungeKuttaTimeStepper<FvOperatorType, + // DiscreteFunctionType>; + // using RhsTimeStepperType = + // AdaptiveRungeKuttaTimeStepper<RhsOperatorType, + // DiscreteFunctionType>; + + // using TimeStepperType = FractionalTimeStepper<OperatorTimeStepperType, RhsTimeStepperType>; + + using TimeStepperType = + AdaptiveRungeKuttaTimeStepper<CombinedOperatorType, DiscreteFunctionType, TimeStepperMethods::dormand_prince>; + + // *************** Calculate dx and initial dt ************************************** + Dune::XT::Grid::Dimensions<GV> dimensions(grid_view); + RangeFieldType dx = dimensions.entity_width.max(); + if (dimDomain == 2) + dx /= std::sqrt(2); + if (dimDomain == 3) + dx /= std::sqrt(3); + RangeFieldType dt = CFL * dx; + + // *********************** create operators and timesteppers ************************************ + NumericalKineticFlux<GV, MomentBasis, EntropyFluxType> numerical_flux(*analytical_flux, *basis_functions); + AdvectionOperatorType advection_operator(grid_view, numerical_flux, advection_source_space, fv_space); + + // boundary treatment + using BoundaryOperator = + LocalAdvectionFvBoundaryTreatmentByCustomExtrapolationOperator<I, VectorType, GV, dimRange>; + using LambdaType = typename BoundaryOperator::LambdaType; + using StateType = typename BoundaryOperator::StateType; + + LambdaType boundary_lambda = + [&](const I& intersection, + const FieldVector<RangeFieldType, dimDomain - 1>& xx_in_reference_intersection_coordinates, + const AnalyticalFluxType& /*flux*/, + const StateType& /*u*/, + const XT::Common::Parameter& /*param*/) { + // return + // boundary_values_alpha.evaluate(intersection.geometry().global(xx_in_reference_intersection_coordinates)); + return boundary_kinetic_fluxes.evaluate( + intersection.geometry().global(xx_in_reference_intersection_coordinates)); + }; + XT::Grid::ApplyOn::NonPeriodicBoundaryIntersections<GV> filter; + advection_operator.append(boundary_lambda, {}, filter); + + // constexpr double epsilon = 1e-11; + // MinmodSlope<E, DummyEigenVectorWrapper<RangeType>> slope; + // ReconstructionOperatorType reconstruction_operator(boundary_values_alpha, fv_space, *analytical_flux, slope); + ReconstructionOperatorType reconstruction_operator(boundary_values_alpha, fv_space, *analytical_flux); + ReconstructionAdvectionOperatorType reconstruction_advection_operator(advection_operator, reconstruction_operator); + + if (XT::Common::is_zero(t_end)) + t_end = problem.t_end(); + + if (!filename.empty()) + filename += "_minmod_"; + filename += ProblemType::static_id(); + filename += "_grid_" + grid_config["num_elements"]; + filename += "_tend_" + XT::Common::to_string(t_end); + filename += "_quad_" + XT::Common::to_string(quad_order); + filename += TestCaseType::reconstruction ? "_ord2" : "_ord1"; + filename += "_" + basis_functions->mn_name(); + + HessianInverterType hessian_inverter( + *analytical_flux, fv_space, problem.psi_vac() * basis_functions->unit_ball_volume() / 10, filename); + auto& non_entropic_advection_operator = + FvOperatorChooser<TestCaseType::reconstruction>::choose(advection_operator, reconstruction_advection_operator); + // FvOperatorType fv_operator(non_entropic_advection_operator, hessian_inverter); + + + const auto u_iso = basis_functions->u_iso(); + const auto basis_integrated = basis_functions->integrated(); + const auto sigma_a = problem.sigma_a(); + const auto sigma_s = problem.sigma_s(); + const auto Q = problem.Q(); + auto rhs_func = [&](const auto& /*source*/, + const auto& local_source, + auto& local_range, + const Dune::XT::Common::Parameter& /*param*/) { + const auto& element = local_range.element(); + local_source->bind(element); + const auto center = element.geometry().center(); + const auto alpha = local_source->evaluate(center); + const auto u = analytical_flux->get_u(alpha); + const auto sigma_a_value = sigma_a->evaluate(center)[0]; + const auto sigma_s_value = sigma_s->evaluate(center)[0]; + const auto sigma_t_value = sigma_a_value + sigma_s_value; + const auto Q_value = Q->evaluate(center)[0]; + auto ret = u * (-sigma_t_value); + ret += u_iso * basis_functions->density(u) * sigma_s_value; + ret += basis_integrated * Q_value; + for (size_t ii = 0; ii < local_range.dofs().size(); ++ii) + local_range.dofs()[ii] += ret[ii]; + }; + NonEntropicRhsOperatorType non_entropic_rhs_operator( + fv_space, fv_space, std::vector<typename NonEntropicRhsOperatorType::GenericElementFunctionType>(1, rhs_func)); + // RhsOperatorType rhs_operator(non_entropic_rhs_operator, hessian_inverter); + CombinedOperatorType combined_operator( + non_entropic_advection_operator, non_entropic_rhs_operator, hessian_inverter); + + // ******************************** do the time steps *********************************************************** + // OperatorTimeStepperType timestepper_op(fv_operator, alpha, -1.0); + // RhsTimeStepperType timestepper_rhs(rhs_operator, alpha, 1.0); + // TimeStepperType timestepper(timestepper_op, timestepper_rhs); + TimeStepperType timestepper(combined_operator, alpha, 1.); + + auto begin_time = std::chrono::steady_clock::now(); + auto visualizer = std::make_unique<XT::Functions::GenericVisualizer<dimRange, 1, double>>( + 1, [&basis_functions, &analytical_flux](const int /*comp*/, const auto& val) { + return basis_functions->density(analytical_flux->get_u(val)); + }); + timestepper.solve(t_end, + dt, + num_save_steps, + num_output_steps, + false, + true, + false, + true, + false, + filename, + // *basis_functions->visualizer(), + *visualizer, + basis_functions->stringifier()); + auto end_time = std::chrono::steady_clock::now(); + std::chrono::duration<double> time_diff = end_time - begin_time; + if (grid_view.comm().rank() == 0) + std::cout << "Solving took: " << XT::Common::to_string(time_diff.count(), 15) << " s" << std::endl; + + auto ret = std::make_pair(FieldVector<double, 3>(0.), int(0)); + double& l1norm = ret.first[0]; + double& l2norm = ret.first[1]; + double& linfnorm = ret.first[2]; + ret.second = grid_view.comm().rank(); + const auto& current_sol = timestepper.current_solution(); + const auto local_sol = current_sol.local_function(); + for (const auto& entity : elements(grid_view, Dune::Partitions::interior)) { + local_sol->bind(entity); + const auto val = local_sol->evaluate(entity.geometry().local(entity.geometry().center())); + RangeFieldType psi = basis_functions->density(val); + l1norm += std::abs(psi) * entity.geometry().volume(); + l2norm += std::pow(psi, 2) * entity.geometry().volume(); + linfnorm = std::max(std::abs(psi), linfnorm); + } + l1norm = grid_view.comm().sum(l1norm); + l2norm = grid_view.comm().sum(l2norm); + linfnorm = grid_view.comm().max(linfnorm); + l2norm = std::sqrt(l2norm); + return ret; + } +}; + +template <class TestCaseType> +struct HyperbolicEntropicCoordsMnTest + : public HyperbolicEntropicCoordsMnDiscretization<TestCaseType> + , public ::testing::Test +{ + void run() + { + auto norms = HyperbolicEntropicCoordsMnDiscretization<TestCaseType>::run( + 10, + -1, + TestCaseType::RealizabilityLimiterChooserType::quad_order, + TestCaseType::RealizabilityLimiterChooserType::quad_refinements, + "", + 2, + TestCaseType::t_end, + "test_dormandprince", + Dune::GDT::is_full_moment_basis<typename TestCaseType::MomentBasis>::value) + .first; + const double l1norm = norms[0]; + const double l2norm = norms[1]; + const double linfnorm = norms[2]; + using ResultsType = typename TestCaseType::ExpectedResultsType; + EXPECT_NEAR(ResultsType::l1norm, l1norm, ResultsType::l1norm * ResultsType::tol); + EXPECT_NEAR(ResultsType::l2norm, l2norm, ResultsType::l2norm * ResultsType::tol); + EXPECT_NEAR(ResultsType::linfnorm, linfnorm, ResultsType::linfnorm * ResultsType::tol); + } +}; + +#endif // DUNE_GDT_TEST_HYPERBOLIC_ENTROPIC_COORDS_MN_DISCRETIZATION_HH diff --git a/dune/gdt/test/hyperbolic__momentmodels__entropic_coords_mn.cc b/dune/gdt/test/hyperbolic__momentmodels__entropic_coords_mn.cc new file mode 100644 index 0000000000000000000000000000000000000000..c805ad1656256b13e21cc6d87a8ccc5a2beb7b7a --- /dev/null +++ b/dune/gdt/test/hyperbolic__momentmodels__entropic_coords_mn.cc @@ -0,0 +1,63 @@ +// This file is part of the dune-gdt project: +// https://github.com/dune-community/dune-gdt +// Copyright 2010-2016 dune-gdt developers and contributors. All rights reserved. +// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause) +// Authors: +// Tobias Leibner (2016) + +// This one has to come first (includes the config.h)! +#include <dune/xt/common/test/main.hxx> + +#define USE_LP_POSITIVITY_LIMITER 1 + +#include <dune/gdt/test/momentmodels/kinetictransport/testcases.hh> +#include <dune/gdt/test/entropic-coords-mn-discretization.hh> + +using Yasp1 = Dune::YaspGrid<1, Dune::EquidistantOffsetCoordinates<double, 1>>; +using Yasp2 = Dune::YaspGrid<2, Dune::EquidistantOffsetCoordinates<double, 2>>; +using Yasp3 = Dune::YaspGrid<3, Dune::EquidistantOffsetCoordinates<double, 3>>; + +using YaspGridTestCasesAll = testing::Types< +#if HAVE_CLP +// Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::LegendreMomentBasis<double, double, 7>, false>, +// Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::LegendreMomentBasis<double, double, 20>, true> +// Dune::GDT::PlaneSourceMnTestCase<Yasp1, Dune::GDT::LegendreMomentBasis<double, double, 20>, true> +// Dune::GDT::PlaneSourceMnTestCase<Yasp1, Dune::GDT::LegendreMomentBasis<double, double, 21>, true> +#endif + // Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::HatFunctionMomentBasis<double, 1, double, 8, 1, 1>, false> + Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::HatFunctionMomentBasis<double, 1, double, 100, 1, 1>, true> +// Dune::GDT::PlaneSourceMnTestCase<Yasp1, Dune::GDT::HatFunctionMomentBasis<double, 1, double, 20, 1, 1>, false>, +// Dune::GDT::PlaneSourceMnTestCase<Yasp1, Dune::GDT::HatFunctionMomentBasis<double, 1, double, 100, 1, 1>, true> +// Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::PartialMomentBasis<double, 1, double, 8, 1, 1>, false> +// Dune::GDT::SourceBeamMnTestCase<Yasp1, Dune::GDT::PartialMomentBasis<double, 1, double, 8, 1, 1>, true>, +// Dune::GDT::PlaneSourceMnTestCase<Yasp1, Dune::GDT::PartialMomentBasis<double, 1, double, 8, 1, 1>, false> +// Dune::GDT::PlaneSourceMnTestCase<Yasp1, Dune::GDT::PartialMomentBasis<double, 1, double, 8, 1, 1>, true> +#if !DXT_DISABLE_LARGE_TESTS +// , +# if HAVE_CLP +// Dune::GDT::PointSourceMnTestCase<Yasp3, Dune::GDT::RealSphericalHarmonicsMomentBasis<double, double, 2, 3>, +// false>, Dune::GDT::PointSourceMnTestCase<Yasp3, Dune::GDT::RealSphericalHarmonicsMomentBasis<double, double, 2, +// 3>, true> Dune::GDT::CheckerboardMnTestCase<Yasp3, Dune::GDT::HatFunctionMomentBasis<double, 3, double, 0, 1, 3>, +// true> Dune::GDT::CheckerboardMnTestCase<Yasp3, Dune::GDT::RealSphericalHarmonicsMomentBasis<double, double, 2, 3>, +// false>, Dune::GDT::ShadowMnTestCase<Yasp3, Dune::GDT::RealSphericalHarmonicsMomentBasis<double, double, 2, 3>, +// false>, Dune::GDT::ShadowMnTestCase<Yasp3, Dune::GDT::HatFunctionMomentBasis<double, 3, double, 1, 1, 3>, true> +# endif +// Dune::GDT::PointSourceMnTestCase<Yasp3, Dune::GDT::HatFunctionMomentBasis<double, 3, double, 0, 1, 3>, false> +// Our shifted qr eigensolver fails for this problem, needs better shifting strategy +# if HAVE_MKL || HAVE_LAPACKE || HAVE_EIGEN +// , +// Dune::GDT::PointSourceMnTestCase<Yasp3, Dune::GDT::HatFunctionMomentBasis<double, 3, double, 0, 1, 3>, true> +# endif +# if HAVE_QHULL +// , +// Dune::GDT::PointSourceMnTestCase<Yasp3, Dune::GDT::PartialMomentBasis<double, 3, double, 0, 1, 3>, false>, +// Dune::GDT::PointSourceMnTestCase<Yasp3, Dune::GDT::PartialMomentBasis<double, 3, double, 0, 1, 3>, true> +# endif +#endif + >; + +TYPED_TEST_CASE(HyperbolicEntropicCoordsMnTest, YaspGridTestCasesAll); +TYPED_TEST(HyperbolicEntropicCoordsMnTest, check) +{ + this->run(); +} diff --git a/dune/gdt/tools/timestepper/adaptive-rungekutta.hh b/dune/gdt/tools/timestepper/adaptive-rungekutta.hh new file mode 100644 index 0000000000000000000000000000000000000000..4e46bf4b1cd26eec5aee0a22ce1fb1bfec2d0628 --- /dev/null +++ b/dune/gdt/tools/timestepper/adaptive-rungekutta.hh @@ -0,0 +1,410 @@ +// 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 (2016 - 2017) +// Rene Milk (2016 - 2018) +// Tobias Leibner (2016 - 2017) + +#ifndef DUNE_GDT_TIMESTEPPER_ADAPTIVE_RUNGEKUTTA_HH +#define DUNE_GDT_TIMESTEPPER_ADAPTIVE_RUNGEKUTTA_HH + +#include <utility> + +#include <dune/gdt/operators/interfaces.hh> + +#include <dune/xt/common/memory.hh> +#include <dune/xt/common/string.hh> + +#include "interface.hh" + + +namespace Dune { +namespace GDT { + + +namespace internal { + + +// unspecialized +template <class RangeFieldType, TimeStepperMethods method> +struct AdaptiveButcherArrayProvider +{ + static_assert(AlwaysFalse<RangeFieldType>::value, + "You cannot use AdaptiveRungeKuttaTimeStepper with this value of TimeStepperMethods!"); +}; + +// user-provided Butcher array +template <class RangeFieldType> +struct AdaptiveButcherArrayProvider<RangeFieldType, TimeStepperMethods::adaptive_rungekutta_other> +{ + static Dune::DynamicMatrix<RangeFieldType> A() + { + DUNE_THROW(Dune::NotImplemented, + "You have to provide a Butcher array in AdaptiveRungeKuttaTimeStepper's constructor for this method!"); + return Dune::DynamicMatrix<RangeFieldType>(); + } + + static Dune::DynamicVector<RangeFieldType> b_1() + { + DUNE_THROW(Dune::NotImplemented, + "You have to provide a Butcher array in AdaptiveRungeKuttaTimeStepper's constructor for this method!"); + return Dune::DynamicVector<RangeFieldType>(); + } + + static Dune::DynamicVector<RangeFieldType> b_2() + { + DUNE_THROW(Dune::NotImplemented, + "You have to provide a Butcher array in AdaptiveRungeKuttaTimeStepper's constructor for this method!"); + return Dune::DynamicVector<RangeFieldType>(); + } + + static Dune::DynamicVector<RangeFieldType> c() + { + DUNE_THROW(Dune::NotImplemented, + "You have to provide a Butcher array in AdaptiveRungeKuttaTimeStepper's constructor for this method!"); + return Dune::DynamicVector<RangeFieldType>(); + } +}; + +// Bogacki-Shampine (adaptive RK23) +template <class RangeFieldType> +class AdaptiveButcherArrayProvider<RangeFieldType, TimeStepperMethods::bogacki_shampine> +{ +public: + static Dune::DynamicMatrix<RangeFieldType> A() + { + return Dune::XT::Common::from_string<Dune::DynamicMatrix<RangeFieldType>>( + "[0 0 0 0; 0.5 0 0 0; 0 0.75 0 0; " + Dune::XT::Common::to_string(2.0 / 9.0, 15) + " " + + Dune::XT::Common::to_string(1.0 / 3.0, 15) + " " + Dune::XT::Common::to_string(4.0 / 9.0, 15) + " 0]"); + } + + static Dune::DynamicVector<RangeFieldType> b_1() + { + return Dune::XT::Common::from_string<Dune::DynamicVector<RangeFieldType>>( + "[" + Dune::XT::Common::to_string(2.0 / 9.0, 15) + " " + Dune::XT::Common::to_string(1.0 / 3.0, 15) + " " + + Dune::XT::Common::to_string(4.0 / 9.0, 15) + " 0]"); + } + + static Dune::DynamicVector<RangeFieldType> b_2() + { + return Dune::XT::Common::from_string<Dune::DynamicVector<RangeFieldType>>( + "[" + Dune::XT::Common::to_string(7.0 / 24.0, 15) + " " + Dune::XT::Common::to_string(1.0 / 4.0, 15) + " " + + Dune::XT::Common::to_string(1.0 / 3.0, 15) + " " + Dune::XT::Common::to_string(1.0 / 8.0, 15) + " 0]"); + } + + static Dune::DynamicVector<RangeFieldType> c() + { + return Dune::XT::Common::from_string<Dune::DynamicVector<RangeFieldType>>("[0.5 0.75 1 0]"); + } +}; + +// Dormand-Prince (adaptive RK45) +template <class RangeFieldType> +class AdaptiveButcherArrayProvider<RangeFieldType, TimeStepperMethods::dormand_prince> +{ +public: + static Dune::DynamicMatrix<RangeFieldType> A() + { + return Dune::XT::Common::from_string<Dune::DynamicMatrix<RangeFieldType>>( + std::string("[0 0 0 0 0 0 0;") + " 0.2 0 0 0 0 0 0;" + " 0.075 0.225 0 0 0 0 0;" + " " + + Dune::XT::Common::to_string(44.0 / 45.0, 15) + " " + Dune::XT::Common::to_string(-56.0 / 15.0, 15) + " " + + Dune::XT::Common::to_string(32.0 / 9.0, 15) + " 0 0 0 0;" + " " + + Dune::XT::Common::to_string(19372.0 / 6561.0, 15) + " " + Dune::XT::Common::to_string(-25360.0 / 2187.0, 15) + + " " + Dune::XT::Common::to_string(64448.0 / 6561.0, 15) + " " + + Dune::XT::Common::to_string(-212.0 / 729.0, 15) + " 0 0 0;" + " " + + Dune::XT::Common::to_string(9017.0 / 3168.0, 15) + " " + Dune::XT::Common::to_string(-355.0 / 33.0, 15) + " " + + Dune::XT::Common::to_string(46732.0 / 5247.0, 15) + " " + Dune::XT::Common::to_string(49.0 / 176.0, 15) + " " + + Dune::XT::Common::to_string(-5103.0 / 18656.0, 15) + " 0 0;" + " " + + Dune::XT::Common::to_string(35.0 / 384.0, 15) + " 0 " + Dune::XT::Common::to_string(500.0 / 1113.0, 15) + " " + + Dune::XT::Common::to_string(125.0 / 192.0, 15) + " " + Dune::XT::Common::to_string(-2187.0 / 6784.0, 15) + " " + + Dune::XT::Common::to_string(11.0 / 84.0, 15) + " 0]"); + } + + static Dune::DynamicVector<RangeFieldType> b_1() + { + return Dune::XT::Common::from_string<Dune::DynamicVector<RangeFieldType>>( + "[" + Dune::XT::Common::to_string(35.0 / 384.0, 15) + " 0 " + Dune::XT::Common::to_string(500.0 / 1113.0, 15) + + " " + Dune::XT::Common::to_string(125.0 / 192.0, 15) + " " + Dune::XT::Common::to_string(-2187.0 / 6784.0, 15) + + " " + Dune::XT::Common::to_string(11.0 / 84.0, 15) + " 0]"); + } + + static Dune::DynamicVector<RangeFieldType> b_2() + { + return Dune::XT::Common::from_string<Dune::DynamicVector<RangeFieldType>>( + "[" + Dune::XT::Common::to_string(5179.0 / 57600.0, 15) + " 0 " + + Dune::XT::Common::to_string(7571.0 / 16695.0, 15) + " " + Dune::XT::Common::to_string(393.0 / 640.0, 15) + " " + + Dune::XT::Common::to_string(-92097.0 / 339200.0, 15) + " " + Dune::XT::Common::to_string(187.0 / 2100.0, 15) + + " " + Dune::XT::Common::to_string(1.0 / 40.0, 15) + "]"); + } + + static Dune::DynamicVector<RangeFieldType> c() + { + return Dune::XT::Common::from_string<Dune::DynamicVector<RangeFieldType>>( + "[0 0.2 0.3 0.8 " + Dune::XT::Common::to_string(8.0 / 9.0, 15) + " 1 1]"); + } +}; // Dormand-Prince (RK45) + + +} // namespace internal + + +/** \brief Time stepper using adaptive Runge Kutta methods + * + * Timestepper using adaptive Runge Kutta methods to solve equations of the form u_t = r * L(u, t) where u is a + * discrete function, L an operator acting on u and \alpha a scalar factor (e.g. -1). + * The specific Runge Kutta method can be chosen as the third template argument. If your desired Runge Kutta method is + * not contained in AdaptiveRungeKuttaMethods, choose AdaptiveRungeKuttaMethods::other and + * supply a DynamicMatrix< RangeFieldType > A and vectors b_1, b_2 (DynamicVector< RangeFieldType >) and c + * (DynamicVector< RangeFieldType >) in the constructor. Here, A, b_1, b_2 and c form the butcher tableau (see + * https://en.wikipedia.org/wiki/List_of_Runge%E2%80%93Kutta_methods#Embedded_methods, A is composed of the coefficients + * a_{ij}, b_1 of b_j, b_2 of b_j^* and c of c_j). The default is the Dormand-Prince RK45 method. + * In each time step, the error is estimated using the difference between the two solutions obtained using either b_1 or + * b_2. If the estimated error is higher than a specified tolerance tol, the calculation is repeated with a smaller + * time step. The tolerance tol and the error estimate are also used to estimate the optimal time step length for the + * next time step via dt_new = dt_old*min(max(0.9*(tol/error)^(1/5), scale_factor_min), scale_factor_max_); + * + * Notation: For an s-stage method, + * \mathbf{u}^{n+1} = \mathbf{u}^n + dt \sum_{i=0}^{s-1} b_i \mathbf{k}_i + * \mathbf{k}_i = L(\mathbf{u}_i, t^n + dt c_i) + * \mathbf{u}_i = \mathbf{u}^n + dt \sum_{j=0}^{i-1} a_{ij} \mathbf{k}_j, + * + * \tparam OperatorImp Type of operator L + * \tparam DiscreteFunctionImp Type of initial values and solution at a fixed time + * \tparam method Adaptive Runge-Kutta method that is used (default is AdaptiveRungeKuttaMethods::dormand_prince) + */ +template <class OperatorImp, class DiscreteFunctionImp, TimeStepperMethods method = TimeStepperMethods::dormand_prince> +class AdaptiveRungeKuttaTimeStepper : public TimeStepperInterface<DiscreteFunctionImp> +{ + typedef TimeStepperInterface<DiscreteFunctionImp> BaseType; + typedef typename internal::AdaptiveButcherArrayProvider<typename BaseType::RangeFieldType, method> + ButcherArrayProviderType; + +public: + typedef OperatorImp OperatorType; + typedef DiscreteFunctionImp DiscreteFunctionType; + + typedef typename DiscreteFunctionType::DomainFieldType DomainFieldType; + typedef typename DiscreteFunctionType::RangeFieldType RangeFieldType; + typedef typename Dune::DynamicMatrix<RangeFieldType> MatrixType; + typedef typename Dune::DynamicVector<RangeFieldType> VectorType; + typedef typename std::vector<std::pair<RangeFieldType, DiscreteFunctionType>> SolutionType; + + /** + * \brief Constructor for AdaptiveRungeKuttaTimeStepper time stepper + * \param op Operator L + * \param initial_values Discrete function containing initial values for u at time t_0. + * \param r Scalar factor (see above, default is 1) + * \param t_0 Initial time (default is 0) + * \param tol Error tolerance for the adaptive scheme (default is 1e-4) + * \param scale_factor_min Minimum allowed factor for time step scaling (default is 0.2). + * \param scale_factor_max Maximum allowed factor for time step scaling (default is 5). + * \param A Coefficient matrix (only provide if you use AdaptiveRungeKuttaMethods::other) + * \param b_1 First set of coefficients (only provide if you use AdaptiveRungeKuttaMethods::other) + * \param b_2 Second set of coefficients (only provide if you use AdaptiveRungeKuttaMethods::other) + * \param c Coefficients for time steps (only provide if you use AdaptiveRungeKuttaMethods::other) + */ + AdaptiveRungeKuttaTimeStepper(const OperatorType& op, + DiscreteFunctionType& initial_values, + const RangeFieldType r = 1.0, + const double t_0 = 0.0, + const RangeFieldType tol = 1e-4, + const RangeFieldType scale_factor_min = 0.2, + const RangeFieldType scale_factor_max = 5, + const MatrixType& A = ButcherArrayProviderType::A(), + const VectorType& b_1 = ButcherArrayProviderType::b_1(), + const VectorType& b_2 = ButcherArrayProviderType::b_2(), + const VectorType& c = ButcherArrayProviderType::c()) + : BaseType(t_0, initial_values) + , op_(op) + , r_(r) + , tol_(tol) + , scale_factor_min_(scale_factor_min) + , scale_factor_max_(scale_factor_max) + , u_tmp_(BaseType::current_solution()) + , A_(A) + , b_1_(b_1) + , b_2_(b_2) + , c_(c) + , b_diff_(b_2_ - b_1_) + , num_stages_(A_.rows()) + { + assert(Dune::XT::Common::FloatCmp::gt(tol_, 0.0)); + assert(Dune::XT::Common::FloatCmp::le(scale_factor_min_, 1.0)); + assert(Dune::XT::Common::FloatCmp::ge(scale_factor_max_, 1.0)); + assert(A_.rows() == A_.cols() && "A has to be a square matrix"); + assert(b_1_.size() == A_.rows()); + assert(b_2_.size() == A_.rows()); + assert(c_.size() == A_.rows()); +#ifndef NDEBUG + for (size_t ii = 0; ii < A_.rows(); ++ii) { + for (size_t jj = ii; jj < A_.cols(); ++jj) { + assert(Dune::XT::Common::FloatCmp::eq(A_[ii][jj], 0.0) + && "A has to be a lower triangular matrix with 0 on the main diagonal"); + } + } +#endif // NDEBUG + // store as many discrete functions as needed for intermediate stages + for (size_t ii = 0; ii < num_stages_; ++ii) { + stages_k_.emplace_back(current_solution()); + } + } // constructor AdaptiveRungeKuttaTimeStepper + + using BaseType::current_solution; + using BaseType::current_time; + using BaseType::solve; + + virtual RangeFieldType solve(const RangeFieldType t_end, + const RangeFieldType initial_dt, + const size_t num_save_steps, + const size_t num_output_steps, + const bool save_solution, + const bool visualize, + const bool with_half_steps, + const bool write_discrete, + const bool write_exact, + const std::string prefix, + typename BaseType::DiscreteSolutionType& sol, + const typename BaseType::VisualizerType& visualizer, + const typename BaseType::StringifierType& stringifier, + const typename BaseType::GridFunctionType& exact_solution) override final + { + const auto ret = BaseType::solve(t_end, + initial_dt, + num_save_steps, + num_output_steps, + save_solution, + visualize, + with_half_steps, + write_discrete, + write_exact, + prefix, + sol, + visualizer, + stringifier, + exact_solution); + // in a fractional step scheme, we cannot use last_stage_of_previous_step + last_stage_of_previous_step_ = nullptr; + return ret; + } + + RangeFieldType step(const RangeFieldType dt, const RangeFieldType max_dt) override final + { + RangeFieldType actual_dt = std::min(dt, max_dt); + RangeFieldType mixed_error = std::numeric_limits<RangeFieldType>::max(); + RangeFieldType time_step_scale_factor = 1.0; + + auto& t = current_time(); + auto& u_n = current_solution(); + + while (Dune::XT::Common::FloatCmp::gt(mixed_error, tol_)) { + bool skip_error_computation = false; + actual_dt *= time_step_scale_factor; + size_t first_stage_to_compute = 0; + if (last_stage_of_previous_step_) { + stages_k_[0].dofs().vector() = last_stage_of_previous_step_->dofs().vector(); + first_stage_to_compute = 1; + } + + for (size_t ii = first_stage_to_compute; ii < num_stages_; ++ii) { + std::fill(stages_k_[ii].dofs().vector().begin(), stages_k_[ii].dofs().vector().end(), RangeFieldType(0.)); + u_tmp_.dofs().vector() = u_n.dofs().vector(); + for (size_t jj = 0; jj < ii; ++jj) + u_tmp_.dofs().vector() += stages_k_[jj].dofs().vector() * (actual_dt * r_ * (A_[ii][jj])); + try { + op_.apply(u_tmp_.dofs().vector(), stages_k_[ii].dofs().vector(), t + actual_dt * c_[ii]); + } catch (const Dune::MathError& e) { + std::cout << "Caught error " << e.what() << std::endl; + mixed_error = 1e10; + skip_error_computation = true; + time_step_scale_factor = 0.5; + break; + } + } + + if (!skip_error_computation) { + // compute error vector + u_tmp_.dofs().vector() = stages_k_[0].dofs().vector() * b_diff_[0]; + for (size_t ii = 1; ii < num_stages_; ++ii) + u_tmp_.dofs().vector() += stages_k_[ii].dofs().vector() * b_diff_[ii]; + u_tmp_.dofs().vector() *= actual_dt * r_; + + // calculate u at timestep n+1 + for (size_t ii = 0; ii < num_stages_; ++ii) + u_n.dofs().vector() += stages_k_[ii].dofs().vector() * (actual_dt * r_ * b_1_[ii]); + + // scale error, use absolute error if norm is less than 0.01 and relative error else + auto& diff_vector = u_tmp_.dofs().vector(); + for (size_t ii = 0; ii < diff_vector.size(); ++ii) { + if (std::abs(u_n.dofs().vector()[ii]) > 0.01) + diff_vector[ii] /= std::abs(u_n.dofs().vector()[ii]); + } + mixed_error = diff_vector.sup_norm(); + // scale dt to get the estimated optimal time step length, TODO: adapt formula + time_step_scale_factor = + std::min(std::max(0.9 * std::pow(tol_ / mixed_error, 1.0 / 5.0), scale_factor_min_), scale_factor_max_); + + if (mixed_error > tol_) { // go back from u at timestep n+1 to timestep n + for (size_t ii = 0; ii < num_stages_; ++ii) + u_n.dofs().vector() += stages_k_[ii].dofs().vector() * (-1.0 * r_ * actual_dt * b_1_[ii]); + } + } + } // while (mixed_error > tol_) + if (!last_stage_of_previous_step_) + last_stage_of_previous_step_ = Dune::XT::Common::make_unique<DiscreteFunctionType>(u_n); + last_stage_of_previous_step_->dofs().vector() = stages_k_[num_stages_ - 1].dofs().vector(); + +#if 0 + const auto u_local_func = u_n.local_discrete_function(); + for (auto&& element : Dune::elements(u_n.space().grid_view())) { + u_local_func->bind(element); + for (size_t ii = 0; ii < BaseType::dimRange; ++ii) { +// constexpr double min_val = -100.; + constexpr double max_val = 1000.; + const auto entry_ii = u_local_func->dofs().get_entry(ii); + if (std::abs(entry_ii) > max_val) { +// std::cout << "limited from " << u_local_func->dofs().get_entry(ii) << " to " << min_val << " in entity " << element.geometry().center() << std::endl; +// std::cout << "Entries are: "; +// for (size_t jj = 0; jj < BaseType::dimRange; ++jj) { +// std::cout << u_local_func->dofs().get_entry(jj) << " "; +// } +// std::cout << std::endl; + last_stage_of_previous_step_ = nullptr; +// u_local_func->dofs().set_entry(ii, min_val); + std::cout << "Replacing " << entry_ii << " by " << std::copysign(max_val, entry_ii) << std::endl; + u_local_func->dofs().set_entry(ii, std::copysign(max_val, entry_ii)); + } + } // ii + } // elements +#endif + + t += actual_dt; + + return actual_dt * time_step_scale_factor; + } // ... step(...) + +private: + const OperatorType& op_; + const RangeFieldType r_; + const RangeFieldType tol_; + const RangeFieldType scale_factor_min_; + const RangeFieldType scale_factor_max_; + DiscreteFunctionType u_tmp_; + const MatrixType A_; + const VectorType b_1_; + const VectorType b_2_; + const VectorType c_; + const VectorType b_diff_; + std::vector<DiscreteFunctionType> stages_k_; + const size_t num_stages_; + std::unique_ptr<DiscreteFunctionType> last_stage_of_previous_step_; +}; // class AdaptiveRungeKuttaTimeStepper + + +} // namespace GDT +} // namespace Dune + +#endif // DUNE_GDT_TIMESTEPPER_ADAPTIVE_RUNGEKUTTA_HH