Dune::Copasi 2.1.0-git79717215+dune.gitlab.629933
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages Concepts
local_operator.hh
Go to the documentation of this file.
1#ifndef DUNE_COPASI_LOCAL_OPERATOR_DIFFUSION_REACTION_CG_HH
2#define DUNE_COPASI_LOCAL_OPERATOR_DIFFUSION_REACTION_CG_HH
3
10
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>
16
17#include <dune/grid/multidomaingrid/singlevalueset.hh>
18
19#include <dune/geometry/type.hh>
20
21#include <dune/common/fvector.hh>
22#include <dune/common/overloadset.hh>
23
25
37template<PDELab::Concept::Basis TestBasis,
38 class LBT,
39 Dune::Concept::GridView CellDataGridView = typename TestBasis::EntitySet,
40 class CellDataType = double,
41 class ExecutionPolicy = PDELab::Execution::SequencedPolicy>
43{
44
45 // utility types
46 using DF = typename LBT::DomainFieldType;
47 using RF = typename LBT::RangeFieldType;
48 static constexpr int dim = LBT::dimDomain;
49
50 using MembraneScalarFunction = typename LocalEquations<dim>::MembraneScalarFunction;
51 using CompartmentNode = typename LocalEquations<dim>::CompartmentNode;
52 struct Outflow
53 {
54 const MembraneScalarFunction& outflow;
55 const CompartmentNode& source;
56 Outflow(const MembraneScalarFunction& outflow, const CompartmentNode& source) : outflow{outflow}, source{source} {}
57 };
58 std::vector<Outflow> _outflow_i;
59 std::vector<Outflow> _outflow_o;
60
61 std::vector<std::size_t> _compartment2domain;
62
63 TestBasis _test_basis;
64
65 bool _is_linear = true;
66 bool _has_outflow = true;
67 bool _do_numerical_jacobian = false;
68 double _fin_diff_epsilon = 1e-7;
69
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;
73 inline static thread_local LocalBasisCache<LBT> _fe_cache = {};
74
75 struct NumericalJacobianCache {
76 std::any coeff_in, coeff_out, up_in, up_out, down_in, down_out;
77 };
78 inline static thread_local NumericalJacobianCache _num_jac_cache = {};
79
80 ExecutionPolicy _execution_policy;
81
82 template<class Value, class... Args>
83 static Value& value_or_emplace(std::any& val, Args&&... args) {
84 if (val.has_value())
85 return std::any_cast<Value&>(val);
86 else
87 return val.template emplace<Value>(std::forward<Args>(args)...);
88 }
89
90 template<class R, class X>
91 struct PseudoJacobian
92 {
93 void accumulate(const auto& ltest,
94 auto test_dof,
95 const auto& ltrial,
96 auto trial_dof,
97 auto value)
98 {
99 _r.accumulate(ltest, test_dof, _z(ltrial, trial_dof) * value);
100 }
101 auto weight() const {return _r.weight(); }
102
103 R& _r;
104 const X& _z;
105 };
106
107 template<class R, class X>
108 PseudoJacobian(R&, const X&) -> PseudoJacobian<R, X>;
109
110 // mono-domains contain all entities, thus all domain sets are the same
111 struct MonoDomainSet
112 {
113 static constexpr std::true_type contains(auto) { return {}; }
114 friend constexpr std::true_type operator==(const MonoDomainSet&, const MonoDomainSet&)
115 {
116 return {};
117 }
118 };
119
120 auto subDomains(const Dune::Concept::Entity auto& entity) const
121 {
123 return _test_basis.entitySet().indexSet().subDomains(entity);
124 else
125 return MonoDomainSet{};
126 }
127
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());
136 });
137 });
138 return intorderadd + quad_factor * order;
139 }
140
141public:
142
143 enum class Form
144 {
145 Stiffness,
146 Mass
147 };
148
149 constexpr static std::true_type localAssembleDoVolume() noexcept { return {}; }
150
151 constexpr static auto localAssembleDoSkeleton() noexcept
152 {
153 return std::bool_constant<Concept::MultiDomainGrid<typename TestBasis::EntitySet::Grid>>{};
154 }
155
156 auto executionPolicy() const {
157 return _execution_policy;
158 }
159
160 bool localAssembleDoBoundary() const noexcept { return _has_outflow; }
161
162 bool localAssembleSkipIntersection(const Dune::Concept::Intersection auto& intersection) const noexcept
163 {
164 // boundary case
165 if (not intersection.neighbor())
166 return false;
167
168 // if domain sets are the same this is not an domain interface and should be
169 // skipped
170 return (subDomains(intersection.inside()) == subDomains(intersection.outside()));
171 }
172
173 bool localAssembleIsLinear() const noexcept { return _is_linear; }
174
185 LocalOperator(const PDELab::Concept::Basis auto& test_basis,
186 Form lop_type,
187 const ParameterTree& config,
188 std::shared_ptr<const FunctorFactory<dim>> functor_factory,
189 std::shared_ptr<const CellData<CellDataGridView, double>> grid_cell_data,
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";
198 else
199 throw format_exception(
200 IOError{}, "The option 'model.jacobian.type' must be either 'analytical' or 'numerical'");
201 }() }
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,
206 _config = config,
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)
213 ptr = LocalEquations<dim>::make_stiffness(_basis.localView(), _config.sub("scalar_field", true), _config.sub("domain"), _functor_factory, _grid_cell_data);
214 if (not ptr)
215 std::terminate();
216 return ptr;
217 })
218 , _local_values_out([_lop_type = lop_type,
219 _basis = _test_basis,
220 _config = config,
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)
227 ptr = LocalEquations<dim>::make_stiffness(_basis.localView(), _config.sub("scalar_field", true), _config.sub("domain"), _functor_factory, _grid_cell_data);
228 if (not ptr)
229 std::terminate();
230 return ptr;
231 })
232 , _execution_policy{ execution_policy }
233 {
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)
240 return;
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();
246 });
247 lbasis.unbind();
248
249 if constexpr (Concept::MultiDomainGrid<typename TestBasis::EntitySet::Grid>)
250 forEachNode(lbasis.tree(),
251 overload(
252 [&](const Concept::CompartmentLocalBasisNode auto& /*ltrial_node*/, auto path) {
253 auto compartment = back(path);
254 _compartment2domain.resize(compartment + 1);
255 _compartment2domain[compartment] =
256 _test_basis.subSpace(path).entitySet().grid().domain();
257 },
258 [&](const auto& /*ltrial_node*/) {}));
259 else
260 _compartment2domain.assign(1, std::numeric_limits<std::size_t>::max());
261 }
262
276 void localAssemblePatternVolume(const PDELab::Concept::LocalBasis auto& ltrial,
277 const PDELab::Concept::LocalBasis auto& ltest,
278 auto& lpattern) const noexcept
279 {
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);
283 if (eq.reaction) {
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);
289 }
290 }
291
292 if (eq.storage) {
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);
301 }
302 }
303
304 if (eq.velocity) {
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);
313 }
314 }
315
316 for (const auto& diffusion : eq.cross_diffusion) {
317 const auto& wrt_lbasis = diffusion.wrt.to_local_basis_node(ltrial);
318
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);
322
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);
328 }
329
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);
335 }
336 }
337 });
338 }
339
340 void localAssemblePatternSkeleton(const Dune::Concept::Intersection auto& intersection,
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
349 {
350 forEachLeafNode(ltest_in.tree(), [&](const auto& ltest_node_in) {
351 if (ltest_node_in.size() == 0)
352 return;
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);
364 }
365 }
366 }
367 });
368
369 if (not intersection.neighbor())
370 return;
371
372 forEachLeafNode(ltest_out.tree(), [&](const auto& ltest_node_out) {
373 if (ltest_node_out.size() == 0)
374 return;
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);
386 }
387 }
388 }
389 });
390
391 }
392
393 void localAssemblePatternBoundary(const Dune::Concept::Intersection auto& intersection,
394 const PDELab::Concept::LocalBasis auto& ltrial_in,
395 const PDELab::Concept::LocalBasis auto& ltest_in,
396 auto& lpattern_in) noexcept
397 {
398 localAssemblePatternSkeleton(intersection, ltrial_in, ltest_in, ltrial_in, ltest_in, lpattern_in, lpattern_in, lpattern_in, lpattern_in);
399 }
400
417 void localAssembleVolume(auto time,
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
422 {
423 if (ltrial.size() == 0)
424 return;
425
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;
430
431 _local_values_in->time = time;
432 _local_values_in->entity_volume = geo.volume();
433 _local_values_in->in_volume = 1;
434
435 // update local values w.r.t grid data
436 if (_grid_cell_data)
437 _grid_cell_data->getData(entity, _local_values_in->cell_values, _local_values_in->cell_mask);
438
439 auto intorder = integrationOrder(ltrial);
440 thread_local const auto quad_rule = QuadratureRules<DF, dim>::rule(geo.type(), intorder);
441
442 // loop over quadrature points
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);
450
451 // evaluate concentrations at quadrature point
452 forEachLeafNode(ltrial.tree(), [&](const auto& node) {
453 if (node.size() == 0)
454 return;
455 auto& value = _local_values_in->get_value(node);
456 auto& gradient = _local_values_in->get_gradient(node);
457 value = 0.;
458 gradient = 0.;
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];
465 }
466 });
467
468 // contribution for each component
469 forEachLeafNode(ltest.tree(), [&](const auto& ltest_node) {
470 if (ltest_node.size() == 0)
471 return;
472 const auto& eq =
473 _local_values_in->get_equation(PDELab::containerEntry(ltrial.tree(), ltest_node.path()));
474
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);
478
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);
486 });
487 }
488
489 _local_values_in->clear();
490 _local_values_in->in_volume = 0;
491 }
492
511 auto time,
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
517
518 {
519 PseudoJacobian mat{ ljacobian, lapp_point };
520 if (localAssembleIsLinear())
521 localAssembleVolume(time, ltrial, lapp_point, ltest, ljacobian);
522 else
523 localAssembleJacobianVolume(time, ltrial, llin_point, ltest, mat);
524 }
525
542 auto time,
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
547 {
548 if (ltrial.size() == 0)
549 return;
550
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;
555
556 _local_values_in->time = time;
557 _local_values_in->entity_volume = geo.volume();
558 _local_values_in->in_volume = 1;
559
560 auto intorder = integrationOrder(ltrial);
561 thread_local const auto quad_rule = QuadratureRules<DF, dim>::rule(geo.type(), intorder);
562
563 // update local values w.r.t grid data
564 if (_grid_cell_data)
565 _grid_cell_data->getData(entity, _local_values_in->cell_values, _local_values_in->cell_mask);
566
567 // loop over quadrature points
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);
575
576 // evaluate concentrations at quadrature point
577 forEachLeafNode(ltrial.tree(), [&](const auto& node) {
578 if (node.size() == 0)
579 return;
580 auto& value = _local_values_in->get_value(node);
581 auto& gradient = _local_values_in->get_gradient(node);
582 value = 0.;
583 gradient = 0.;
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];
590 }
591 });
592
593 // contribution for each component
594 forEachLeafNode(ltest.tree(), [&](const auto& ltest_node) {
595 if (ltest_node.size() == 0)
596 return;
597 const auto& eq =
598 _local_values_in->get_equation(PDELab::containerEntry(ltrial.tree(), ltest_node.path()));
599
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);
603
604 // accumulate reaction part into jacobian
605 if (eq.reaction) {
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);
615 }
616 }
617
618 if (eq.storage) {
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);
627
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,
636 dof_i,
637 wrt_lbasis,
638 dof_j,
639 jac * eq.value * phi[dof_i] * psi[dof_j] * factor);
640 }
641 }
642
643 if (eq.velocity) {
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,
651 dof_i,
652 wrt_lbasis,
653 dof_j,
654 -dot(vel * phi[dof_i][0], (jacpsi[dof_j] * geojacinv)[0]) * factor);
655 }
656
657 // accumulate jacobian for non-linear terms
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,
666 dof_i,
667 jac_wrt_lbasis,
668 dof_j,
669 -phi[dof_i] * dot(adv_flux, (jacpsi[dof_j] * geojacinv)[0]) * factor);
670 }
671 }
672
673 // accumulate cross-diffusion part into jacobian
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);
678 // by product rule
679 // accumulate linear term
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);
685 }
686
687 // accumulate jacobian for non-linear terms
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,
696 dof_i,
697 jac_wrt_lbasis,
698 dof_j,
699 phi[dof_i] * dot(diffusive_flux, (jacpsi[dof_j] * geojacinv)[0]) * factor);
700 }
701 }
702 });
703 }
704
705 _local_values_in->clear();
706 _local_values_in->in_volume = 0;
707 }
708
709 template<PDELab::Concept::LocalBasis LocalTrial,
710 PDELab::Concept::LocalConstContainer LocalCoeff,
711 PDELab::Concept::LocalBasis LocalTest,
712 class LocalJac>
714 const LocalTrial& ltrial,
715 const LocalCoeff& llin_point,
716 const LocalTest& ltest,
717 LocalJac& ljacobian) noexcept
718 {
719 if (ltrial.size() == 0)
720 return;
721
722 // numerical jacobian
723 // get local storage that we can modify...
724 using LCTrial = PDELab::LocalContainerBuffer<typename LocalTrial::GlobalBasis, typename LocalCoeff::Container>;
725 LCTrial& coeff = value_or_emplace<LCTrial>(_num_jac_cache.coeff_in, ltrial);
726
727 // Note LocalCoeff::Container works here because LocalTrial == LocalTest!
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);
731 // pre-compute scaling factor, the weight cancels out in the end if ljacobian is weighted
732
733 // calculate down = f(u)
734 down.clear(ltest);
735 localAssembleVolume(time, ltrial, llin_point, ltest, down);
736
737 // fill coeff with current linearization point
738 coeff.clear(ltrial);
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);
742 });
743
744 forEachLeafNode(ltrial.tree(), [&](const auto& ltrial_node) {
745 for (std::size_t trail_dof = 0; trail_dof != ltrial_node.size(); ++trail_dof) {
746 up.clear(ltest);
747 auto delta = _fin_diff_epsilon * (1.0 + std::abs(coeff(ltrial_node, trail_dof)));
748 coeff(ltrial_node, trail_dof) += delta;
749 // calculate up = f(u+delta)
750 localAssembleVolume(time, ltrial, coeff, ltest, up);
751 // accumulate finite difference
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,
755 test_dof,
756 ltrial_node,
757 trail_dof,
758 (up(ltest_node, test_dof) - down(ltest_node, test_dof)) / delta);
759 }
760 });
761 // reset coefficient vector
762 coeff(ltrial_node, trail_dof) = llin_point(ltrial_node, trail_dof);
763 }
764 });
765 }
766
767 template<class... Args>
768 void localAssembleJacobianVolume(Args&&... args) noexcept
769 {
770 if (_do_numerical_jacobian)
771 localAssembleNumericalJacobianVolume(std::forward<Args>(args)...);
772 else
773 localAssembleAnalyticalJacobianVolume(std::forward<Args>(args)...);
774 }
775
776 template<PDELab::Concept::LocalBasis LocalBasisTrial, PDELab::Concept::LocalBasis LocalBasisTest>
777 void localAssembleSkeleton(const Dune::Concept::Intersection auto& intersection,
778 auto time,
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
787 {
788 if (ltrial_in.size() == 0 and ltrial_out.size() == 0)
789 return;
790
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();
794 // in case of a boundary, outside objects are an alias of the inside ones
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;
798
799 auto domain_set_i = subDomains(entity_i);
800 auto domain_set_o = subDomains(entity_o);
801
802 if (intersection.neighbor() and domain_set_i == domain_set_o)
803 return; // not an intersection case
804
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); };
807
808 const auto& geo_f = move_geometry(time, ltrial_in, lcoefficients_in, _local_values_in, _fe_cache, intersection.geometry(), quad_proj_i);
809
810 using Geometry = std::decay_t<decltype(geo_i)>;
811 std::optional<typename Geometry::JacobianInverse> geojacinv_opt_i, geojacinv_opt_o;
812
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());
817
818 // update local values w.r.t grid data
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);
822 }
823
824 _outflow_i.clear();
825 _outflow_o.clear();
826
827 // collect ouflow part for the inside compartment
828 if (ltrial_in.size() != 0)
829 forEachLeafNode(ltest_in.tree(), [&](const auto& ltest_node_in, auto path) {
830 // evaluate outflow only if component exists on this side but not on
831 // the outside entity
832 const auto& eq =
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())
836 return;
837
838 // accumulate outflow part into residual
839 if (intersection.neighbor()) { // interior skeleton case
840 if (domain_set_o.contains(_compartment2domain[compartment_i]))
841 return;
842 for (std::size_t compartment_o = 0; compartment_o != _compartment2domain.size();
843 ++compartment_o) {
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);
849 }
850 } else if (eq.outflow[compartment_i]) { // boundary case
851 _outflow_i.emplace_back(eq.outflow[compartment_i], eq);
852 }
853 });
854
855 // collect ouflow part for the outside compartment
856 if (intersection.neighbor() and ltrial_out.size() != 0)
857 forEachLeafNode(ltest_out.tree(), [&](const auto& ltest_node_out, auto path) {
858 // evaluate outflow only if component exists on this side but not on
859 // the outside entity
860 const auto& eq =
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())
865 return;
866
867 // accumulate outflow part into residual (interior skeleton case)
868 for (std::size_t compartment_i = 0; compartment_i != _compartment2domain.size();
869 ++compartment_i) {
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);
875 }
876 });
877
878 if (_outflow_i.empty() and _outflow_o.empty())
879 return;
880
881 auto intorder = integrationOrder(ltrial_in, ltrial_out);
882 thread_local const auto quad_rule = QuadratureRules<DF, dim-1>::rule(geo_f.type(), intorder);
883
884 // loop over quadrature points
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);
891
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;
897
898 // evaluate concentrations at quadrature point (inside part)
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)
902 return;
903 // take inside values unless they only exists outside
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);
908 value = 0.;
909 gradient = 0.;
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];
916 }
917 });
918
919 // contribution for each component
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);
927 }
928 }
929
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;
935
936 // evaluate concentrations at quadrature point (outside part)
937 forEachLeafNode(ltrial_out.tree(), [&](const auto& node_out, auto path) {
938 const auto& node_in = PDELab::containerEntry(ltrial_in.tree(), path);
939 // take outside values unless they only exists inside
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);
944 value = 0.;
945 gradient = 0.;
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];
952 }
953 });
954
955 // contribution for each component
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);
963 }
964 }
965 }
966
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;
971 }
972
974 const Dune::Concept::Intersection auto& intersection,
975 auto time,
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
986 {
987 if (ltrial_in.size() == 0 and ltrial_out.size() == 0)
988 return;
989
990 const auto& entity_i = intersection.inside();
991
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();
994 // in case of a boundary, outside objects are an alias of the inside ones
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{});
998
999 auto domain_set_i = subDomains(entity_i);
1000 auto domain_set_o = subDomains(entity_o);
1001
1002 if (intersection.neighbor() and domain_set_i == domain_set_o)
1003 return; // not an intersection case
1004
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); };
1007
1008 const auto& geo_f = move_geometry(time, ltrial_in, llin_point_in, _local_values_in, _fe_cache, intersection.geometry(), quad_proj_i);
1009
1010 using Geometry = std::decay_t<decltype(geo_i)>;
1011 std::optional<typename Geometry::JacobianInverse> geojacinv_opt_i, geojacinv_opt_o;
1012
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());
1017
1018 // update local values w.r.t grid data
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);
1022 }
1023
1024 _outflow_i.clear();
1025 _outflow_o.clear();
1026
1027 // collect ouflow part for the inside compartment
1028 if (ltrial_in.size() != 0)
1029 forEachLeafNode(ltest_in.tree(), [&](const auto& ltest_node_in, auto path) {
1030 // evaluate outflow only if component exists on this side but not on
1031 // the outside entity
1032 const auto& eq =
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())
1036 return;
1037
1038 // accumulate outflow part into residual
1039 if (intersection.neighbor()) { // interior skeleton case
1040 if (domain_set_o.contains(_compartment2domain[compartment_i]))
1041 return;
1042 for (std::size_t compartment_o = 0; compartment_o != _compartment2domain.size();
1043 ++compartment_o) {
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);
1050 }
1051 } else if (eq.outflow[compartment_i]) { // boundary case
1052 if (not eq.outflow[compartment_i].compartment_jacobian.empty())
1053 _outflow_i.emplace_back(eq.outflow[compartment_i], eq);
1054 }
1055 });
1056
1057 // collect ouflow part for the outside compartment
1058 if (intersection.neighbor() and ltrial_out.size() != 0)
1059 forEachLeafNode(ltest_out.tree(), [&](const auto& ltest_node_out, auto path) {
1060 // evaluate outflow only if component exists on this side but not on
1061 // the outside entity
1062 const auto& eq =
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())
1067 return;
1068
1069 // accumulate outflow part into residual (interior skeleton case)
1070 for (std::size_t compartment_i = 0; compartment_i != _compartment2domain.size();
1071 ++compartment_i) {
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);
1078 }
1079 });
1080
1081 if (_outflow_i.empty() and _outflow_o.empty())
1082 return;
1083
1084 auto intorder = integrationOrder(ltrial_in, ltrial_out);
1085 thread_local const auto quad_rule = QuadratureRules<DF, dim-1>::rule(geo_f.type(), intorder);
1086
1087 // loop over quadrature points
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);
1094
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;
1100
1101 // evaluate concentrations at quadrature point (inside part)
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)
1105 return;
1106 // take inside values unless they only exists outside
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);
1111 value = 0.;
1112 gradient = 0.;
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];
1119 }
1120 });
1121
1122 // contribution for each component
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,
1138 dof_i,
1139 wrt_lbasis,
1140 dof_j,
1141 jac * phi[dof_i] * psi[dof_j] * factor);
1142 }
1143 }
1144 }
1145
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;
1151
1152 // evaluate concentrations at quadrature point (outside part)
1153 forEachLeafNode(ltrial_out.tree(), [&](const auto& node_out, auto path) {
1154 const auto& node_in = PDELab::containerEntry(ltrial_in.tree(), path);
1155 // take outside values unless they only exists inside
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);
1160 value = 0.;
1161 gradient = 0.;
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];
1168 }
1169 });
1170
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,
1186 dof_i,
1187 wrt_lbasis,
1188 dof_j,
1189 jac * phi[dof_i] * psi[dof_j] * factor);
1190 }
1191 }
1192 }
1193 }
1194
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;
1199 }
1200
1201 template<PDELab::Concept::LocalBasis LocalTrial,
1202 PDELab::Concept::LocalConstContainer LocalCoeff,
1203 PDELab::Concept::LocalBasis LocalTest,
1204 class LocalJac>
1205 void localAssembleNumericalJacobianSkeleton(const Dune::Concept::Intersection auto& intersection,
1206 auto time,
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
1217 {
1218 if (ltrial_in.size() == 0 and ltrial_out.size() == 0)
1219 return;
1220
1221 // numerical jacobian
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);
1225
1226 // Note LocalCoeff::Container works here because LocalTrial == LocalTest!
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);
1232
1233 // fill coeff with current linearization point
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);
1238 });
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);
1243 });
1244
1245 down_in.clear(ltest_in);
1246 down_out.clear(ltest_out);
1247 localAssembleSkeleton(intersection,
1248 time,
1249 ltrial_in,
1250 coeff_in,
1251 ltest_in,
1252 ltrial_out,
1253 coeff_out,
1254 ltest_out,
1255 down_in,
1256 down_out);
1257
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;
1264 // calculate up = f(u+delta)
1265 localAssembleSkeleton(intersection,
1266 time,
1267 ltrial_in,
1268 coeff_in,
1269 ltest_in,
1270 ltrial_out,
1271 coeff_out,
1272 ltest_out,
1273 up_in,
1274 up_out);
1275 // accumulate finite difference
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(
1279 ltest_node,
1280 test_dof,
1281 ltrial_node,
1282 trail_dof,
1283 (up_in(ltest_node, test_dof) - down_in(ltest_node, test_dof)) / delta);
1284 }
1285 });
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(
1289 ltest_node,
1290 test_dof,
1291 ltrial_node,
1292 trail_dof,
1293 (up_out(ltest_node, test_dof) - down_out(ltest_node, test_dof)) / delta);
1294 }
1295 });
1296 // reset coefficient vector
1297 coeff_in(ltrial_node, trail_dof) = llin_point_in(ltrial_node, trail_dof);
1298 }
1299 });
1300
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;
1307 // calculate up = f(u+delta)
1308 localAssembleSkeleton(intersection,
1309 time,
1310 ltrial_in,
1311 coeff_in,
1312 ltest_in,
1313 ltrial_out,
1314 coeff_out,
1315 ltest_out,
1316 up_in,
1317 up_out);
1318 // accumulate finite difference
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(
1322 ltest_node,
1323 test_dof,
1324 ltrial_node,
1325 trail_dof,
1326 (up_in(ltest_node, test_dof) - down_in(ltest_node, test_dof)) / delta);
1327 }
1328 });
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(
1332 ltest_node,
1333 test_dof,
1334 ltrial_node,
1335 trail_dof,
1336 (up_out(ltest_node, test_dof) - down_out(ltest_node, test_dof)) / delta);
1337 }
1338 });
1339 // reset coefficient vector
1340 coeff_out(ltrial_node, trail_dof) = llin_point_out(ltrial_node, trail_dof);
1341 }
1342 });
1343 }
1344
1345 template<class... Args>
1346 void localAssembleJacobianSkeleton(Args&&...args) noexcept
1347 {
1348 if (_do_numerical_jacobian)
1349 localAssembleNumericalJacobianSkeleton(std::forward<Args>(args)...);
1350 else
1351 localAssembleAnalyticalJacobianSkeleton(std::forward<Args>(args)...);
1352 }
1353
1355 const Dune::Concept::Intersection auto& intersection,
1356 auto time,
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
1367 {
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,
1374 time,
1375 ltrial_in,
1376 lapp_point_in,
1377 ltest_in,
1378 ltrial_out,
1379 lapp_point_out,
1380 ltest_out,
1381 ljacobian_in,
1382 ljacobian_out);
1383 else
1384 localAssembleJacobianSkeleton(intersection,
1385 time,
1386 ltrial_in,
1387 llin_point_in,
1388 ltest_in,
1389 ltrial_out,
1390 llin_point_out,
1391 ltest_out,
1392 mat_ii,
1393 mat_io,
1394 mat_oi,
1395 mat_oo);
1396 }
1397
1398 void localAssembleBoundary(const Dune::Concept::Intersection auto& intersection,
1399 auto time,
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
1404 {
1405 localAssembleSkeleton(intersection,
1406 time,
1407 ltrial_in,
1408 lcoefficients_in,
1409 ltest_in,
1410 ltrial_in,
1411 lcoefficients_in,
1412 ltest_in,
1413 lresidual_in,
1414 lresidual_in);
1415 }
1416
1417 void localAssembleJacobianBoundary(const Dune::Concept::Intersection auto& intersection,
1418 auto time,
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
1423 {
1424 localAssembleJacobianSkeleton(intersection,
1425 time,
1426 ltrial_in,
1427 llin_point_in,
1428 ltest_in,
1429 ltrial_in,
1430 llin_point_in,
1431 ltest_in,
1432 ljacobian_ii,
1433 ljacobian_ii,
1434 ljacobian_ii,
1435 ljacobian_ii);
1436 }
1437
1439 const Dune::Concept::Intersection auto& intersection,
1440 auto time,
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
1446 {
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);
1450 else
1451 localAssembleJacobianBoundary(intersection, time, ltrial_in, llin_point_in, ltest_in, mat_ii);
1452 }
1453};
1454
1455} // namespace Dune::Copasi
1456
1457#endif // DUNE_COPASI_LOCAL_OPERATOR_DIFFUSION_REACTION_CG_HH
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 &ltrial, const PDELab::Concept::LocalConstContainer auto &llin_point, const PDELab::Concept::LocalBasis auto &ltest, auto &ljacobian) noexcept
The jacobian volume integral.
Definition: local_operator.hh:541
void localAssembleNumericalJacobianSkeleton(const Dune::Concept::Intersection auto &intersection, auto time, const LocalTrial &ltrial_in, const LocalCoeff &llin_point_in, const LocalTest &ltest_in, const LocalTrial &ltrial_out, const LocalCoeff &llin_point_out, const LocalTest &ltest_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 &ltrial, const PDELab::Concept::LocalBasis auto &ltest, 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 &ltrial, const PDELab::Concept::LocalConstContainer auto &lcoefficients, const PDELab::Concept::LocalBasis auto &ltest, 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 &ltrial_in, const PDELab::Concept::LocalConstContainer auto &llin_point_in, const PDELab::Concept::LocalBasis auto &ltest_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 &ltrial_in, const PDELab::Concept::LocalConstContainer auto &lcoefficients_in, const PDELab::Concept::LocalBasis auto &ltest_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 &ltrial_in, const PDELab::Concept::LocalConstContainer auto &lcoefficients_in, const LocalBasisTest &ltest_in, const LocalBasisTrial &ltrial_out, const PDELab::Concept::LocalConstContainer auto &lcoefficients_out, const LocalBasisTest &ltest_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 &ltrial_in, const PDELab::Concept::LocalConstContainer auto &llin_point_in, const PDELab::Concept::LocalConstContainer auto &lapp_point_in, const PDELab::Concept::LocalBasis auto &ltest_in, const PDELab::Concept::LocalBasis auto &ltrial_out, const PDELab::Concept::LocalConstContainer auto &llin_point_out, const PDELab::Concept::LocalConstContainer auto &lapp_point_out, const PDELab::Concept::LocalBasis auto &ltest_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 &ltrial_in, const PDELab::Concept::LocalBasis auto &ltest_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 &ltrial_in, const PDELab::Concept::LocalConstContainer auto &llin_point_in, const PDELab::Concept::LocalConstContainer auto &lapp_point_in, const PDELab::Concept::LocalBasis auto &ltest_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 &ltrial_in, const PDELab::Concept::LocalConstContainer auto &llin_point_in, const PDELab::Concept::LocalBasis auto &ltest_in, const PDELab::Concept::LocalBasis auto &ltrial_out, const PDELab::Concept::LocalConstContainer auto &llin_point_out, const PDELab::Concept::LocalBasis auto &ltest_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 &ltrial, const PDELab::Concept::LocalConstContainer auto &llin_point, const PDELab::Concept::LocalConstContainer auto &lapp_point, const PDELab::Concept::LocalBasis auto &ltest, 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 &ltrial_in, const PDELab::Concept::LocalBasis auto &ltest_in, const PDELab::Concept::LocalBasis auto &ltrial_out, const PDELab::Concept::LocalBasis auto &ltest_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 &ltrial, const LocalCoeff &llin_point, const LocalTest &ltest, 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