diff --git a/dune/gdt/local/operators/advection-fv.hh b/dune/gdt/local/operators/advection-fv.hh index fdebe9549ea15be3303b34725704aa7d06fb21fb..36f59a3a15b4056165ff7416d753b8034115fef2 100644 --- a/dune/gdt/local/operators/advection-fv.hh +++ b/dune/gdt/local/operators/advection-fv.hh @@ -201,10 +201,12 @@ public: using typename BaseType::SourceSpaceType; using typename BaseType::SourceType; + using D = typename IntersectionType::ctype; using StateDomainType = FieldVector<typename SGV::ctype, SGV::dimension>; using DynamicStateType = typename CouplingOperatorType::DynamicStateType; - using LambdaType = std::function<void(const DynamicStateType& /*u*/, - const StateDomainType& /*n*/, + using LambdaType = std::function<void(const IntersectionType& /*intersection*/, + const FieldVector<D, d - 1>& /*xx_in_reference_intersection_coordinates*/, + const DynamicStateType& /*u*/, DynamicStateType& /*g*/, const XT::Common::Parameter& /*param*/)>; @@ -286,8 +288,8 @@ public: local_sources_[0]->evaluate(source_is_elementwise_constant_ ? CouplingOperatorType::static_x : intersection().geometryInInside().center(), u_); - const auto normal = intersection().centerUnitOuterNormal(); - numerical_boundary_flux_(u_, normal, g_, param); + numerical_boundary_flux_( + intersection(), intersection().geometry().local(intersection().geometry().center()), u_, g_, param); auto& local_range_inside_dofs = local_range_inside.dofs(); const auto factor = intersection().geometry().volume() / intersection().inside().geometry().volume(); for (size_t ii = 0; ii < m; ++ii) diff --git a/dune/gdt/test/momentmodels/entropic-coords-mn-discretization.hh b/dune/gdt/test/momentmodels/entropic-coords-mn-discretization.hh index 4b03770ebaea7ad52e4b10762f46e5f520022b4f..9a76b1beab85844055f0d1411c077ba081ae3fe8 100644 --- a/dune/gdt/test/momentmodels/entropic-coords-mn-discretization.hh +++ b/dune/gdt/test/momentmodels/entropic-coords-mn-discretization.hh @@ -137,13 +137,15 @@ struct HyperbolicEntropicCoordsMnDiscretization const auto u_local_func = u.local_discrete_function(); const auto alpha_local_func = alpha.local_discrete_function(); + const auto entropy_local_func = entropy_flux->derived_local_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); + entropy_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); + const auto alpha_local = entropy_local_func->get_alpha(u_local, false)->first; for (size_t ii = 0; ii < dimRange; ++ii) alpha_local_func->dofs().set_entry(ii, alpha_local[ii]); } @@ -309,7 +311,7 @@ struct HyperbolicEntropicCoordsMnDiscretization // The hessian has entries in the order of psi_min, the inverse thus scales with 1/psi_min, and thus the timestep // should be psi_min to get an update of order 1 - double initial_dt = 1e-14; // std::min(dt, min_acceptable_density); + double initial_dt = dx / 100.; // std::min(dt, min_acceptable_density); timestepper.solve(t_end, initial_dt, num_save_steps, diff --git a/dune/gdt/test/momentmodels/entropyflux.hh b/dune/gdt/test/momentmodels/entropyflux.hh index bbd3e6f7e8372ffff3a5aeb5e761b7b233ee54d0..df43f947c7047a9798706f924f7f3d1c1f829529 100644 --- a/dune/gdt/test/momentmodels/entropyflux.hh +++ b/dune/gdt/test/momentmodels/entropyflux.hh @@ -331,7 +331,6 @@ public: ret = implementation_->evaluate_kinetic_flux_with_alphas(alpha_i, alpha_j, n_ij, dd); } // StateType evaluate_kinetic_flux(...) - // Returns alpha(u), starting from alpha_iso. To get better performance when calculating several alphas, use // Localfunction's get_alpha std::unique_ptr<AlphaReturnType> get_alpha(const StateType& u, const bool regularize) const @@ -342,6 +341,21 @@ public: return implementation_->get_alpha(u, *alpha_iso, regularize); } + std::unique_ptr<AlphaReturnType> get_alpha(const E& entity, const StateType& u, const bool regularize) const + { + thread_local const auto local_func = derived_local_function(); + local_func->bind(entity); + return local_func->get_alpha(u, regularize); + } + + StateType evaluate_kinetic_outflow(const typename AlphaReturnType::first_type& alpha_i, + const DomainType& n_ij, + const size_t dd) const + + { + return implementation_->evaluate_kinetic_outflow(alpha_i, n_ij, dd); + } // DomainType evaluate_kinetic_outflow(...) + const MomentBasis& basis_functions() const { return implementation_->basis_functions(); diff --git a/dune/gdt/test/momentmodels/entropyflux_implementations.hh b/dune/gdt/test/momentmodels/entropyflux_implementations.hh index fc55f30a87a3773590d957cf9ca038822ea06df0..060e428eca2fc396f5feb68eac435242f7a90081 100644 --- a/dune/gdt/test/momentmodels/entropyflux_implementations.hh +++ b/dune/gdt/test/momentmodels/entropyflux_implementations.hh @@ -545,6 +545,25 @@ public: return ret; } // DomainType evaluate_kinetic_flux_with_alphas(...) + DomainType evaluate_kinetic_outflow(const DomainType& alpha_i, const FluxDomainType& n_ij, const size_t dd) const + + { + auto& eta_ast_prime_vals_i = working_storage(); + evaluate_eta_ast_prime(alpha_i, M_, eta_ast_prime_vals_i); + 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 = eta_ast_prime_vals_i[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 + ret *= n_ij[dd]; + return ret; + } // DomainType evaluate_kinetic_outflow(...) + // Calculates left and right kinetic flux with reconstructed densities. Ansatz distribution values contains // evaluations of the ansatz distribution at each quadrature point for a stencil of three entities. The distributions // are reconstructed pointwise for each quadrature point and the resulting (part of) the kinetic flux is < @@ -2051,6 +2070,46 @@ public: return ret; } // DomainType evaluate_kinetic_flux(...) + // DomainType evaluate_kinetic_outflow(const DomainType& alpha, + // const FluxDomainType& n_ij, + // const size_t dd) const + // { + // return evaluate_kinetic_outflow(alpha, n_ij, dd); + // } // DomainType evaluate_kinetic_outflow(...) + + DomainType evaluate_kinetic_outflow(const BlockVectorType& alpha_i, const FluxDomainType& n_ij, const size_t dd) const + { + // calculate \sum_{i=1}^d < \omega_i m G_\alpha(u) > n_i + auto& eta_ast_prime_vals = working_storage(); + evaluate_eta_ast_prime(alpha_i, M_, eta_ast_prime_vals); + DomainType ret(0); + for (size_t jj = 0; jj < num_blocks; ++jj) { + const auto offset = block_size * jj; + if (quad_signs_[jj][dd] == 0) { + // in this case, we have to decide which eta_ast_prime_vals to use for each quadrature point individually + for (size_t ll = 0; ll < quad_points_[jj].size(); ++ll) { + const auto position = quad_points_[jj][ll][dd]; + if (position * n_ij[dd] > 0.) { + RangeFieldType factor = eta_ast_prime_vals[jj][ll] * quad_weights_[jj][ll] * position; + for (size_t ii = 0; ii < block_size; ++ii) + ret[offset + ii] += M_[jj].get_entry(ll, ii) * factor; + } + } // ll + } else { + // all quadrature points have the same sign + if (quad_signs_[jj][dd] * n_ij[dd] > 0.) { + for (size_t ll = 0; ll < quad_points_[jj].size(); ++ll) { + const RangeFieldType factor = eta_ast_prime_vals[jj][ll] * quad_weights_[jj][ll] * quad_points_[jj][ll][dd]; + for (size_t ii = 0; ii < block_size; ++ii) + ret[offset + ii] += M_[jj].get_entry(ll, ii) * factor; + } // ll + } + } // quad_sign + } // jj + ret *= n_ij[dd]; + return ret; + } // DomainType evaluate_kinetic_outflow(...) + // Calculates left and right kinetic flux with reconstructed densities. Ansatz distribution values contains // evaluations of the ansatz distribution at each quadrature point for a stencil of three entities. The distributions // are reconstructed pointwise for each quadrature point and the resulting (part of) the kinetic flux is < @@ -3086,6 +3145,37 @@ public: return ret; } // DomainType evaluate_kinetic_flux(...) + DomainType evaluate_kinetic_outflow(const DomainType& alpha_i, const FluxDomainType& n_ij, const size_t dd) const + { + return evaluate_kinetic_outflow(XT::LA::convert_to<VectorType>(alpha_i), n_ij, dd); + } + + DomainType evaluate_kinetic_outflow(const VectorType& alpha_i, const FluxDomainType& n_ij, const size_t dd) const + { + auto& eta_ast_prime_vals = working_storage(); + evaluate_eta_ast_prime(alpha_i, eta_ast_prime_vals); + // calculate \sum_{i=1}^d < \omega_i m G_\alpha(u) > n_i + DomainType ret(0); + const auto& faces = basis_functions_.triangulation().faces(); + LocalVectorType local_ret; + for (size_t jj = 0; jj < num_faces_; ++jj) { + const bool positive_dir = v_positive_[jj][dd]; + if ((n_ij[dd] > 0. && positive_dir) || (n_ij[dd] < 0. && !positive_dir)) { + local_ret *= 0.; + const auto& vertices = faces[jj]->vertices(); + for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll) { + RangeFieldType factor = eta_ast_prime_vals[jj][ll] * quad_weights_[jj][ll] * quad_points_[jj][ll][dd]; + for (size_t ii = 0; ii < 3; ++ii) + local_ret[ii] += M_[jj][ll][ii] * factor; + } // ll (quad points) + for (size_t ii = 0; ii < 3; ++ii) + ret[vertices[ii]->index()] += local_ret[ii]; + } + } // jj (faces) + ret *= n_ij[dd]; + return ret; + } // DomainType evaluate_kinetic_outflow(...) + // Calculates left and right kinetic flux with reconstructed densities. Ansatz distribution values contains // evaluations of the ansatz distribution at each quadrature point for a stencil of three entities. The distributions // are reconstructed pointwise for each quadrature point and the resulting (part of) the kinetic flux is < @@ -4474,6 +4564,26 @@ public: return ret; } // DomainType evaluate_kinetic_flux(...) + DomainType evaluate_kinetic_outflow(const VectorType& alpha_i, const FluxDomainType& n_ij, const size_t dd) const + { + auto& eta_ast_prime_vals = working_storage(); + evaluate_eta_ast_prime(alpha_i, eta_ast_prime_vals); + // calculate \sum_{i=1}^d < \omega_i m G_\alpha(u) > n_i + DomainType ret(0); + const size_t first_positive = dimRange / 2; + if (n_ij[dd] < 0.) { + for (size_t ll = 0; ll < first_positive; ++ll) + ret[ll] = eta_ast_prime_vals[ll] * grid_points_[ll]; + } else { + for (size_t ll = first_positive; ll < dimRange; ++ll) + ret[ll] = second_exp_vals[ll] * grid_points_[ll]; + } + ret *= n_ij[dd] * interval_length; + ret[0] *= 0.5; + ret[dimRange - 1] *= 0.5; + return ret; + } // DomainType evaluate_kinetic_outflow(...) + // Calculates left and right kinetic flux with reconstructed densities. Ansatz distribution values contains // evaluations of the ansatz distribution at each quadrature point for a stencil of three entities. The distributions // are reconstructed pointwise for each quadrature point and the resulting (part of) the kinetic flux is @@ -5178,6 +5288,27 @@ public: return ret; } // DomainType evaluate_kinetic_flux(...) + DomainType evaluate_kinetic_outflow(const VectorType& alpha_i, const FluxDomainType& n_ij, const size_t dd) const + { + assert(dd == 0); + auto& eta_ast_prime_vals = working_storage(); + evaluate_eta_ast_prime(alpha_i, eta_ast_prime_vals); + // calculate \sum_{i=1}^d < \omega_i m G_\alpha(u) > n_i + DomainType ret(0); + for (size_t jj = 0; jj < num_intervals; ++jj) { + for (size_t ll = 0; ll < quad_weights_[jj].size(); ++ll) { + const auto position = quad_points_[jj][ll]; + if (position * n_ij[dd] > 0.) { + const RangeFieldType factor = eta_ast_prime_vals[jj][ll] * quad_weights_[jj][ll] * position; + for (size_t ii = 0; ii < 2; ++ii) + ret[jj + ii] += M_[jj][ll][ii] * factor; + } + } // ll (quad points) + } // jj (faces) + ret *= n_ij[dd]; + return ret; + } // DomainType evaluate_kinetic_flux(...) + // Calculates left and right kinetic flux with reconstructed densities. Ansatz distribution values contains // evaluations of the ansatz distribution at each quadrature point for a stencil of three entities. The distributions // are reconstructed pointwise for each quadrature point and the resulting (part of) the kinetic flux is < diff --git a/dune/gdt/test/momentmodels/mn-discretization.hh b/dune/gdt/test/momentmodels/mn-discretization.hh index 9f78fde32142e040f8ade3f0a92e20e1fd6adc30..634f1691b41ebb42e2a3f51043339f9af09d73c6 100644 --- a/dune/gdt/test/momentmodels/mn-discretization.hh +++ b/dune/gdt/test/momentmodels/mn-discretization.hh @@ -66,8 +66,10 @@ struct HyperbolicMnDiscretization static constexpr size_t dimDomain = MomentBasis::dimDomain; static constexpr size_t dimRange = MomentBasis::dimRange; static const auto la_backend = TestCaseType::la_backend; + using DomainType = FieldVector<RangeFieldType, dimDomain>; using MatrixType = typename XT::LA::Container<RangeFieldType, la_backend>::MatrixType; using VectorType = typename XT::LA::Container<RangeFieldType, la_backend>::VectorType; + using DynamicRangeType = DynamicVector<RangeFieldType>; //******************* create grid and FV space *************************************** auto grid_config = ProblemType::default_grid_cfg(); @@ -94,6 +96,7 @@ struct HyperbolicMnDiscretization using AnalyticalFluxType = typename ProblemType::FluxType; using EntropyFluxType = typename ProblemType::ActualFluxType; auto analytical_flux = problem.flux(); + auto* entropy_flux = dynamic_cast<EntropyFluxType*>(analytical_flux.get()); // 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. @@ -146,24 +149,60 @@ struct HyperbolicMnDiscretization // boundary treatment using BoundaryOperator = - LocalAdvectionFvBoundaryTreatmentByCustomExtrapolationOperator<I, VectorType, GV, dimRange>; + // LocalAdvectionFvBoundaryTreatmentByCustomExtrapolationOperator<I, VectorType, GV, dimRange>; + LocalAdvectionFvBoundaryTreatmentByCustomNumericalFluxOperator<I, VectorType, GV, dimRange>; using LambdaType = typename BoundaryOperator::LambdaType; - using DynamicStateType = typename BoundaryOperator::DynamicStateType; + // using DynamicStateType = typename BoundaryOperator::DynamicStateType; + // LambdaType boundary_lambda = + // [&boundary_values](const I& intersection, + // const FieldVector<RangeFieldType, dimDomain - 1>& + // xx_in_reference_intersection_coordinates, const AnalyticalFluxType& /*flux*/, const + // DynamicStateType& /*u*/, DynamicStateType& v, const XT::Common::Parameter& param) { + // boundary_values->evaluate(intersection.geometry().global(xx_in_reference_intersection_coordinates), v, + // param); + // }; + + // store boundary influx fluxes + std::map<DomainType, DynamicRangeType, 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 dd = intersection.indexInInside() / 2; + const DynamicRangeType boundary_flux = + problem.kinetic_boundary_flux(x, intersection.centerUnitOuterNormal()[dd], dd); + boundary_fluxes.insert(std::make_pair(x, boundary_flux)); + } + using GenericFunctionType = XT::Functions::GenericFunction<dimDomain, dimRange, 1, RangeFieldType>; + GenericFunctionType boundary_kinetic_fluxes( + 1, [&](const DomainType& x, DynamicRangeType& ret, const XT::Common::Parameter&) { ret = boundary_fluxes[x]; }); + LambdaType boundary_lambda = - [&boundary_values](const I& intersection, - const FieldVector<RangeFieldType, dimDomain - 1>& xx_in_reference_intersection_coordinates, - const AnalyticalFluxType& /*flux*/, - const DynamicStateType& /*u*/, - DynamicStateType& v, - const XT::Common::Parameter& param) { - boundary_values->evaluate(intersection.geometry().global(xx_in_reference_intersection_coordinates), v, param); + [&boundary_kinetic_fluxes, + &entropy_flux](const I& intersection, + const FieldVector<RangeFieldType, dimDomain - 1>& xx_in_reference_intersection_coordinates, + const DynamicRangeType& u, + DynamicRangeType& g, + const XT::Common::Parameter& param) { + // influx + boundary_kinetic_fluxes.evaluate( + intersection.geometry().global(xx_in_reference_intersection_coordinates), g, param); + // outflux + const auto& entity = intersection.inside(); + const auto dd = intersection.indexInInside() / 2; + const auto alpha_entity = entropy_flux->get_alpha(entity, u, true)->first; + const auto outflux = + entropy_flux->evaluate_kinetic_outflow(alpha_entity, intersection.centerUnitOuterNormal(), dd); + g -= outflux; + g *= -1; }; + XT::Grid::ApplyOn::NonPeriodicBoundaryIntersections<GV> filter; advection_operator.append(boundary_lambda, {}, filter); constexpr double epsilon = 1e-11; auto slope = TestCaseType::RealizabilityLimiterChooserType::template make_slope<EigenvectorWrapperType>( - *dynamic_cast<EntropyFluxType*>(analytical_flux.get()), *basis_functions, epsilon); + *entropy_flux, *basis_functions, epsilon); ReconstructionOperatorType reconstruction_operator(*analytical_flux, *boundary_values, fv_space, *slope, false); ReconstructionAdvectionOperatorType reconstruction_advection_operator(advection_operator, reconstruction_operator);