1#ifndef DUNE_COPASI_MODEL_DIFFUSION_REACTION_REDUCE_HH
2#define DUNE_COPASI_MODEL_DIFFUSION_REACTION_REDUCE_HH
11#include <dune/pdelab/concepts/basis.hh>
12#include <dune/pdelab/common/local_container.hh>
13#include <dune/pdelab/common/trace.hh>
14#include <dune/pdelab/common/for_each_entity.hh>
16#include <dune/grid/common/partitionset.hh>
18#include <dune/common/float_cmp.hh>
20#include <function2/function2.hpp>
21#include <spdlog/spdlog.h>
26#include <tbb/enumerable_thread_specific.h>
39template<
class ExecutionPolicy, PDELab::Concept::Basis Basis,
class Coefficients>
41 PDELab::Execution::is_execution_policy_v<ExecutionPolicy> )
42static std::map<std::string, double>
51 constexpr std::size_t dim = Basis::EntitySet::GridView::dimension;
54 if (basis.entitySet().size(0) == 0) {
58 std::shared_ptr<const ParserContext> parser_context;
63 if (
not parser_context) {
64 parser_context = std::make_shared<const ParserContext>();
67 auto parser_type = string2parser.at(
config.get(
"parser_type", default_parser_str));
70 for (std::size_t
i = 0;
i !=
lnode.degree(); ++
i)
71 if (
lnode.child(
i).size() != 0)
72 return lnode.child(
i).finiteElement();
78 for (std::size_t
i = 0;
i !=
lnode.degree(); ++
i)
79 if (
lnode.child(
i).size() != 0)
85 using Geometry =
typename Basis::EntitySet::template Codim<0>::Entity::Geometry;
89 typename Basis::LocalView lbasis;
90 PDELab::LocalContainerBuffer<Basis, const Coefficients> lcoeff;
91 std::unique_ptr<LocalEquations<dim>> leqs;
92 LocalBasisCache<typename FEM::Traits::LocalBasisType::Traits> fe_cache = {};
93 std::optional<typename Geometry::JacobianInverse> geojacinv_opt = std::nullopt;
94 std::vector<double> values = {};
95 std::vector<fu2::unique_function<double() const>> evaluations = {};
96 std::vector<fu2::unique_function<double(
double,
double)
const>> reductions = {};
100 auto lbasis = basis.localView();
106 auto sz =
config.getSubKeys().size();
108 data.evaluations.reserve(
sz);
109 data.reductions.reserve(
sz);
114 if (
sub_config.hasKey(
"evaluation.expression"))
119 auto&
reduction =
data.reductions.emplace_back(std::plus<>{});
120 if (
sub_config.hasKey(
"reduction.expression")) {
122 parser_context->parse_function_expression(
sub_config.sub(
"reduction")[
"expression"]);
123 if (
args.size() == 3)
125 "Reduction expression for '{0}' uses 3 arguments whereas 2 are now required.\nThis "
126 "functionality has changed: the third argument became the contextual keyword "
127 "'integration_factor' that may be used to express '{0}.evaluation.expression'.",
129 if (
args.size() != 2)
132 sub_config.get(
"reduction.parser_type", std::string{ parser2string.at(parser_type) }));
133 auto str_args = std::array{ std::string{
args[0] }, std::string{
args[1] } };
143 struct ThreadData :
public std::array<ThreadLocalData,1> {
144 ThreadLocalData& local() {
145 return this->front();
151 if (
config.getSubKeys().empty()) {
155 spdlog::info(
"Evaluating reduce operators");
157 auto& data = thread_data.local();
158 auto geo = cell.geometry();
160 data.lbasis.bind(cell);
161 data.lcoeff.load(data.lbasis, std::false_type{});
163 std::size_t
const order = 4;
165 QuadratureRules<typename Geometry::ctype, Geometry::coorddimension>::rule(
geo.type(),
order);
170 data.geojacinv_opt.emplace(
geo.jacobianInverse(position));
172 data.leqs->position =
geo.global(position);
173 data.leqs->integration_factor =
weight *
geo.integrationElement(position);
177 if (node.size() == 0)
179 auto& value = data.leqs->get_value(node);
180 auto& gradient = data.leqs->get_gradient(node);
183 data.fe_cache.bind(node.finiteElement(), quad_rule);
184 const auto& phi = data.fe_cache.evaluateFunction(q);
185 const auto& jacphi = data.fe_cache.evaluateJacobian(q);
186 for (std::size_t dof = 0; dof != node.size(); ++dof) {
187 value += data.lcoeff(node, dof) * phi[dof];
188 gradient += data.lcoeff(node, dof) * (jacphi[dof] * geojacinv)[0];
193 for (std::size_t
i = 0;
i !=
data.values.size(); ++
i)
197 data.lbasis.unbind();
201 if (basis.entitySet().grid().comm().size() > 1){
202 throw format_exception(ParallelError{},
"Transoform and Reduce is not implemented in parallel");
206 std::vector<double> values;
207 for (
const auto& key : config.getSubKeys())
208 values.emplace_back(config.sub(key, true).get(
"initial.value", 0.));
209 for (std::size_t i = 0; i != values.size(); ++i)
210 for (
const auto& data : thread_data)
211 values[i] = data.reductions[i](data.values[i], values[i]);
214 std::size_t max_key_chars = 0;
215 std::size_t max_val_chars = 5;
216 for (
const auto& key : config.getSubKeys())
217 max_key_chars = std::max(max_key_chars, key.size());
219 std::map<std::string, double> key_value;
221 spdlog::info(
" ┌{0:─^{1}}┐",
"", max_key_chars + max_val_chars + 13);
222 std::string error_msg;
223 auto values_it = values.begin();
224 for (
const auto& key : config.getSubKeys()) {
225 auto& value = key_value[key] = *(values_it++);
227 if (config.sub(key).hasKey(
"transformation.expression")) {
228 auto [args, expr] = parser_context->parse_function_expression(config.sub(key)[
"transformation.expression"]);
229 if (args.size() != 1)
230 throw format_exception(IOError{},
"Warning function must have exactly 1 argument");
231 auto sub_parser_type = string2parser.at(config.sub(key).get(
"transformation.parser_type", std::string{parser2string.at(parser_type)}));
232 auto transformation = parser_context->make_function(sub_parser_type, std::array{std::string{args[0]}}, expr);
233 value = transformation(value);
236 bool processed =
false;
238 if (config.sub(key).hasKey(
"error.expression")) {
239 auto [args, expr] = parser_context->parse_function_expression(config.sub(key)[
"error.expression"]);
240 if (args.size() != 1)
241 throw format_exception(IOError{},
"Error function must have exactly 1 argument");
242 auto sub_parser_type = string2parser.at(config.sub(key).get(
"error.parser_type", std::string{parser2string.at(parser_type)}));
243 auto error = parser_context->make_function(sub_parser_type, std::array{std::string{args[0]}}, expr);
244 if (FloatCmp::ne(error(value), 0.)) {
247 if (not error_msg.empty())
249 error_msg += fmt::format(
"Reduction on the token '{}' raised an error because the "
250 "expression '{}' with evaluates to false with '{} := {}'",
258 if (not processed and config.sub(key).hasKey(
"warn.expression")) {
259 auto [args, expr] = parser_context->parse_function_expression(config.sub(key)[
"warn.expression"]);
260 if (args.size() != 1)
261 throw format_exception(IOError{},
"Warning function must have exactly 1 argument");
262 auto sub_parser_type = string2parser.at(config.sub(key).get(
"warn.parser_type", std::string{parser2string.at(parser_type)}));
263 auto warn = parser_context->make_function(sub_parser_type, std::array{std::string{args[0]}}, expr);
264 if (FloatCmp::ne(warn(value), 0.)) {
270 if (not processed and not config.sub(key).get(
"quiet",
false)) {
274 spdlog::info(
" └{0:─^{1}}┘",
"", max_key_chars+max_val_chars+13);
276 if (not error_msg.empty()) {
static std::unique_ptr< LocalEquations > make(const LocalBasis &lbasis, const ParameterTree &config={}, std::shared_ptr< const FunctorFactory< dim > > functor_factory=nullptr, std::shared_ptr< const CellData< CellDataGridView, CellDataType > > grid_cell_data=nullptr, BitFlags< FactoryFalgs > opts=BitFlags< FactoryFalgs >::no_flags())
Definition local_equations.hh:264
Definition functor_factory.hh:24
Definition compartment_tree.hh:99
#define DUNE_COPASI_FMT_STYLED_BOLD(key)
Definition fmt_style.hh:13
auto format_exception(Exception &&e, fmt::format_string< Args... > format, Args &&... args)
Definition exceptions.hh:23
constexpr bool is_bitflags_v
Alias for Bitflag indicator.
Definition bit_flags.hh:24