1#ifndef DUNE_COPASI_LOCAL_OPERATOR_DIFFUSION_REACTION_CG_HH
2#define DUNE_COPASI_LOCAL_OPERATOR_DIFFUSION_REACTION_CG_HH
11#include <dune/pdelab/common/concurrency/shared_stash.hh>
12#include <dune/pdelab/common/execution.hh>
13#include <dune/pdelab/common/tree_traversal.hh>
14#include <dune/pdelab/operator/local_assembly/archetype.hh>
15#include <dune/pdelab/operator/local_assembly/interface.hh>
17#include <dune/grid/multidomaingrid/singlevalueset.hh>
19#include <dune/geometry/type.hh>
21#include <dune/common/fvector.hh>
22#include <dune/common/overloadset.hh>
37template<PDELab::Concept::Basis TestBasis,
39 Dune::Concept::GridView CellDataGridView =
typename TestBasis::EntitySet,
40 class CellDataType = double,
41 class ExecutionPolicy = PDELab::Execution::SequencedPolicy>
46 using DF =
typename LBT::DomainFieldType;
47 using RF =
typename LBT::RangeFieldType;
48 static constexpr int dim = LBT::dimDomain;
54 const MembraneScalarFunction& outflow;
55 const CompartmentNode& source;
56 Outflow(
const MembraneScalarFunction& outflow,
const CompartmentNode& source) : outflow{outflow}, source{source} {}
58 std::vector<Outflow> _outflow_i;
59 std::vector<Outflow> _outflow_o;
61 std::vector<std::size_t> _compartment2domain;
63 TestBasis _test_basis;
65 bool _is_linear =
true;
66 bool _has_outflow =
true;
67 bool _do_numerical_jacobian =
false;
68 double _fin_diff_epsilon = 1e-7;
70 std::shared_ptr<const CellData<CellDataGridView, CellDataType>> _grid_cell_data;
71 PDELab::SharedStash<LocalEquations<dim>> _local_values_in;
72 PDELab::SharedStash<LocalEquations<dim>> _local_values_out;
75 struct NumericalJacobianCache {
76 std::any coeff_in, coeff_out, up_in, up_out, down_in, down_out;
78 inline static thread_local NumericalJacobianCache _num_jac_cache = {};
80 ExecutionPolicy _execution_policy;
82 template<
class Value,
class... Args>
83 static Value& value_or_emplace(std::any& val, Args&&... args) {
85 return std::any_cast<Value&>(val);
87 return val.template emplace<Value>(std::forward<Args>(args)...);
90 template<
class R,
class X>
93 void accumulate(
const auto& ltest,
99 _r.accumulate(ltest, test_dof, _z(ltrial, trial_dof) * value);
101 auto weight()
const {
return _r.weight(); }
107 template<
class R,
class X>
108 PseudoJacobian(R&,
const X&) -> PseudoJacobian<R, X>;
113 static constexpr std::true_type contains(
auto) {
return {}; }
114 friend constexpr std::true_type
operator==(
const MonoDomainSet&,
const MonoDomainSet&)
120 auto subDomains(
const Dune::Concept::Entity
auto& entity)
const
123 return _test_basis.entitySet().indexSet().subDomains(entity);
125 return MonoDomainSet{};
128 std::size_t integrationOrder(
const auto&... lbasis_pack)
const {
129 std::size_t order = 0;
130 std::size_t intorderadd = 0;
131 std::size_t quad_factor = 2;
132 Hybrid::forEach(std::tie(lbasis_pack...), [&](
const auto& lbasis){
133 forEachLeafNode(lbasis.tree(), [&](const auto& node) {
134 if (node.size() != 0)
135 order = std::max<std::size_t>(order, node.finiteElement().localBasis().order());
138 return intorderadd + quad_factor * order;
153 return std::bool_constant<Concept::MultiDomainGrid<typename TestBasis::EntitySet::Grid>>{};
157 return _execution_policy;
165 if (not intersection.neighbor())
170 return (subDomains(intersection.inside()) == subDomains(intersection.outside()));
187 const ParameterTree& config,
190 ExecutionPolicy execution_policy = {})
191 : _test_basis{ test_basis }
192 , _is_linear{ config.get(
"is_linear",
false) }
193 , _do_numerical_jacobian{ [&]() ->
bool {
194 auto type = config.get(
"jacobian.type",
"analytical");
195 if (
const auto& type = config.get(
"jacobian.type",
"analytical");
196 (type ==
"analytical") or (type ==
"numerical"))
197 return type ==
"numerical";
200 IOError{},
"The option 'model.jacobian.type' must be either 'analytical' or 'numerical'");
202 , _fin_diff_epsilon{ config.get(
"jacobian.epsilon", 1e-7) }
203 , _grid_cell_data{ grid_cell_data }
204 , _local_values_in([_lop_type = lop_type,
205 _basis = _test_basis,
207 _functor_factory = functor_factory,
208 _grid_cell_data = grid_cell_data]() {
209 std::unique_ptr<LocalEquations<dim>> ptr;
210 if (_lop_type == Form::Mass)
211 ptr =
LocalEquations<dim>::make_mass(_basis.localView(), _config.sub(
"scalar_field",
true), _config.sub(
"domain"), _functor_factory, _grid_cell_data);
212 else if (_lop_type == Form::Stiffness)
218 , _local_values_out([_lop_type = lop_type,
219 _basis = _test_basis,
221 _functor_factory = std::move(functor_factory),
222 _grid_cell_data = std::move(grid_cell_data)]() {
223 std::unique_ptr<LocalEquations<dim>> ptr;
224 if (_lop_type == Form::Mass)
225 ptr =
LocalEquations<dim>::make_mass(_basis.localView(), _config.sub(
"scalar_field",
true), _config.sub(
"domain"), _functor_factory, _grid_cell_data);
226 else if (_lop_type == Form::Stiffness)
232 , _execution_policy{ execution_policy }
234 if (_is_linear and std::exchange(_do_numerical_jacobian,
false))
235 spdlog::warn(
"Local operator is linear, thus, the jacobian is always evaluated with the primary expression meaning that 'jacobian.type = analytical'");
236 if (not _is_linear and not _local_values_in->domain_deformation.compartment_jacobian.empty() and not _do_numerical_jacobian)
237 throw format_exception(NotImplemented{},
"Non-linear moving domains must use numerical jacobian");
238 auto lbasis = _test_basis.localView();
239 if (_test_basis.entitySet().size(0) == 0)
241 lbasis.bind(*_test_basis.entitySet().template begin<0>());
242 _has_outflow =
false;
243 forEachLeafNode(lbasis.tree(), [&](
const auto& ltrial_node) {
244 const auto& eq = _local_values_in->get_equation(ltrial_node);
245 _has_outflow |= not eq.outflow.empty();
249 if constexpr (Concept::MultiDomainGrid<typename TestBasis::EntitySet::Grid>)
250 forEachNode(lbasis.tree(),
252 [&](
const Concept::CompartmentLocalBasisNode
auto& ,
auto path) {
253 auto compartment = back(path);
254 _compartment2domain.resize(compartment + 1);
255 _compartment2domain[compartment] =
256 _test_basis.subSpace(path).entitySet().grid().domain();
258 [&](
const auto& ) {}));
260 _compartment2domain.assign(1, std::numeric_limits<std::size_t>::max());
277 const PDELab::Concept::LocalBasis
auto& ltest,
278 auto& lpattern)
const noexcept
280 forEachLeafNode(ltest.tree(), [&](
const auto& ltest_node) {
281 const auto& ltrial_node = PDELab::containerEntry(ltrial.tree(), ltest_node.path());
282 const auto& eq = _local_values_in->get_equation(ltrial_node);
284 for (const auto& jacobian_entry : eq.reaction.compartment_jacobian) {
285 const auto& wrt_lbasis = jacobian_entry.wrt.to_local_basis_node(ltrial);
286 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i)
287 for (std::size_t dof_j = 0; dof_j != wrt_lbasis.size(); ++dof_j)
288 lpattern.addLink(ltest_node, dof_i, wrt_lbasis, dof_j);
293 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i)
294 for (std::size_t dof_j = 0; dof_j != ltrial_node.size(); ++dof_j)
295 lpattern.addLink(ltest_node, dof_i, ltrial_node, dof_j);
296 for (const auto& jacobian_entry : eq.storage.compartment_jacobian) {
297 const auto& wrt_lbasis = jacobian_entry.wrt.to_local_basis_node(ltrial);
298 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i)
299 for (std::size_t dof_j = 0; dof_j != wrt_lbasis.size(); ++dof_j)
300 lpattern.addLink(ltest_node, dof_i, wrt_lbasis, dof_j);
305 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i)
306 for (std::size_t dof_j = 0; dof_j != ltrial_node.size(); ++dof_j)
307 lpattern.addLink(ltest_node, dof_i, ltrial_node, dof_j);
308 for (const auto& jacobian_entry : eq.velocity.compartment_jacobian) {
309 const auto& wrt_lbasis = jacobian_entry.wrt.to_local_basis_node(ltrial);
310 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i)
311 for (std::size_t dof_j = 0; dof_j != wrt_lbasis.size(); ++dof_j)
312 lpattern.addLink(ltest_node, dof_i, wrt_lbasis, dof_j);
316 for (
const auto& diffusion : eq.cross_diffusion) {
317 const auto& wrt_lbasis = diffusion.wrt.to_local_basis_node(ltrial);
319 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i)
320 for (std::size_t dof_j = 0; dof_j != wrt_lbasis.size(); ++dof_j)
321 lpattern.addLink(ltest_node, dof_i, wrt_lbasis, dof_j);
323 for (const auto& jacobian_entry : diffusion.compartment_jacobian) {
324 const auto& jac_wrt_lbasis = jacobian_entry.wrt.to_local_basis_node(ltrial);
325 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i)
326 for (std::size_t dof_j = 0; dof_j != jac_wrt_lbasis.size(); ++dof_j)
327 lpattern.addLink(ltest_node, dof_i, jac_wrt_lbasis, dof_j);
330 for (
const auto& jacobian_entry : _local_values_in->domain_deformation.compartment_jacobian) {
331 const auto& jac_wrt_lbasis = jacobian_entry.wrt.to_local_basis_node(ltrial);
332 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i)
333 for (std::size_t dof_j = 0; dof_j != jac_wrt_lbasis.size(); ++dof_j)
334 lpattern.addLink(ltest_node, dof_i, jac_wrt_lbasis, dof_j);
341 const PDELab::Concept::LocalBasis
auto& ltrial_in,
342 const PDELab::Concept::LocalBasis
auto& ltest_in,
343 const PDELab::Concept::LocalBasis
auto& ltrial_out,
344 const PDELab::Concept::LocalBasis
auto& ltest_out,
345 auto& lpattern_in_in,
346 auto& lpattern_in_out,
347 auto& lpattern_out_in,
348 auto& lpattern_out_out)
noexcept
350 forEachLeafNode(ltest_in.tree(), [&](
const auto& ltest_node_in) {
351 if (ltest_node_in.size() == 0)
353 const auto& ltrial_node = PDELab::containerEntry(ltrial_in.tree(), ltest_node_in.path());
354 const auto& eq = _local_values_in->get_equation(ltrial_node);
355 for (const auto& outflow_i : eq.outflow) {
356 for (const auto& jacobian_entry : outflow_i.compartment_jacobian) {
357 const auto& wrt_lbasis_in = jacobian_entry.wrt.to_local_basis_node(ltrial_in);
358 const auto& wrt_lbasis_out = jacobian_entry.wrt.to_local_basis_node(ltrial_out);
359 for (std::size_t dof_i = 0; dof_i != ltest_node_in.size(); ++dof_i) {
360 for (std::size_t dof_j = 0; dof_j != wrt_lbasis_in.size(); ++dof_j)
361 lpattern_in_in.addLink(ltest_node_in, dof_i, wrt_lbasis_in, dof_j);
362 for (std::size_t dof_j = 0; dof_j != wrt_lbasis_out.size(); ++dof_j)
363 lpattern_in_out.addLink(ltest_node_in, dof_i, wrt_lbasis_out, dof_j);
369 if (not intersection.neighbor())
372 forEachLeafNode(ltest_out.tree(), [&](
const auto& ltest_node_out) {
373 if (ltest_node_out.size() == 0)
375 const auto& ltrial_node = PDELab::containerEntry(ltrial_out.tree(), ltest_node_out.path());
376 const auto& eq = _local_values_in->get_equation(ltrial_node);
377 for (const auto& outflow_o : eq.outflow) {
378 for (const auto& jacobian_entry : outflow_o.compartment_jacobian) {
379 const auto& wrt_lbasis_in = jacobian_entry.wrt.to_local_basis_node(ltrial_in);
380 const auto& wrt_lbasis_out = jacobian_entry.wrt.to_local_basis_node(ltrial_out);
381 for (std::size_t dof_i = 0; dof_i != ltest_node_out.size(); ++dof_i) {
382 for (std::size_t dof_j = 0; dof_j != wrt_lbasis_in.size(); ++dof_j)
383 lpattern_out_in.addLink(ltest_node_out, dof_i, wrt_lbasis_in, dof_j);
384 for (std::size_t dof_j = 0; dof_j != wrt_lbasis_out.size(); ++dof_j)
385 lpattern_out_out.addLink(ltest_node_out, dof_i, wrt_lbasis_out, dof_j);
394 const PDELab::Concept::LocalBasis
auto& ltrial_in,
395 const PDELab::Concept::LocalBasis
auto& ltest_in,
396 auto& lpattern_in)
noexcept
398 localAssemblePatternSkeleton(intersection, ltrial_in, ltest_in, ltrial_in, ltest_in, lpattern_in, lpattern_in, lpattern_in, lpattern_in);
418 const PDELab::Concept::LocalBasis
auto& ltrial,
419 const PDELab::Concept::LocalConstContainer
auto& lcoefficients,
420 const PDELab::Concept::LocalBasis
auto& ltest,
421 PDELab::Concept::LocalMutableContainer
auto& lresidual)
noexcept
423 if (ltrial.size() == 0)
426 const auto& entity = ltrial.element();
427 const auto& geo =
move_geometry(time, ltrial, lcoefficients, _local_values_in, _fe_cache, entity.geometry(), std::identity{});
428 using Geometry = std::decay_t<
decltype(geo)>;
429 std::optional<typename Geometry::JacobianInverse> geojacinv_opt;
431 _local_values_in->time = time;
432 _local_values_in->entity_volume = geo.volume();
433 _local_values_in->in_volume = 1;
437 _grid_cell_data->getData(entity, _local_values_in->cell_values, _local_values_in->cell_mask);
439 auto intorder = integrationOrder(ltrial);
440 thread_local const auto quad_rule = QuadratureRules<DF, dim>::rule(geo.type(), intorder);
443 for (std::size_t q = 0; q != quad_rule.size(); ++q) {
444 const auto [position, weight] = quad_rule[q];
445 if (not geojacinv_opt or not geo.affine())
446 geojacinv_opt.emplace(geo.jacobianInverse(position));
447 const auto& geojacinv = *geojacinv_opt;
448 _local_values_in->position = geo.global(position);
449 auto factor = weight * geo.integrationElement(position);
452 forEachLeafNode(ltrial.tree(), [&](
const auto& node) {
453 if (node.size() == 0)
455 auto& value = _local_values_in->get_value(node);
456 auto& gradient = _local_values_in->get_gradient(node);
459 _fe_cache.bind(node.finiteElement(), quad_rule);
460 const auto& phi = _fe_cache.evaluateFunction(q);
461 const auto& jacphi = _fe_cache.evaluateJacobian(q);
462 for (std::size_t dof = 0; dof != node.size(); ++dof) {
463 value += lcoefficients(node, dof) * phi[dof];
464 gradient += lcoefficients(node, dof) * (jacphi[dof] * geojacinv)[0];
469 forEachLeafNode(ltest.tree(), [&](
const auto& ltest_node) {
470 if (ltest_node.size() == 0)
473 _local_values_in->get_equation(PDELab::containerEntry(ltrial.tree(), ltest_node.path()));
475 _fe_cache.bind(ltest_node.finiteElement(), quad_rule);
476 const auto& psi = _fe_cache.evaluateFunction(q);
477 const auto& jacpsi = _fe_cache.evaluateJacobian(q);
479 RF scalar = eq.reaction ? RF{-eq.reaction()} : 0.;
480 scalar += eq.storage ? RF{eq.value * eq.storage()} : 0.;
481 auto flux = eq.velocity ? eq.velocity() * eq.value[0] : FieldVector<RF,dim>(0.);
482 for (
const auto& diffusion : eq.cross_diffusion)
483 flux -= diffusion(diffusion.wrt.gradient);
484 for (std::size_t dof = 0; dof != ltest_node.size(); ++dof)
485 lresidual.accumulate(ltest_node, dof, (scalar * psi[dof] -dot(flux, (jacpsi[dof] * geojacinv)[0] )) * factor);
489 _local_values_in->clear();
490 _local_values_in->in_volume = 0;
512 const PDELab::Concept::LocalBasis
auto& ltrial,
513 const PDELab::Concept::LocalConstContainer
auto& llin_point,
514 const PDELab::Concept::LocalConstContainer
auto& lapp_point,
515 const PDELab::Concept::LocalBasis
auto& ltest,
516 PDELab::Concept::LocalMutableContainer
auto& ljacobian)
noexcept
519 PseudoJacobian mat{ ljacobian, lapp_point };
520 if (localAssembleIsLinear())
521 localAssembleVolume(time, ltrial, lapp_point, ltest, ljacobian);
523 localAssembleJacobianVolume(time, ltrial, llin_point, ltest, mat);
543 const PDELab::Concept::LocalBasis
auto& ltrial,
544 const PDELab::Concept::LocalConstContainer
auto& llin_point,
545 const PDELab::Concept::LocalBasis
auto& ltest,
546 auto& ljacobian)
noexcept
548 if (ltrial.size() == 0)
551 const auto& entity = ltrial.element();
552 const auto& geo =
move_geometry(time, ltrial, llin_point, _local_values_in, _fe_cache, entity.geometry(), std::identity{});
553 using Geometry = std::decay_t<
decltype(geo)>;
554 std::optional<typename Geometry::JacobianInverse> geojacinv_opt;
556 _local_values_in->time = time;
557 _local_values_in->entity_volume = geo.volume();
558 _local_values_in->in_volume = 1;
560 auto intorder = integrationOrder(ltrial);
561 thread_local const auto quad_rule = QuadratureRules<DF, dim>::rule(geo.type(), intorder);
565 _grid_cell_data->getData(entity, _local_values_in->cell_values, _local_values_in->cell_mask);
568 for (std::size_t q = 0; q != quad_rule.size(); ++q) {
569 const auto [position, weight] = quad_rule[q];
570 if (not geojacinv_opt or not geo.affine())
571 geojacinv_opt.emplace(geo.jacobianInverse(position));
572 const auto& geojacinv = *geojacinv_opt;
573 _local_values_in->position = geo.global(position);
574 auto factor = weight * geo.integrationElement(position);
577 forEachLeafNode(ltrial.tree(), [&](
const auto& node) {
578 if (node.size() == 0)
580 auto& value = _local_values_in->get_value(node);
581 auto& gradient = _local_values_in->get_gradient(node);
584 _fe_cache.bind(node.finiteElement(), quad_rule);
585 const auto& phi = _fe_cache.evaluateFunction(q);
586 const auto& jacphi = _fe_cache.evaluateJacobian(q);
587 for (std::size_t dof = 0; dof != node.size(); ++dof) {
588 value += llin_point(node, dof) * phi[dof];
589 gradient += llin_point(node, dof) * (jacphi[dof] * geojacinv)[0];
594 forEachLeafNode(ltest.tree(), [&](
const auto& ltest_node) {
595 if (ltest_node.size() == 0)
598 _local_values_in->get_equation(PDELab::containerEntry(ltrial.tree(), ltest_node.path()));
600 _fe_cache.bind(ltest_node.finiteElement(), quad_rule);
601 const auto& psi = _fe_cache.evaluateFunction(q);
602 const auto& jacpsi = _fe_cache.evaluateJacobian(q);
606 for (const auto& jacobian_entry : eq.reaction.compartment_jacobian) {
607 auto jac = jacobian_entry();
608 const auto& wrt_lbasis = jacobian_entry.wrt.to_local_basis_node(ltrial);
609 _fe_cache.bind(wrt_lbasis.finiteElement(), quad_rule);
610 const auto& phi = _fe_cache.evaluateFunction(q);
611 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i)
612 for (std::size_t dof_j = 0; dof_j != wrt_lbasis.size(); ++dof_j)
613 ljacobian.accumulate(
614 ltest_node, dof_i, wrt_lbasis, dof_j, -jac * phi[dof_i] * psi[dof_j] * factor);
619 auto stg = eq.storage();
620 const auto& wrt_lbasis = eq.to_local_basis_node(ltrial);
621 _fe_cache.bind(wrt_lbasis.finiteElement(), quad_rule);
622 const auto& phi = _fe_cache.evaluateFunction(q);
623 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i)
624 for (std::size_t dof_j = 0; dof_j != wrt_lbasis.size(); ++dof_j)
625 ljacobian.accumulate(
626 ltest_node, dof_i, wrt_lbasis, dof_j, stg * phi[dof_i] * psi[dof_j] * factor);
628 for (const auto& jacobian_entry : eq.storage.compartment_jacobian) {
629 auto jac = jacobian_entry();
630 const auto& wrt_lbasis = jacobian_entry.wrt.to_local_basis_node(ltrial);
631 _fe_cache.bind(wrt_lbasis.finiteElement(), quad_rule);
632 const auto& phi = _fe_cache.evaluateFunction(q);
633 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i)
634 for (std::size_t dof_j = 0; dof_j != wrt_lbasis.size(); ++dof_j)
635 ljacobian.accumulate(ltest_node,
639 jac * eq.value * phi[dof_i] * psi[dof_j] * factor);
644 auto vel = eq.velocity();
645 const auto& wrt_lbasis = eq.to_local_basis_node(ltrial);
646 _fe_cache.bind(wrt_lbasis.finiteElement(), quad_rule);
647 const auto& phi = _fe_cache.evaluateFunction(q);
648 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i) {
649 for (std::size_t dof_j = 0; dof_j != wrt_lbasis.size(); ++dof_j)
650 ljacobian.accumulate(ltest_node,
654 -dot(vel * phi[dof_i][0], (jacpsi[dof_j] * geojacinv)[0]) * factor);
658 for (
const auto& jacobian_entry : eq.velocity.compartment_jacobian) {
659 auto adv_flux = jacobian_entry() * eq.value[0];
660 const auto& jac_wrt_lbasis = jacobian_entry.wrt.to_local_basis_node(ltrial);
661 _fe_cache.bind(jac_wrt_lbasis.finiteElement(), quad_rule);
662 const auto& phi = _fe_cache.evaluateFunction(q);
663 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i)
664 for (std::size_t dof_j = 0; dof_j != jac_wrt_lbasis.size(); ++dof_j)
665 ljacobian.accumulate(ltest_node,
669 -phi[dof_i] * dot(adv_flux, (jacpsi[dof_j] * geojacinv)[0]) * factor);
674 for (
const auto& diffusion : eq.cross_diffusion) {
675 const auto& wrt_lbasis = diffusion.wrt.to_local_basis_node(ltrial);
676 _fe_cache.bind(wrt_lbasis.finiteElement(), quad_rule);
677 const auto& jacphi = _fe_cache.evaluateJacobian(q);
680 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i) {
681 auto diffusive_flux = diffusion((jacphi[dof_i] * geojacinv)[0]);
682 for (std::size_t dof_j = 0; dof_j != wrt_lbasis.size(); ++dof_j)
683 ljacobian.accumulate(
684 ltest_node, dof_i, wrt_lbasis, dof_j, dot(diffusive_flux, (jacpsi[dof_j] * geojacinv)[0]) * factor);
688 for (
const auto& jacobian_entry : diffusion.compartment_jacobian) {
689 auto diffusive_flux = jacobian_entry(jacobian_entry.wrt.gradient);
690 const auto& jac_wrt_lbasis = jacobian_entry.wrt.to_local_basis_node(ltrial);
691 _fe_cache.bind(jac_wrt_lbasis.finiteElement(), quad_rule);
692 const auto& phi = _fe_cache.evaluateFunction(q);
693 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i)
694 for (std::size_t dof_j = 0; dof_j != jac_wrt_lbasis.size(); ++dof_j)
695 ljacobian.accumulate(ltest_node,
699 phi[dof_i] * dot(diffusive_flux, (jacpsi[dof_j] * geojacinv)[0]) * factor);
705 _local_values_in->clear();
706 _local_values_in->in_volume = 0;
709 template<PDELab::Concept::LocalBasis LocalTrial,
710 PDELab::Concept::LocalConstContainer LocalCoeff,
711 PDELab::Concept::LocalBasis LocalTest,
714 const LocalTrial& ltrial,
715 const LocalCoeff& llin_point,
716 const LocalTest& ltest,
717 LocalJac& ljacobian)
noexcept
719 if (ltrial.size() == 0)
724 using LCTrial = PDELab::LocalContainerBuffer<typename LocalTrial::GlobalBasis, typename LocalCoeff::Container>;
725 LCTrial& coeff = value_or_emplace<LCTrial>(_num_jac_cache.coeff_in, ltrial);
728 using LCTest = PDELab::LocalContainerBuffer<typename LocalTest::GlobalBasis, typename LocalCoeff::Container>;
729 LCTest& up = value_or_emplace<LCTrial>(_num_jac_cache.up_in, ltest);
730 LCTest& down = value_or_emplace<LCTrial>(_num_jac_cache.down_in, ltest);
735 localAssembleVolume(time, ltrial, llin_point, ltest, down);
739 forEachLeafNode(ltrial.tree(), [&](
const auto& node) {
740 for (std::size_t dof = 0; dof != node.size(); ++dof)
741 coeff(node, dof) = llin_point(node, dof);
744 forEachLeafNode(ltrial.tree(), [&](
const auto& ltrial_node) {
745 for (std::size_t trail_dof = 0; trail_dof != ltrial_node.size(); ++trail_dof) {
747 auto delta = _fin_diff_epsilon * (1.0 + std::abs(coeff(ltrial_node, trail_dof)));
748 coeff(ltrial_node, trail_dof) += delta;
750 localAssembleVolume(time, ltrial, coeff, ltest, up);
752 forEachLeafNode(ltest.tree(), [&](const auto& ltest_node) {
753 for (std::size_t test_dof = 0; test_dof != ltest_node.size(); ++test_dof) {
754 ljacobian.accumulate(ltest_node,
758 (up(ltest_node, test_dof) - down(ltest_node, test_dof)) / delta);
762 coeff(ltrial_node, trail_dof) = llin_point(ltrial_node, trail_dof);
767 template<
class... Args>
770 if (_do_numerical_jacobian)
771 localAssembleNumericalJacobianVolume(std::forward<Args>(args)...);
773 localAssembleAnalyticalJacobianVolume(std::forward<Args>(args)...);
776 template<PDELab::Concept::LocalBasis LocalBasisTrial, PDELab::Concept::LocalBasis LocalBasisTest>
779 const LocalBasisTrial& ltrial_in,
780 const PDELab::Concept::LocalConstContainer
auto& lcoefficients_in,
781 const LocalBasisTest& ltest_in,
782 const LocalBasisTrial& ltrial_out,
783 const PDELab::Concept::LocalConstContainer
auto& lcoefficients_out,
784 const LocalBasisTest& ltest_out,
785 PDELab::Concept::LocalMutableContainer
auto& lresidual_in,
786 PDELab::Concept::LocalMutableContainer
auto& lresidual_out)
noexcept
788 if (ltrial_in.size() == 0 and ltrial_out.size() == 0)
791 const auto& entity_i = intersection.inside();
792 const auto& geo_i =
move_geometry(time, ltrial_in, lcoefficients_in, _local_values_in, _fe_cache, entity_i.geometry(), std::identity{});
793 const auto& geo_in_i = intersection.geometryInInside();
795 const auto& entity_o = intersection.neighbor() ? intersection.outside() : entity_i;
796 const auto& geo_o =
move_geometry(time, ltrial_out, lcoefficients_out, _local_values_out, _fe_cache, entity_o.geometry(), std::identity{});
797 const auto& geo_in_o = intersection.neighbor() ? intersection.geometryInOutside() : geo_in_i;
799 auto domain_set_i = subDomains(entity_i);
800 auto domain_set_o = subDomains(entity_o);
802 if (intersection.neighbor() and domain_set_i == domain_set_o)
805 auto quad_proj_i = [&](
auto quad_pos){
return geo_in_i.global(quad_pos); };
806 auto quad_proj_o = [&](
auto quad_pos){
return geo_in_o.global(quad_pos); };
808 const auto& geo_f =
move_geometry(time, ltrial_in, lcoefficients_in, _local_values_in, _fe_cache, intersection.geometry(), quad_proj_i);
810 using Geometry = std::decay_t<
decltype(geo_i)>;
811 std::optional<typename Geometry::JacobianInverse> geojacinv_opt_i, geojacinv_opt_o;
813 _local_values_in->time = _local_values_out->time = time;
814 _local_values_in->entity_volume = _local_values_out->entity_volume = geo_f.volume();
815 _local_values_in->in_boundary = _local_values_out->in_boundary =
static_cast<double>(not intersection.neighbor());
816 _local_values_in->in_skeleton = _local_values_out->in_skeleton =
static_cast<double>(intersection.neighbor());
819 if (_grid_cell_data) {
820 _grid_cell_data->getData(entity_i, _local_values_in->cell_values, _local_values_in->cell_mask);
821 _grid_cell_data->getData(entity_o, _local_values_out->cell_values, _local_values_out->cell_mask);
828 if (ltrial_in.size() != 0)
829 forEachLeafNode(ltest_in.tree(), [&](
const auto& ltest_node_in,
auto path) {
833 _local_values_in->get_equation(PDELab::containerEntry(ltrial_in.tree(), path));
834 auto compartment_i = eq.path[Indices::_1];
835 if (ltest_node_in.size() == 0 or eq.outflow.empty())
839 if (intersection.neighbor()) {
840 if (domain_set_o.contains(_compartment2domain[compartment_i]))
842 for (std::size_t compartment_o = 0; compartment_o != _compartment2domain.size();
844 auto domain_o = _compartment2domain[compartment_o];
845 if (compartment_i != compartment_o and
846 (domain_set_o.contains(domain_o) or domain_set_i.contains(domain_o)) and
847 eq.outflow[compartment_o])
848 _outflow_i.emplace_back(eq.outflow[compartment_o], eq);
850 }
else if (eq.outflow[compartment_i]) {
851 _outflow_i.emplace_back(eq.outflow[compartment_i], eq);
856 if (intersection.neighbor() and ltrial_out.size() != 0)
857 forEachLeafNode(ltest_out.tree(), [&](
const auto& ltest_node_out,
auto path) {
861 _local_values_out->get_equation(PDELab::containerEntry(ltrial_out.tree(), path));
862 auto compartment_o = eq.path[Indices::_1];
863 if (ltest_node_out.size() == 0 or
864 domain_set_i.contains(_compartment2domain[compartment_o]) or eq.outflow.empty())
868 for (std::size_t compartment_i = 0; compartment_i != _compartment2domain.size();
870 auto domain_i = _compartment2domain[compartment_i];
871 if (compartment_i != compartment_o and
872 (domain_set_o.contains(domain_i) or domain_set_i.contains(domain_i)) and
873 eq.outflow[compartment_i])
874 _outflow_o.emplace_back(eq.outflow[compartment_i], eq);
878 if (_outflow_i.empty() and _outflow_o.empty())
881 auto intorder = integrationOrder(ltrial_in, ltrial_out);
882 thread_local const auto quad_rule = QuadratureRules<DF, dim-1>::rule(geo_f.type(), intorder);
885 for (std::size_t q = 0; q != quad_rule.size(); ++q) {
886 const auto [position_f, weight] = quad_rule[q];
887 auto factor = weight * geo_f.integrationElement(position_f);
888 auto normal = intersection.unitOuterNormal(position_f);
889 _local_values_in->position = _local_values_out->position = geo_f.global(position_f);
890 _local_values_out->normal = -(_local_values_in->normal = normal);
892 if (not _outflow_i.empty()) {
893 const auto position_i = quad_proj_i(position_f);
894 if (not geojacinv_opt_i or not geo_i.affine())
895 geojacinv_opt_i.emplace(geo_i.jacobianInverse(position_i));
896 const auto& geojacinv_i = *geojacinv_opt_i;
899 forEachLeafNode(ltrial_in.tree(), [&](
const auto& node_in,
auto path) {
900 const auto& node_out = PDELab::containerEntry(ltrial_out.tree(), path);
901 if (node_in.size() == 0 and node_out.size() == 0)
904 const auto& node = (node_in.size() != 0) ? node_in : node_out;
905 const auto& lcoefficients = (node_in.size() != 0) ? lcoefficients_in : lcoefficients_out;
906 auto& value = _local_values_in->get_value(node);
907 auto& gradient = _local_values_in->get_gradient(node);
910 _fe_cache.bind(node.finiteElement(), quad_rule, quad_proj_i, not intersection.conforming());
911 const auto& phi = _fe_cache.evaluateFunction(q);
912 const auto& jacphi = _fe_cache.evaluateJacobian(q);
913 for (std::size_t dof = 0; dof != node.size(); ++dof) {
914 value += lcoefficients(node, dof) * phi[dof];
915 gradient += lcoefficients(node, dof) * (jacphi[dof] * geojacinv_i)[0];
920 for (
const auto& [outflow_i, source_i] : _outflow_i) {
921 auto outflow = std::invoke(outflow_i);
922 const auto& ltest_node_in = source_i.to_local_basis_node(ltest_in);
923 _fe_cache.bind(ltest_node_in.finiteElement(), quad_rule, quad_proj_i, not intersection.conforming());
924 const auto& psi_i = _fe_cache.evaluateFunction(q);
925 for (std::size_t dof = 0; dof != ltest_node_in.size(); ++dof)
926 lresidual_in.accumulate(ltest_node_in, dof, outflow * psi_i[dof] * factor);
930 if (not _outflow_o.empty()) {
931 const auto position_o = quad_proj_o(position_f);
932 if (not geojacinv_opt_o or not geo_o.affine())
933 geojacinv_opt_o.emplace(geo_o.jacobianInverse(position_o));
934 const auto& geojacinv_o = *geojacinv_opt_o;
937 forEachLeafNode(ltrial_out.tree(), [&](
const auto& node_out,
auto path) {
938 const auto& node_in = PDELab::containerEntry(ltrial_in.tree(), path);
940 const auto& node = (node_out.size() != 0) ? node_out : node_in;
941 const auto& lcoefficients = (node_out.size() != 0) ? lcoefficients_out : lcoefficients_in;
942 auto& value = _local_values_out->get_value(node);
943 auto& gradient = _local_values_out->get_gradient(node);
946 _fe_cache.bind(node.finiteElement(), quad_rule, quad_proj_o, not intersection.conforming());
947 const auto& phi = _fe_cache.evaluateFunction(q);
948 const auto& jacphi = _fe_cache.evaluateJacobian(q);
949 for (std::size_t dof = 0; dof != node.size(); ++dof) {
950 value += lcoefficients(node, dof) * phi[dof];
951 gradient += lcoefficients(node, dof) * (jacphi[dof] * geojacinv_o)[0];
956 for (
const auto& [outflow_o, source_o] : _outflow_o) {
957 auto outflow = std::invoke(outflow_o);
958 const auto& ltest_node_out = source_o.to_local_basis_node(ltest_out);
959 _fe_cache.bind(ltest_node_out.finiteElement(), quad_rule, quad_proj_o, not intersection.conforming());
960 const auto& psi_o = _fe_cache.evaluateFunction(q);
961 for (std::size_t dof = 0; dof != ltest_node_out.size(); ++dof)
962 lresidual_out.accumulate(ltest_node_out, dof, outflow * psi_o[dof] * factor);
967 _local_values_in->clear();
968 _local_values_out->clear();
969 _local_values_in->in_boundary = _local_values_in->in_skeleton = 0;
970 _local_values_out->in_boundary = _local_values_out->in_skeleton = 0;
974 const Dune::Concept::Intersection
auto& intersection,
976 const PDELab::Concept::LocalBasis
auto& ltrial_in,
977 const PDELab::Concept::LocalConstContainer
auto& llin_point_in,
978 const PDELab::Concept::LocalBasis
auto& ltest_in,
979 const PDELab::Concept::LocalBasis
auto& ltrial_out,
980 const PDELab::Concept::LocalConstContainer
auto& llin_point_out,
981 const PDELab::Concept::LocalBasis
auto& ltest_out,
982 auto& ljacobian_in_in,
983 auto& ljacobian_in_out,
984 auto& ljacobian_out_in,
985 auto& ljacobian_out_out)
noexcept
987 if (ltrial_in.size() == 0 and ltrial_out.size() == 0)
990 const auto& entity_i = intersection.inside();
992 const auto& geo_i =
move_geometry(time, ltrial_in, llin_point_in, _local_values_in, _fe_cache, entity_i.geometry(), std::identity{});
993 const auto& geo_in_i = intersection.geometryInInside();
995 const auto& entity_o = intersection.neighbor() ? intersection.outside() : entity_i;
996 const auto& geo_in_o = intersection.neighbor() ? intersection.geometryInOutside() : geo_in_i;
997 const auto& geo_o =
move_geometry(time, ltrial_out, llin_point_out, _local_values_out, _fe_cache, entity_o.geometry(), std::identity{});
999 auto domain_set_i = subDomains(entity_i);
1000 auto domain_set_o = subDomains(entity_o);
1002 if (intersection.neighbor() and domain_set_i == domain_set_o)
1005 auto quad_proj_i = [&](
auto quad_pos){
return geo_in_i.global(quad_pos); };
1006 auto quad_proj_o = [&](
auto quad_pos){
return geo_in_o.global(quad_pos); };
1008 const auto& geo_f =
move_geometry(time, ltrial_in, llin_point_in, _local_values_in, _fe_cache, intersection.geometry(), quad_proj_i);
1010 using Geometry = std::decay_t<
decltype(geo_i)>;
1011 std::optional<typename Geometry::JacobianInverse> geojacinv_opt_i, geojacinv_opt_o;
1013 _local_values_in->time = _local_values_out->time = time;
1014 _local_values_in->entity_volume = _local_values_out->entity_volume = geo_f.volume();
1015 _local_values_in->in_boundary = _local_values_out->in_boundary =
static_cast<double>(not intersection.neighbor());
1016 _local_values_in->in_skeleton = _local_values_out->in_skeleton =
static_cast<double>(intersection.neighbor());
1019 if (_grid_cell_data) {
1020 _grid_cell_data->getData(entity_i, _local_values_in->cell_values, _local_values_in->cell_mask);
1021 _grid_cell_data->getData(entity_o, _local_values_out->cell_values, _local_values_out->cell_mask);
1028 if (ltrial_in.size() != 0)
1029 forEachLeafNode(ltest_in.tree(), [&](
const auto& ltest_node_in,
auto path) {
1033 _local_values_in->get_equation(PDELab::containerEntry(ltrial_in.tree(), path));
1034 auto compartment_i = eq.path[Indices::_1];
1035 if (ltest_node_in.size() == 0 or eq.outflow.empty())
1039 if (intersection.neighbor()) {
1040 if (domain_set_o.contains(_compartment2domain[compartment_i]))
1042 for (std::size_t compartment_o = 0; compartment_o != _compartment2domain.size();
1044 auto domain_o = _compartment2domain[compartment_o];
1045 if (compartment_i != compartment_o and
1046 (domain_set_o.contains(domain_o) or domain_set_i.contains(domain_o)) and
1047 eq.outflow[compartment_o])
1048 if (not eq.outflow[compartment_o].compartment_jacobian.empty())
1049 _outflow_i.emplace_back(eq.outflow[compartment_o], eq);
1051 }
else if (eq.outflow[compartment_i]) {
1052 if (not eq.outflow[compartment_i].compartment_jacobian.empty())
1053 _outflow_i.emplace_back(eq.outflow[compartment_i], eq);
1058 if (intersection.neighbor() and ltrial_out.size() != 0)
1059 forEachLeafNode(ltest_out.tree(), [&](
const auto& ltest_node_out,
auto path) {
1063 _local_values_out->get_equation(PDELab::containerEntry(ltrial_out.tree(), path));
1064 auto compartment_o = eq.path[Indices::_1];
1065 if (ltest_node_out.size() == 0 or
1066 domain_set_i.contains(_compartment2domain[compartment_o]) or eq.outflow.empty())
1070 for (std::size_t compartment_i = 0; compartment_i != _compartment2domain.size();
1072 auto domain_i = _compartment2domain[compartment_i];
1073 if (compartment_i != compartment_o and
1074 (domain_set_o.contains(domain_i) or domain_set_i.contains(domain_i)) and
1075 eq.outflow[compartment_i])
1076 if (not eq.outflow[compartment_i].compartment_jacobian.empty())
1077 _outflow_o.emplace_back(eq.outflow[compartment_i], eq);
1081 if (_outflow_i.empty() and _outflow_o.empty())
1084 auto intorder = integrationOrder(ltrial_in, ltrial_out);
1085 thread_local const auto quad_rule = QuadratureRules<DF, dim-1>::rule(geo_f.type(), intorder);
1088 for (std::size_t q = 0; q != quad_rule.size(); ++q) {
1089 const auto [position_f, weight] = quad_rule[q];
1090 auto factor = weight * geo_f.integrationElement(position_f);
1091 auto normal = intersection.unitOuterNormal(position_f);
1092 _local_values_in->position = _local_values_out->position = geo_f.global(position_f);
1093 _local_values_out->normal = -(_local_values_in->normal = normal);
1095 if (not _outflow_i.empty()) {
1096 const auto position_i = quad_proj_i(position_f);
1097 if (not geojacinv_opt_i or not geo_i.affine())
1098 geojacinv_opt_i.emplace(geo_i.jacobianInverse(position_i));
1099 const auto& geojacinv_i = *geojacinv_opt_i;
1102 forEachLeafNode(ltrial_in.tree(), [&](
const auto& node_in,
auto path) {
1103 const auto& node_out = PDELab::containerEntry(ltrial_out.tree(), path);
1104 if (node_in.size() == 0 and node_out.size() == 0)
1107 const auto& node = (node_in.size() != 0) ? node_in : node_out;
1108 const auto& llin_point = (node_in.size() != 0) ? llin_point_in : llin_point_out;
1109 auto& value = _local_values_in->get_value(node);
1110 auto& gradient = _local_values_in->get_gradient(node);
1113 _fe_cache.bind(node.finiteElement(), quad_rule, quad_proj_i, not intersection.conforming());
1114 const auto& phi = _fe_cache.evaluateFunction(q);
1115 const auto& jacphi = _fe_cache.evaluateJacobian(q);
1116 for (std::size_t dof = 0; dof != node.size(); ++dof) {
1117 value += llin_point(node, dof) * phi[dof];
1118 gradient += llin_point(node, dof) * (jacphi[dof] * geojacinv_i)[0];
1123 for (
const auto& [outflow_i, source_i] : _outflow_i) {
1124 const auto& ltest_node_in = source_i.to_local_basis_node(ltest_in);
1125 _fe_cache.bind(ltest_node_in.finiteElement(), quad_rule, quad_proj_i, not intersection.conforming());
1126 const auto& psi = _fe_cache.evaluateFunction(q);
1127 for (
const auto& jacobian_entry : outflow_i.compartment_jacobian) {
1128 auto jac = jacobian_entry();
1129 bool do_self_basis = jacobian_entry.wrt.to_local_basis_node(ltrial_out).size() == 0;
1130 const auto& ltrial = do_self_basis ? ltrial_in : ltrial_out;
1131 auto& ljacobian = do_self_basis ? ljacobian_in_in : ljacobian_in_out;
1132 const auto& wrt_lbasis = jacobian_entry.wrt.to_local_basis_node(ltrial);
1133 _fe_cache.bind(wrt_lbasis.finiteElement(), quad_rule, quad_proj_i, not intersection.conforming());
1134 const auto& phi = _fe_cache.evaluateFunction(q);
1135 for (std::size_t dof_i = 0; dof_i != ltest_node_in.size(); ++dof_i)
1136 for (std::size_t dof_j = 0; dof_j != wrt_lbasis.size(); ++dof_j)
1137 ljacobian.accumulate(ltest_node_in,
1141 jac * phi[dof_i] * psi[dof_j] * factor);
1146 if (not _outflow_o.empty()) {
1147 const auto position_o = quad_proj_o(position_f);
1148 if (not geojacinv_opt_o or not geo_o.affine())
1149 geojacinv_opt_o.emplace(geo_o.jacobianInverse(position_o));
1150 const auto& geojacinv_o = *geojacinv_opt_o;
1153 forEachLeafNode(ltrial_out.tree(), [&](
const auto& node_out,
auto path) {
1154 const auto& node_in = PDELab::containerEntry(ltrial_in.tree(), path);
1156 const auto& node = (node_out.size() != 0) ? node_out : node_in;
1157 const auto& llin_point = (node_out.size() != 0) ? llin_point_out : llin_point_in;
1158 auto& value = _local_values_out->get_value(node);
1159 auto& gradient = _local_values_out->get_gradient(node);
1162 _fe_cache.bind(node.finiteElement(), quad_rule, quad_proj_o, not intersection.conforming());
1163 const auto& phi = _fe_cache.evaluateFunction(q);
1164 const auto& jacphi = _fe_cache.evaluateJacobian(q);
1165 for (std::size_t dof = 0; dof != node.size(); ++dof) {
1166 value += llin_point(node, dof) * phi[dof];
1167 gradient += llin_point(node, dof) * (jacphi[dof] * geojacinv_o)[0];
1171 for (
const auto& [outflow_o, source_o] : _outflow_o) {
1172 const auto& ltest_node_out = source_o.to_local_basis_node(ltest_out);
1173 _fe_cache.bind(ltest_node_out.finiteElement(), quad_rule, quad_proj_o, not intersection.conforming());
1174 const auto& psi = _fe_cache.evaluateFunction(q);
1175 for (
const auto& jacobian_entry : outflow_o.compartment_jacobian) {
1176 auto jac = jacobian_entry();
1177 bool do_self_basis = jacobian_entry.wrt.to_local_basis_node(ltrial_in).size() == 0;
1178 const auto& ltrial = do_self_basis ? ltrial_out : ltrial_in;
1179 auto& ljacobian = do_self_basis ? ljacobian_out_out : ljacobian_out_in;
1180 const auto& wrt_lbasis = jacobian_entry.wrt.to_local_basis_node(ltrial);
1181 _fe_cache.bind(wrt_lbasis.finiteElement(), quad_rule, quad_proj_o, not intersection.conforming());
1182 const auto& phi = _fe_cache.evaluateFunction(q);
1183 for (std::size_t dof_i = 0; dof_i != ltest_node_out.size(); ++dof_i)
1184 for (std::size_t dof_j = 0; dof_j != wrt_lbasis.size(); ++dof_j)
1185 ljacobian.accumulate(ltest_node_out,
1189 jac * phi[dof_i] * psi[dof_j] * factor);
1195 _local_values_in->clear();
1196 _local_values_out->clear();
1197 _local_values_in->in_boundary = _local_values_in->in_skeleton = 0;
1198 _local_values_out->in_boundary = _local_values_out->in_skeleton = 0;
1201 template<PDELab::Concept::LocalBasis LocalTrial,
1202 PDELab::Concept::LocalConstContainer LocalCoeff,
1203 PDELab::Concept::LocalBasis LocalTest,
1207 const LocalTrial& ltrial_in,
1208 const LocalCoeff& llin_point_in,
1209 const LocalTest& ltest_in,
1210 const LocalTrial& ltrial_out,
1211 const LocalCoeff& llin_point_out,
1212 const LocalTest& ltest_out,
1213 LocalJac& ljacobian_in_in,
1214 LocalJac& ljacobian_in_out,
1215 LocalJac& ljacobian_out_in,
1216 LocalJac& ljacobian_out_out)
noexcept
1218 if (ltrial_in.size() == 0 and ltrial_out.size() == 0)
1222 using LCTrial = PDELab::LocalContainerBuffer<typename LocalTrial::GlobalBasis, typename LocalCoeff::Container>;
1223 LCTrial& coeff_in = value_or_emplace<LCTrial>(_num_jac_cache.coeff_in, ltrial_in);
1224 LCTrial& coeff_out = value_or_emplace<LCTrial>(_num_jac_cache.coeff_out, ltrial_out);
1227 using LCTest = PDELab::LocalContainerBuffer<typename LocalTest::GlobalBasis, typename LocalCoeff::Container>;
1228 LCTest& up_in = value_or_emplace<LCTrial>(_num_jac_cache.up_in, ltest_in);
1229 LCTest& up_out = value_or_emplace<LCTrial>(_num_jac_cache.up_out, ltest_out);
1230 LCTest& down_in = value_or_emplace<LCTrial>(_num_jac_cache.down_in, ltest_in);
1231 LCTest& down_out = value_or_emplace<LCTrial>(_num_jac_cache.down_out, ltest_out);
1234 coeff_in.clear(ltrial_in);
1235 forEachLeafNode(ltrial_in.tree(), [&](
const auto& node) {
1236 for (std::size_t dof = 0; dof != node.size(); ++dof)
1237 coeff_in(node, dof) = llin_point_in(node, dof);
1239 coeff_out.clear(ltrial_out);
1240 forEachLeafNode(ltrial_out.tree(), [&](
const auto& node) {
1241 for (std::size_t dof = 0; dof != node.size(); ++dof)
1242 coeff_out(node, dof) = llin_point_out(node, dof);
1245 down_in.clear(ltest_in);
1246 down_out.clear(ltest_out);
1247 localAssembleSkeleton(intersection,
1258 forEachLeafNode(ltrial_in.tree(), [&](
const auto& ltrial_node) {
1259 for (std::size_t trail_dof = 0; trail_dof != ltrial_node.size(); ++trail_dof) {
1260 up_in.clear(ltest_in);
1261 up_out.clear(ltest_out);
1262 auto delta = _fin_diff_epsilon * (1.0 + std::abs(coeff_in(ltrial_node, trail_dof)));
1263 coeff_in(ltrial_node, trail_dof) += delta;
1265 localAssembleSkeleton(intersection,
1276 forEachLeafNode(ltest_in.tree(), [&](const auto& ltest_node) {
1277 for (std::size_t test_dof = 0; test_dof != ltest_node.size(); ++test_dof) {
1278 ljacobian_in_in.accumulate(
1283 (up_in(ltest_node, test_dof) - down_in(ltest_node, test_dof)) / delta);
1286 forEachLeafNode(ltest_out.tree(), [&](const auto& ltest_node) {
1287 for (std::size_t test_dof = 0; test_dof != ltest_node.size(); ++test_dof) {
1288 ljacobian_out_in.accumulate(
1293 (up_out(ltest_node, test_dof) - down_out(ltest_node, test_dof)) / delta);
1297 coeff_in(ltrial_node, trail_dof) = llin_point_in(ltrial_node, trail_dof);
1301 forEachLeafNode(ltrial_out.tree(), [&](
const auto& ltrial_node) {
1302 for (std::size_t trail_dof = 0; trail_dof != ltrial_node.size(); ++trail_dof) {
1303 up_in.clear(ltest_in);
1304 up_out.clear(ltest_out);
1305 auto delta = _fin_diff_epsilon * (1.0 + std::abs(coeff_in(ltrial_node, trail_dof)));
1306 coeff_out(ltrial_node, trail_dof) += delta;
1308 localAssembleSkeleton(intersection,
1319 forEachLeafNode(ltest_in.tree(), [&](const auto& ltest_node) {
1320 for (std::size_t test_dof = 0; test_dof != ltest_node.size(); ++test_dof) {
1321 ljacobian_in_out.accumulate(
1326 (up_in(ltest_node, test_dof) - down_in(ltest_node, test_dof)) / delta);
1329 forEachLeafNode(ltest_out.tree(), [&](const auto& ltest_node) {
1330 for (std::size_t test_dof = 0; test_dof != ltest_node.size(); ++test_dof) {
1331 ljacobian_out_out.accumulate(
1336 (up_out(ltest_node, test_dof) - down_out(ltest_node, test_dof)) / delta);
1340 coeff_out(ltrial_node, trail_dof) = llin_point_out(ltrial_node, trail_dof);
1345 template<
class... Args>
1348 if (_do_numerical_jacobian)
1349 localAssembleNumericalJacobianSkeleton(std::forward<Args>(args)...);
1351 localAssembleAnalyticalJacobianSkeleton(std::forward<Args>(args)...);
1355 const Dune::Concept::Intersection
auto& intersection,
1357 const PDELab::Concept::LocalBasis
auto& ltrial_in,
1358 const PDELab::Concept::LocalConstContainer
auto& llin_point_in,
1359 const PDELab::Concept::LocalConstContainer
auto& lapp_point_in,
1360 const PDELab::Concept::LocalBasis
auto& ltest_in,
1361 const PDELab::Concept::LocalBasis
auto& ltrial_out,
1362 const PDELab::Concept::LocalConstContainer
auto& llin_point_out,
1363 const PDELab::Concept::LocalConstContainer
auto& lapp_point_out,
1364 const PDELab::Concept::LocalBasis
auto& ltest_out,
1365 PDELab::Concept::LocalMutableContainer
auto& ljacobian_in,
1366 PDELab::Concept::LocalMutableContainer
auto& ljacobian_out)
noexcept
1368 PseudoJacobian mat_ii{ ljacobian_in, lapp_point_in };
1369 PseudoJacobian mat_io{ ljacobian_in, lapp_point_out };
1370 PseudoJacobian mat_oi{ ljacobian_out, lapp_point_in };
1371 PseudoJacobian mat_oo{ ljacobian_out, lapp_point_out };
1372 if (localAssembleIsLinear())
1373 localAssembleSkeleton(intersection,
1384 localAssembleJacobianSkeleton(intersection,
1400 const PDELab::Concept::LocalBasis
auto& ltrial_in,
1401 const PDELab::Concept::LocalConstContainer
auto& lcoefficients_in,
1402 const PDELab::Concept::LocalBasis
auto& ltest_in,
1403 PDELab::Concept::LocalMutableContainer
auto& lresidual_in)
noexcept
1405 localAssembleSkeleton(intersection,
1419 const PDELab::Concept::LocalBasis
auto& ltrial_in,
1420 const PDELab::Concept::LocalConstContainer
auto& llin_point_in,
1421 const PDELab::Concept::LocalBasis
auto& ltest_in,
1422 auto& ljacobian_ii)
noexcept
1424 localAssembleJacobianSkeleton(intersection,
1439 const Dune::Concept::Intersection
auto& intersection,
1441 const PDELab::Concept::LocalBasis
auto& ltrial_in,
1442 const PDELab::Concept::LocalConstContainer
auto& llin_point_in,
1443 const PDELab::Concept::LocalConstContainer
auto& lapp_point_in,
1444 const PDELab::Concept::LocalBasis
auto& ltest_in,
1445 PDELab::Concept::LocalMutableContainer
auto& ljacobian_in)
noexcept
1447 PseudoJacobian mat_ii{ ljacobian_in, lapp_point_in };
1448 if (localAssembleIsLinear())
1449 localAssembleBoundary(intersection, time, ltrial_in, lapp_point_in, ltest_in, ljacobian_in);
1451 localAssembleJacobianBoundary(intersection, time, ltrial_in, llin_point_in, ltest_in, mat_ii);
Container for cell data of a grid view.
Definition: cell_data.hh:25
static std::unique_ptr< LocalEquations > make_stiffness(const PDELab::Concept::LocalBasis auto &lbasis, const ParameterTree &eqs_cfg, const ParameterTree &domain_cfg, std::shared_ptr< const FunctorFactory< dim > > functor_factory, std::shared_ptr< const CellData< CellDataGridView, CellDataType > > grid_cell_data)
Definition: local_equations.hh:323
static std::unique_ptr< LocalEquations > make_mass(const PDELab::Concept::LocalBasis auto &lbasis, const ParameterTree &eqs_cfg, const ParameterTree &domain_cfg, std::shared_ptr< const FunctorFactory< dim > > functor_factory, std::shared_ptr< const CellData< CellDataGridView, CellDataType > > grid_cell_data)
Definition: local_equations.hh:341
This class describes a PDELab local operator for diffusion reaction systems.
Definition: local_operator.hh:43
auto executionPolicy() const
Definition: local_operator.hh:156
void localAssembleAnalyticalJacobianVolume(auto time, const PDELab::Concept::LocalBasis auto <rial, const PDELab::Concept::LocalConstContainer auto &llin_point, const PDELab::Concept::LocalBasis auto <est, auto &ljacobian) noexcept
The jacobian volume integral.
Definition: local_operator.hh:541
void localAssembleNumericalJacobianSkeleton(const Dune::Concept::Intersection auto &intersection, auto time, const LocalTrial <rial_in, const LocalCoeff &llin_point_in, const LocalTest <est_in, const LocalTrial <rial_out, const LocalCoeff &llin_point_out, const LocalTest <est_out, LocalJac &ljacobian_in_in, LocalJac &ljacobian_in_out, LocalJac &ljacobian_out_in, LocalJac &ljacobian_out_out) noexcept
Definition: local_operator.hh:1205
void localAssemblePatternVolume(const PDELab::Concept::LocalBasis auto <rial, const PDELab::Concept::LocalBasis auto <est, auto &lpattern) const noexcept
Pattern volume.
Definition: local_operator.hh:276
Form
Definition: local_operator.hh:144
void localAssembleVolume(auto time, const PDELab::Concept::LocalBasis auto <rial, const PDELab::Concept::LocalConstContainer auto &lcoefficients, const PDELab::Concept::LocalBasis auto <est, PDELab::Concept::LocalMutableContainer auto &lresidual) noexcept
The volume integral.
Definition: local_operator.hh:417
static constexpr auto localAssembleDoSkeleton() noexcept
Definition: local_operator.hh:151
void localAssembleJacobianBoundary(const Dune::Concept::Intersection auto &intersection, auto time, const PDELab::Concept::LocalBasis auto <rial_in, const PDELab::Concept::LocalConstContainer auto &llin_point_in, const PDELab::Concept::LocalBasis auto <est_in, auto &ljacobian_ii) noexcept
Definition: local_operator.hh:1417
bool localAssembleSkipIntersection(const Dune::Concept::Intersection auto &intersection) const noexcept
Definition: local_operator.hh:162
bool localAssembleDoBoundary() const noexcept
Definition: local_operator.hh:160
void localAssembleBoundary(const Dune::Concept::Intersection auto &intersection, auto time, const PDELab::Concept::LocalBasis auto <rial_in, const PDELab::Concept::LocalConstContainer auto &lcoefficients_in, const PDELab::Concept::LocalBasis auto <est_in, PDELab::Concept::LocalMutableContainer auto &lresidual_in) noexcept
Definition: local_operator.hh:1398
void localAssembleJacobianSkeleton(Args &&...args) noexcept
Definition: local_operator.hh:1346
LocalOperator(const PDELab::Concept::Basis auto &test_basis, Form lop_type, const ParameterTree &config, std::shared_ptr< const FunctorFactory< dim > > functor_factory, std::shared_ptr< const CellData< CellDataGridView, double > > grid_cell_data, ExecutionPolicy execution_policy={})
Constructs a new instance.
Definition: local_operator.hh:185
bool localAssembleIsLinear() const noexcept
Definition: local_operator.hh:173
void localAssembleSkeleton(const Dune::Concept::Intersection auto &intersection, auto time, const LocalBasisTrial <rial_in, const PDELab::Concept::LocalConstContainer auto &lcoefficients_in, const LocalBasisTest <est_in, const LocalBasisTrial <rial_out, const PDELab::Concept::LocalConstContainer auto &lcoefficients_out, const LocalBasisTest <est_out, PDELab::Concept::LocalMutableContainer auto &lresidual_in, PDELab::Concept::LocalMutableContainer auto &lresidual_out) noexcept
Definition: local_operator.hh:777
static constexpr std::true_type localAssembleDoVolume() noexcept
Definition: local_operator.hh:149
void localAssembleJacobianSkeletonApply(const Dune::Concept::Intersection auto &intersection, auto time, const PDELab::Concept::LocalBasis auto <rial_in, const PDELab::Concept::LocalConstContainer auto &llin_point_in, const PDELab::Concept::LocalConstContainer auto &lapp_point_in, const PDELab::Concept::LocalBasis auto <est_in, const PDELab::Concept::LocalBasis auto <rial_out, const PDELab::Concept::LocalConstContainer auto &llin_point_out, const PDELab::Concept::LocalConstContainer auto &lapp_point_out, const PDELab::Concept::LocalBasis auto <est_out, PDELab::Concept::LocalMutableContainer auto &ljacobian_in, PDELab::Concept::LocalMutableContainer auto &ljacobian_out) noexcept
Definition: local_operator.hh:1354
void localAssemblePatternBoundary(const Dune::Concept::Intersection auto &intersection, const PDELab::Concept::LocalBasis auto <rial_in, const PDELab::Concept::LocalBasis auto <est_in, auto &lpattern_in) noexcept
Definition: local_operator.hh:393
void localAssembleJacobianBoundaryApply(const Dune::Concept::Intersection auto &intersection, auto time, const PDELab::Concept::LocalBasis auto <rial_in, const PDELab::Concept::LocalConstContainer auto &llin_point_in, const PDELab::Concept::LocalConstContainer auto &lapp_point_in, const PDELab::Concept::LocalBasis auto <est_in, PDELab::Concept::LocalMutableContainer auto &ljacobian_in) noexcept
Definition: local_operator.hh:1438
void localAssembleAnalyticalJacobianSkeleton(const Dune::Concept::Intersection auto &intersection, auto time, const PDELab::Concept::LocalBasis auto <rial_in, const PDELab::Concept::LocalConstContainer auto &llin_point_in, const PDELab::Concept::LocalBasis auto <est_in, const PDELab::Concept::LocalBasis auto <rial_out, const PDELab::Concept::LocalConstContainer auto &llin_point_out, const PDELab::Concept::LocalBasis auto <est_out, auto &ljacobian_in_in, auto &ljacobian_in_out, auto &ljacobian_out_in, auto &ljacobian_out_out) noexcept
Definition: local_operator.hh:973
void localAssembleJacobianVolumeApply(auto time, const PDELab::Concept::LocalBasis auto <rial, const PDELab::Concept::LocalConstContainer auto &llin_point, const PDELab::Concept::LocalConstContainer auto &lapp_point, const PDELab::Concept::LocalBasis auto <est, PDELab::Concept::LocalMutableContainer auto &ljacobian) noexcept
The jacobian volume integral for matrix free operations.
Definition: local_operator.hh:510
void localAssemblePatternSkeleton(const Dune::Concept::Intersection auto &intersection, const PDELab::Concept::LocalBasis auto <rial_in, const PDELab::Concept::LocalBasis auto <est_in, const PDELab::Concept::LocalBasis auto <rial_out, const PDELab::Concept::LocalBasis auto <est_out, auto &lpattern_in_in, auto &lpattern_in_out, auto &lpattern_out_in, auto &lpattern_out_out) noexcept
Definition: local_operator.hh:340
void localAssembleJacobianVolume(Args &&... args) noexcept
Definition: local_operator.hh:768
void localAssembleNumericalJacobianVolume(auto time, const LocalTrial <rial, const LocalCoeff &llin_point, const LocalTest <est, LocalJac &ljacobian) noexcept
Definition: local_operator.hh:713
Definition: functor_factory.hh:24
Concept for dune multidomain grids.
Definition: grid.hh:51
Definition: factory.hh:28
auto format_exception(Exception &&e, fmt::format_string< Args... > format, Args &&... args)
Definition: exceptions.hh:23
constexpr std::enable_if_t< is_bitflags_v< Enum >, bool > operator==(Enum lhs, Enum rhs)
Return true if all flags of both enums (allowed to be bitflags) are equal.
Definition: bit_flags.hh:244
Dune::Concept::Geometry auto move_geometry(auto time, const PDELab::Concept::LocalBasis auto &lbasis, const PDELab::Concept::LocalConstContainer auto &lcoeff, auto &local_eqs, auto &fe_cache, const Geometry &geo, auto quad_proj)
Moves a geometry based on the local deformation equations.
Definition: move_geometry.hh:37
Definition: local_equations.hh:173