Dune::Copasi
Loading...
Searching...
No Matches
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
9
10#include <dune/pdelab/common/concurrency/shared_stash.hh>
11#include <dune/pdelab/common/execution.hh>
12#include <dune/pdelab/common/tree_traversal.hh>
13#include <dune/pdelab/operator/local_assembly/archetype.hh>
14#include <dune/pdelab/operator/local_assembly/interface.hh>
15
16#include <dune/grid/multidomaingrid/singlevalueset.hh>
17
18#include <dune/geometry/type.hh>
19
20#include <dune/common/fvector.hh>
21#include <dune/common/overloadset.hh>
22
24
36template<PDELab::Concept::Basis TestBasis,
37 class LBT,
38 Dune::Concept::GridView CellDataGridView = typename TestBasis::EntitySet,
39 class CellDataType = double,
40 class ExecutionPolicy = PDELab::Execution::SequencedPolicy>
42{
43
44 // utility types
45 using DF = typename LBT::DomainFieldType;
46 using RF = typename LBT::RangeFieldType;
47 static constexpr int dim = LBT::dimDomain;
48
49 using MembraneScalarFunction = typename LocalEquations<dim>::MembraneScalarFunction;
50 using CompartmentNode = typename LocalEquations<dim>::CompartmentNode;
51 struct Outflow
52 {
53 const MembraneScalarFunction& outflow;
54 const CompartmentNode& source;
55 Outflow(const MembraneScalarFunction& outflow, const CompartmentNode& source) : outflow{outflow}, source{source} {}
56 };
57 std::vector<Outflow> _outflow_i;
58 std::vector<Outflow> _outflow_o;
59
60 std::vector<std::size_t> _compartment2domain;
61
62 TestBasis _test_basis;
63
64 bool _is_linear = true;
65 bool _has_outflow = true;
66
67 std::shared_ptr<const CellData<CellDataGridView, CellDataType>> _grid_cell_data;
68 PDELab::SharedStash<LocalBasisCache<LBT>> _fe_cache;
69 PDELab::SharedStash<LocalEquations<dim>> _local_values_in;
70 PDELab::SharedStash<LocalEquations<dim>> _local_values_out;
71
72 ExecutionPolicy _execution_policy;
73
74 template<class R, class X>
75 struct PseudoJacobian
76 {
77 void accumulate(const auto& ltest,
78 auto test_dof,
79 const auto& ltrial,
80 auto trial_dof,
81 auto value)
82 {
83 _r.accumulate(ltest, test_dof, _z(ltrial, trial_dof) * value);
84 }
85
86 R& _r;
87 const X& _z;
88 };
89
90 template<class R, class X>
91 PseudoJacobian(R&, const X&) -> PseudoJacobian<R, X>;
92
93 // mono-domains contain all entities, thus all domain sets are the same
94 struct MonoDomainSet
95 {
96 static constexpr std::true_type contains(auto) { return {}; }
97 friend constexpr std::true_type operator==(const MonoDomainSet&, const MonoDomainSet&)
98 {
99 return {};
100 }
101 };
102
103 auto subDomains(const Dune::Concept::Entity auto& entity) const
104 {
106 return _test_basis.entitySet().indexSet().subDomains(entity);
107 else
108 return MonoDomainSet{};
109 }
110
111 std::size_t integrationOrder(const auto&... lbasis_pack) const {
112 std::size_t order = 0;
113 std::size_t intorderadd = 0;
114 std::size_t quad_factor = 2;
115 Hybrid::forEach(std::tie(lbasis_pack...), [&](const auto& lbasis){
116 forEachLeafNode(lbasis.tree(), [&](const auto& node) {
117 if (node.size() != 0)
118 order = std::max<std::size_t>(order, node.finiteElement().localBasis().order());
119 });
120 });
121 return intorderadd + quad_factor * order;
122 }
123
124public:
125
126 enum class Form
127 {
128 Stiffness,
129 Mass
130 };
131
132 constexpr static std::true_type localAssembleDoVolume() noexcept { return {}; }
133
134 constexpr static auto localAssembleDoSkeleton() noexcept
135 {
136 return std::bool_constant<Concept::MultiDomainGrid<typename TestBasis::EntitySet::Grid>>{};
137 }
138
139 auto executionPolicy() const {
140 return _execution_policy;
141 }
142
143 bool localAssembleDoBoundary() const noexcept { return _has_outflow; }
144
145 bool localAssembleSkipIntersection(const Dune::Concept::Intersection auto& intersection) const noexcept
146 {
147 // boundary case
148 if (not intersection.neighbor())
149 return false;
150
151 // if domain sets are the same this is not an domain interface and should be
152 // skipped
153 return (subDomains(intersection.inside()) == subDomains(intersection.outside()));
154 }
155
156 bool localAssembleIsLinear() const noexcept { return _is_linear; }
157
168 LocalOperator(const PDELab::Concept::Basis auto& test_basis,
170 bool is_linear,
171 const ParameterTree& config,
172 std::shared_ptr<const FunctorFactory<dim>> functor_factory,
175 : _test_basis{ test_basis }
176 , _is_linear{ is_linear }
177 , _grid_cell_data{grid_cell_data}
178 , _fe_cache([]() { return std::make_unique<LocalBasisCache<LBT>>(); })
179 , _local_values_in([_lop_type = lop_type,
180 _basis = _test_basis,
181 _config = config,
182 _functor_factory = functor_factory,
183 _grid_cell_data = grid_cell_data]() {
184 std::unique_ptr<LocalEquations<dim>> ptr;
185 if (_lop_type == Form::Mass)
186 ptr = LocalEquations<dim>::make_mass(_basis.localView(), _config, _functor_factory, _grid_cell_data);
187 else if (_lop_type == Form::Stiffness)
188 ptr = LocalEquations<dim>::make_stiffness(_basis.localView(), _config, _functor_factory, _grid_cell_data);
189 if (not ptr)
190 std::terminate();
191 return ptr;
192 })
193 , _local_values_out([_lop_type = lop_type,
194 _basis = _test_basis,
195 _config = config,
196 _functor_factory = std::move(functor_factory),
197 _grid_cell_data = std::move(grid_cell_data)]() {
198 std::unique_ptr<LocalEquations<dim>> ptr;
199 if (_lop_type == Form::Mass)
200 ptr = LocalEquations<dim>::make_mass(_basis.localView(), _config, _functor_factory, _grid_cell_data);
201 else if (_lop_type == Form::Stiffness)
202 ptr = LocalEquations<dim>::make_stiffness(_basis.localView(), _config, _functor_factory, _grid_cell_data);
203 if (not ptr)
204 std::terminate();
205 return ptr;
206 })
207 , _execution_policy{ execution_policy }
208 {
209 auto lbasis = _test_basis.localView();
210 if (_test_basis.entitySet().size(0) == 0)
211 return;
212 lbasis.bind(*_test_basis.entitySet().template begin<0>());
213 _has_outflow = false;
214 forEachLeafNode(lbasis.tree(), [&](const auto& ltrial_node) {
215 const auto& eq = _local_values_in->get_equation(ltrial_node);
216 _has_outflow |= not eq.outflow.empty();
217 });
218 lbasis.unbind();
219
220 if constexpr (Concept::MultiDomainGrid<typename TestBasis::EntitySet::Grid>)
221 forEachNode(lbasis.tree(),
222 overload(
223 [&](const Concept::CompartmentLocalBasisNode auto& /*ltrial_node*/, auto path) {
224 auto compartment = back(path);
225 _compartment2domain.resize(compartment + 1);
226 _compartment2domain[compartment] =
227 _test_basis.subSpace(path).entitySet().grid().domain();
228 },
229 [&](const auto& /*ltrial_node*/) {}));
230 else
231 _compartment2domain.assign(1, std::numeric_limits<std::size_t>::max());
232 }
233
247 void localAssemblePatternVolume(const PDELab::Concept::LocalBasis auto& ltrial,
248 const PDELab::Concept::LocalBasis auto& ltest,
249 auto& lpattern) const noexcept
250 {
251 forEachLeafNode(ltest.tree(), [&](const auto& ltest_node) {
252 const auto& ltrial_node = PDELab::containerEntry(ltrial.tree(), ltest_node.path());
253 const auto& eq = _local_values_in->get_equation(ltrial_node);
254 if (eq.reaction) {
255 for (const auto& jacobian_entry : eq.reaction.compartment_jacobian) {
256 const auto& wrt_lbasis = jacobian_entry.wrt.to_local_basis_node(ltrial);
257 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i)
258 for (std::size_t dof_j = 0; dof_j != wrt_lbasis.size(); ++dof_j)
259 lpattern.addLink(ltest_node, dof_i, wrt_lbasis, dof_j);
260 }
261 }
262
263 if (eq.storage) {
264 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i)
265 for (std::size_t dof_j = 0; dof_j != ltrial_node.size(); ++dof_j)
266 lpattern.addLink(ltest_node, dof_i, ltrial_node, dof_j);
267 for (const auto& jacobian_entry : eq.storage.compartment_jacobian) {
268 const auto& wrt_lbasis = jacobian_entry.wrt.to_local_basis_node(ltrial);
269 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i)
270 for (std::size_t dof_j = 0; dof_j != wrt_lbasis.size(); ++dof_j)
271 lpattern.addLink(ltest_node, dof_i, wrt_lbasis, dof_j);
272 }
273 }
274
275 if (eq.velocity) {
276 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i)
277 for (std::size_t dof_j = 0; dof_j != ltrial_node.size(); ++dof_j)
278 lpattern.addLink(ltest_node, dof_i, ltrial_node, dof_j);
279 for (const auto& jacobian_entry : eq.velocity.compartment_jacobian) {
280 const auto& wrt_lbasis = jacobian_entry.wrt.to_local_basis_node(ltrial);
281 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i)
282 for (std::size_t dof_j = 0; dof_j != wrt_lbasis.size(); ++dof_j)
283 lpattern.addLink(ltest_node, dof_i, wrt_lbasis, dof_j);
284 }
285 }
286
287 for (const auto& diffusion : eq.cross_diffusion) {
288 const auto& wrt_lbasis = diffusion.wrt.to_local_basis_node(ltrial);
289
290 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i)
291 for (std::size_t dof_j = 0; dof_j != wrt_lbasis.size(); ++dof_j)
292 lpattern.addLink(ltest_node, dof_i, wrt_lbasis, dof_j);
293
294 for (const auto& jacobian_entry : diffusion.compartment_jacobian) {
295 const auto& jac_wrt_lbasis = jacobian_entry.wrt.to_local_basis_node(ltrial);
296 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i)
297 for (std::size_t dof_j = 0; dof_j != jac_wrt_lbasis.size(); ++dof_j)
298 lpattern.addLink(ltest_node, dof_i, jac_wrt_lbasis, dof_j);
299 }
300 }
301 });
302 }
303
304 void localAssemblePatternSkeleton(const Dune::Concept::Intersection auto& intersection,
305 const PDELab::Concept::LocalBasis auto& ltrial_in,
306 const PDELab::Concept::LocalBasis auto& ltest_in,
307 const PDELab::Concept::LocalBasis auto& ltrial_out,
308 const PDELab::Concept::LocalBasis auto& ltest_out,
309 auto& lpattern_in_in,
310 auto& lpattern_in_out,
311 auto& lpattern_out_in,
312 auto& lpattern_out_out) noexcept
313 {
314 forEachLeafNode(ltest_in.tree(), [&](const auto& ltest_node_in) {
315 if (ltest_node_in.size() == 0)
316 return;
317 const auto& ltrial_node = PDELab::containerEntry(ltrial_in.tree(), ltest_node_in.path());
318 const auto& eq = _local_values_in->get_equation(ltrial_node);
319 for (const auto& outflow_i : eq.outflow) {
320 for (const auto& jacobian_entry : outflow_i.compartment_jacobian) {
321 const auto& wrt_lbasis_in = jacobian_entry.wrt.to_local_basis_node(ltrial_in);
322 const auto& wrt_lbasis_out = jacobian_entry.wrt.to_local_basis_node(ltrial_out);
323 for (std::size_t dof_i = 0; dof_i != ltest_node_in.size(); ++dof_i) {
324 for (std::size_t dof_j = 0; dof_j != wrt_lbasis_in.size(); ++dof_j)
325 lpattern_in_in.addLink(ltest_node_in, dof_i, wrt_lbasis_in, dof_j);
326 for (std::size_t dof_j = 0; dof_j != wrt_lbasis_out.size(); ++dof_j)
327 lpattern_in_out.addLink(ltest_node_in, dof_i, wrt_lbasis_out, dof_j);
328 }
329 }
330 }
331 });
332
333 if (not intersection.neighbor())
334 return;
335
336 forEachLeafNode(ltest_out.tree(), [&](const auto& ltest_node_out) {
337 if (ltest_node_out.size() == 0)
338 return;
339 const auto& ltrial_node = PDELab::containerEntry(ltrial_out.tree(), ltest_node_out.path());
340 const auto& eq = _local_values_in->get_equation(ltrial_node);
341 for (const auto& outflow_o : eq.outflow) {
342 for (const auto& jacobian_entry : outflow_o.compartment_jacobian) {
343 const auto& wrt_lbasis_in = jacobian_entry.wrt.to_local_basis_node(ltrial_in);
344 const auto& wrt_lbasis_out = jacobian_entry.wrt.to_local_basis_node(ltrial_out);
345 for (std::size_t dof_i = 0; dof_i != ltest_node_out.size(); ++dof_i) {
346 for (std::size_t dof_j = 0; dof_j != wrt_lbasis_in.size(); ++dof_j)
347 lpattern_out_in.addLink(ltest_node_out, dof_i, wrt_lbasis_in, dof_j);
348 for (std::size_t dof_j = 0; dof_j != wrt_lbasis_out.size(); ++dof_j)
349 lpattern_out_out.addLink(ltest_node_out, dof_i, wrt_lbasis_out, dof_j);
350 }
351 }
352 }
353 });
354
355 }
356
357 void localAssemblePatternBoundary(const Dune::Concept::Intersection auto& intersection,
358 const PDELab::Concept::LocalBasis auto& ltrial_in,
359 const PDELab::Concept::LocalBasis auto& ltest_in,
360 auto& lpattern_in) noexcept
361 {
362 localAssemblePatternSkeleton(intersection, ltrial_in, ltest_in, ltrial_in, ltest_in, lpattern_in, lpattern_in, lpattern_in, lpattern_in);
363 }
364
381 void localAssembleVolume(auto time,
382 const PDELab::Concept::LocalBasis auto& ltrial,
383 const PDELab::Concept::LocalConstContainer auto& lcoefficients,
384 const PDELab::Concept::LocalBasis auto& ltest,
385 PDELab::Concept::LocalMutableContainer auto& lresidual) noexcept
386 {
387 if (ltrial.size() == 0)
388 return;
389
390 const auto& entity = ltrial.element();
391 const auto& geo = entity.geometry();
392 using Geometry = std::decay_t<decltype(geo)>;
393 std::optional<typename Geometry::JacobianInverse> geojacinv_opt;
394
395 _local_values_in->time = time;
396 _local_values_in->entity_volume = geo.volume();
397 _local_values_in->in_volume = 1;
398
399 // update local values w.r.t grid data
400 if (_grid_cell_data)
401 _grid_cell_data->getData(entity, _local_values_in->cell_values, _local_values_in->cell_mask);
402
403 auto intorder = integrationOrder(ltrial);
404 const auto& quad_rule = QuadratureRules<DF, dim>::rule(geo.type(), intorder);
405
406 // loop over quadrature points
407 for (std::size_t q = 0; q != quad_rule.size(); ++q) {
408 const auto [position, weight] = quad_rule[q];
409 if (not geojacinv_opt or not geo.affine())
410 geojacinv_opt.emplace(geo.jacobianInverse(position));
411 const auto& geojacinv = *geojacinv_opt;
412 _local_values_in->position = geo.global(position);
413 auto factor = weight * geo.integrationElement(position);
414
415 // evaluate concentrations at quadrature point
416 forEachLeafNode(ltrial.tree(), [&](const auto& node) {
417 if (node.size() == 0)
418 return;
419 auto& value = _local_values_in->get_value(node);
420 auto& gradient = _local_values_in->get_gradient(node);
421 value = 0.;
422 gradient = 0.;
423 _fe_cache->bind(node.finiteElement(), quad_rule);
424 const auto& phi = _fe_cache->evaluateFunction(q);
425 const auto& jacphi = _fe_cache->evaluateJacobian(q);
426 for (std::size_t dof = 0; dof != node.size(); ++dof) {
427 value += lcoefficients(node, dof) * phi[dof];
428 gradient += lcoefficients(node, dof) * (jacphi[dof] * geojacinv)[0];
429 }
430 });
431
432 // contribution for each component
433 forEachLeafNode(ltest.tree(), [&](const auto& ltest_node) {
434 if (ltest_node.size() == 0)
435 return;
436 const auto& eq =
437 _local_values_in->get_equation(PDELab::containerEntry(ltrial.tree(), ltest_node.path()));
438
439 _fe_cache->bind(ltest_node.finiteElement(), quad_rule);
440 const auto& psi = _fe_cache->evaluateFunction(q);
441 const auto& jacpsi = _fe_cache->evaluateJacobian(q);
442
443 RF scalar = eq.reaction ? RF{-eq.reaction()} : 0.;
444 scalar += eq.storage ? RF{eq.value * eq.storage()} : 0.;
445 auto flux = eq.velocity ? eq.velocity() * eq.value[0] : FieldVector<RF,dim>(0.);
446 for (const auto& diffusion : eq.cross_diffusion)
447 flux -= diffusion(diffusion.wrt.gradient);
448 for (std::size_t dof = 0; dof != ltest_node.size(); ++dof)
449 lresidual.accumulate(ltest_node, dof, (scalar * psi[dof] -dot(flux, (jacpsi[dof] * geojacinv)[0] )) * factor);
450 });
451 }
452
453 _local_values_in->clear();
454 _local_values_in->in_volume = 0;
455 }
456
475 auto time,
476 const PDELab::Concept::LocalBasis auto& ltrial,
477 const PDELab::Concept::LocalConstContainer auto& llin_point,
478 const PDELab::Concept::LocalConstContainer auto& lapp_point,
479 const PDELab::Concept::LocalBasis auto& ltest,
480 PDELab::Concept::LocalMutableContainer auto& ljacobian) noexcept
481
482 {
483 PseudoJacobian mat{ ljacobian, lapp_point };
484 if (localAssembleIsLinear())
485 localAssembleVolume(time, ltrial, lapp_point, ltest, ljacobian);
486 else
487 localAssembleJacobianVolume(time, ltrial, llin_point, ltest, mat);
488 }
489
506 const PDELab::Concept::LocalBasis auto& ltrial,
507 const PDELab::Concept::LocalConstContainer auto& llin_point,
508 const PDELab::Concept::LocalBasis auto& ltest,
509 auto& ljacobian) noexcept
510 {
511 if (ltrial.size() == 0)
512 return;
513
514 const auto& entity = ltrial.element();
515 const auto& geo = entity.geometry();
516 using Geometry = std::decay_t<decltype(geo)>;
517 std::optional<typename Geometry::JacobianInverse> geojacinv_opt;
518
519 _local_values_in->time = time;
520 _local_values_in->entity_volume = geo.volume();
521 _local_values_in->in_volume = 1;
522
523 auto intorder = integrationOrder(ltrial);
524 const auto& quad_rule = QuadratureRules<DF, dim>::rule(geo.type(), intorder);
525
526 // update local values w.r.t grid data
527 if (_grid_cell_data)
528 _grid_cell_data->getData(entity, _local_values_in->cell_values, _local_values_in->cell_mask);
529
530 // loop over quadrature points
531 for (std::size_t q = 0; q != quad_rule.size(); ++q) {
532 const auto [position, weight] = quad_rule[q];
533 if (not geojacinv_opt or not geo.affine())
534 geojacinv_opt.emplace(geo.jacobianInverse(position));
535 const auto& geojacinv = *geojacinv_opt;
536 _local_values_in->position = geo.global(position);
537 auto factor = weight * geo.integrationElement(position);
538
539 // evaluate concentrations at quadrature point
540 forEachLeafNode(ltrial.tree(), [&](const auto& node) {
541 if (node.size() == 0)
542 return;
543 auto& value = _local_values_in->get_value(node);
544 auto& gradient = _local_values_in->get_gradient(node);
545 value = 0.;
546 gradient = 0.;
547 _fe_cache->bind(node.finiteElement(), quad_rule);
548 const auto& phi = _fe_cache->evaluateFunction(q);
549 const auto& jacphi = _fe_cache->evaluateJacobian(q);
550 for (std::size_t dof = 0; dof != node.size(); ++dof) {
551 value += llin_point(node, dof) * phi[dof];
552 gradient += llin_point(node, dof) * (jacphi[dof] * geojacinv)[0];
553 }
554 });
555
556 // contribution for each component
557 forEachLeafNode(ltest.tree(), [&](const auto& ltest_node) {
558 if (ltest_node.size() == 0)
559 return;
560 const auto& eq =
561 _local_values_in->get_equation(PDELab::containerEntry(ltrial.tree(), ltest_node.path()));
562
563 _fe_cache->bind(ltest_node.finiteElement(), quad_rule);
564 const auto& psi = _fe_cache->evaluateFunction(q);
565 const auto& jacpsi = _fe_cache->evaluateJacobian(q);
566
567 // accumulate reaction part into jacobian
568 if (eq.reaction) {
569 for (const auto& jacobian_entry : eq.reaction.compartment_jacobian) {
570 auto jac = jacobian_entry();
571 const auto& wrt_lbasis = jacobian_entry.wrt.to_local_basis_node(ltrial);
572 _fe_cache->bind(wrt_lbasis.finiteElement(), quad_rule);
573 const auto& phi = _fe_cache->evaluateFunction(q);
574 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i)
575 for (std::size_t dof_j = 0; dof_j != wrt_lbasis.size(); ++dof_j)
576 ljacobian.accumulate(
577 ltest_node, dof_i, wrt_lbasis, dof_j, -jac * phi[dof_i] * psi[dof_j] * factor);
578 }
579 }
580
581 if (eq.storage) {
582 auto stg = eq.storage();
583 const auto& wrt_lbasis = eq.to_local_basis_node(ltrial);
584 _fe_cache->bind(wrt_lbasis.finiteElement(), quad_rule);
585 const auto& phi = _fe_cache->evaluateFunction(q);
586 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i)
587 for (std::size_t dof_j = 0; dof_j != wrt_lbasis.size(); ++dof_j)
588 ljacobian.accumulate(
589 ltest_node, dof_i, wrt_lbasis, dof_j, stg * phi[dof_i] * psi[dof_j] * factor);
590
591 for (const auto& jacobian_entry : eq.storage.compartment_jacobian) {
592 auto jac = jacobian_entry();
593 const auto& wrt_lbasis = jacobian_entry.wrt.to_local_basis_node(ltrial);
594 _fe_cache->bind(wrt_lbasis.finiteElement(), quad_rule);
595 const auto& phi = _fe_cache->evaluateFunction(q);
596 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i)
597 for (std::size_t dof_j = 0; dof_j != wrt_lbasis.size(); ++dof_j)
598 ljacobian.accumulate(ltest_node,
599 dof_i,
600 wrt_lbasis,
601 dof_j,
602 jac * eq.value * phi[dof_i] * psi[dof_j] * factor);
603 }
604 }
605
606 if (eq.velocity) {
607 auto vel = eq.velocity();
608 const auto& wrt_lbasis = eq.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(ltest_node,
614 dof_i,
615 wrt_lbasis,
616 dof_j,
617 -dot(vel * phi[dof_i][0], (jacpsi[dof_j] * geojacinv)[0]) * factor);
618 }
619
620 // accumulate jacobian for non-linear terms
621 for (const auto& jacobian_entry : eq.velocity.compartment_jacobian) {
622 auto adv_flux = jacobian_entry() * eq.value[0];
623 const auto& jac_wrt_lbasis = jacobian_entry.wrt.to_local_basis_node(ltrial);
624 _fe_cache->bind(jac_wrt_lbasis.finiteElement(), quad_rule);
625 const auto& phi = _fe_cache->evaluateFunction(q);
626 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i)
627 for (std::size_t dof_j = 0; dof_j != jac_wrt_lbasis.size(); ++dof_j)
628 ljacobian.accumulate(ltest_node,
629 dof_i,
630 jac_wrt_lbasis,
631 dof_j,
632 -phi[dof_i] * dot(adv_flux, (jacpsi[dof_j] * geojacinv)[0]) * factor);
633 }
634 }
635
636 // accumulate cross-diffusion part into jacobian
637 for (const auto& diffusion : eq.cross_diffusion) {
638 const auto& wrt_lbasis = diffusion.wrt.to_local_basis_node(ltrial);
639 _fe_cache->bind(wrt_lbasis.finiteElement(), quad_rule);
640 const auto& jacphi = _fe_cache->evaluateJacobian(q);
641 // by product rule
642 // accumulate linear term
643 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i) {
644 auto diffusive_flux = diffusion((jacphi[dof_i] * geojacinv)[0]);
645 for (std::size_t dof_j = 0; dof_j != wrt_lbasis.size(); ++dof_j)
646 ljacobian.accumulate(
647 ltest_node, dof_i, wrt_lbasis, dof_j, dot(diffusive_flux, (jacpsi[dof_j] * geojacinv)[0]) * factor);
648 }
649
650 // accumulate jacobian for non-linear terms
651 for (const auto& jacobian_entry : diffusion.compartment_jacobian) {
652 auto diffusive_flux = jacobian_entry(jacobian_entry.wrt.gradient);
653 const auto& jac_wrt_lbasis = jacobian_entry.wrt.to_local_basis_node(ltrial);
654 _fe_cache->bind(jac_wrt_lbasis.finiteElement(), quad_rule);
655 const auto& phi = _fe_cache->evaluateFunction(q);
656 for (std::size_t dof_i = 0; dof_i != ltest_node.size(); ++dof_i)
657 for (std::size_t dof_j = 0; dof_j != jac_wrt_lbasis.size(); ++dof_j)
658 ljacobian.accumulate(ltest_node,
659 dof_i,
660 jac_wrt_lbasis,
661 dof_j,
662 phi[dof_i] * dot(diffusive_flux, (jacpsi[dof_j] * geojacinv)[0]) * factor);
663 }
664 }
665 });
666 }
667
668 _local_values_in->clear();
669 _local_values_in->in_volume = 1;
670 }
671
672 template<PDELab::Concept::LocalBasis LocalBasisTrial, PDELab::Concept::LocalBasis LocalBasisTest>
673 void localAssembleSkeleton(const Dune::Concept::Intersection auto& intersection,
674 auto time,
675 const LocalBasisTrial& ltrial_in,
676 const PDELab::Concept::LocalConstContainer auto& lcoefficients_in,
677 const LocalBasisTest& ltest_in,
678 const LocalBasisTrial& ltrial_out,
679 const PDELab::Concept::LocalConstContainer auto& lcoefficients_out,
680 const LocalBasisTest& ltest_out,
681 PDELab::Concept::LocalMutableContainer auto& lresidual_in,
682 PDELab::Concept::LocalMutableContainer auto& lresidual_out) noexcept
683 {
684 if (ltrial_in.size() == 0 and ltrial_out.size() == 0)
685 return;
686
687 const auto& entity_i = intersection.inside();
688 const auto& geo_i = entity_i.geometry();
689 const auto& geo_in_i = intersection.geometryInInside();
690 // in case of a boundary, outside objects are an alias of the inside ones
691 const auto& entity_o = intersection.neighbor() ? intersection.outside() : entity_i;
692 const auto& geo_in_o = intersection.neighbor() ? intersection.geometryInOutside() : geo_in_i;
693 const auto& geo_o = entity_o.geometry();
694
695 auto domain_set_i = subDomains(entity_i);
696 auto domain_set_o = subDomains(entity_o);
697
698 if (intersection.neighbor() and domain_set_i == domain_set_o)
699 return; // not an intersection case
700
701 auto geo_f = intersection.geometry();
702
703 using Geometry = std::decay_t<decltype(entity_i.geometry())>;
704 std::optional<typename Geometry::JacobianInverse> geojacinv_opt_i, geojacinv_opt_o;
705
706 _local_values_in->time = _local_values_out->time = time;
707 _local_values_in->entity_volume = _local_values_out->entity_volume = geo_f.volume();
708 _local_values_in->in_boundary = _local_values_out->in_boundary = static_cast<double>(not intersection.neighbor());
709 _local_values_in->in_skeleton = _local_values_out->in_skeleton = static_cast<double>(intersection.neighbor());
710
711 // update local values w.r.t grid data
712 if (_grid_cell_data) {
713 _grid_cell_data->getData(entity_i, _local_values_in->cell_values, _local_values_in->cell_mask);
714 _grid_cell_data->getData(entity_o, _local_values_out->cell_values, _local_values_out->cell_mask);
715 }
716
717 _outflow_i.clear();
718 _outflow_o.clear();
719
720 // collect ouflow part for the inside compartment
721 if (ltrial_in.size() != 0)
722 forEachLeafNode(ltest_in.tree(), [&](const auto& ltest_node_in, auto path) {
723 // evaluate outflow only if component exists on this side but not on
724 // the outside entity
725 const auto& eq =
726 _local_values_in->get_equation(PDELab::containerEntry(ltrial_in.tree(), path));
727 auto compartment_i = eq.path[Indices::_1];
728 if (ltest_node_in.size() == 0 or eq.outflow.empty())
729 return;
730
731 // accumulate outflow part into residual
732 if (intersection.neighbor()) { // interior skeleton case
733 if (domain_set_o.contains(_compartment2domain[compartment_i]))
734 return;
735 for (std::size_t compartment_o = 0; compartment_o != _compartment2domain.size();
736 ++compartment_o) {
737 auto domain_o = _compartment2domain[compartment_o];
738 if (compartment_i != compartment_o and
739 (domain_set_o.contains(domain_o) or domain_set_i.contains(domain_o)) and
740 eq.outflow[compartment_o])
741 _outflow_i.emplace_back(eq.outflow[compartment_o], eq);
742 }
743 } else if (eq.outflow[compartment_i]) { // boundary case
744 _outflow_i.emplace_back(eq.outflow[compartment_i], eq);
745 }
746 });
747
748 // collect ouflow part for the outside compartment
749 if (intersection.neighbor() and ltrial_out.size() != 0)
750 forEachLeafNode(ltest_out.tree(), [&](const auto& ltest_node_out, auto path) {
751 // evaluate outflow only if component exists on this side but not on
752 // the outside entity
753 const auto& eq =
754 _local_values_out->get_equation(PDELab::containerEntry(ltrial_out.tree(), path));
755 auto compartment_o = eq.path[Indices::_1];
756 if (ltest_node_out.size() == 0 or
757 domain_set_i.contains(_compartment2domain[compartment_o]) or eq.outflow.empty())
758 return;
759
760 // accumulate outflow part into residual (interior skeleton case)
761 for (std::size_t compartment_i = 0; compartment_i != _compartment2domain.size();
762 ++compartment_i) {
763 auto domain_i = _compartment2domain[compartment_i];
764 if (compartment_i != compartment_o and
765 (domain_set_o.contains(domain_i) or domain_set_i.contains(domain_i)) and
766 eq.outflow[compartment_i])
767 _outflow_o.emplace_back(eq.outflow[compartment_i], eq);
768 }
769 });
770
771 if (_outflow_i.empty() and _outflow_o.empty())
772 return;
773
774 auto intorder = integrationOrder(ltrial_in, ltrial_out);
775 const auto& quad_rule = QuadratureRules<DF, dim-1>::rule(geo_f.type(), intorder);
776
777 // loop over quadrature points
778 for (std::size_t q = 0; q != quad_rule.size(); ++q) {
779 const auto [position_f, weight] = quad_rule[q];
780 auto factor = weight * geo_f.integrationElement(position_f);
781 auto normal = intersection.unitOuterNormal(position_f);
782 _local_values_in->position = _local_values_out->position = geo_f.global(position_f);
783 _local_values_out->normal = -(_local_values_in->normal = normal);
784
785 if (not _outflow_i.empty()) {
786 auto quad_proj = [&](auto quad_pos){ return geo_in_i.global(quad_pos); };
787 const auto position_i = quad_proj(position_f);
788 if (not geojacinv_opt_i or not geo_i.affine())
789 geojacinv_opt_i.emplace(geo_i.jacobianInverse(position_i));
790 const auto& geojacinv_i = *geojacinv_opt_i;
791
792 // evaluate concentrations at quadrature point (inside part)
793 forEachLeafNode(ltrial_in.tree(), [&](const auto& node_in, auto path) {
794 const auto& node_out = PDELab::containerEntry(ltrial_out.tree(), path);
795 if (node_in.size() == 0 and node_out.size() == 0)
796 return;
797 // take inside values unless they only exists outside
798 const auto& node = (node_in.size() != 0) ? node_in : node_out;
799 const auto& lcoefficients = (node_in.size() != 0) ? lcoefficients_in : lcoefficients_out;
800 auto& value = _local_values_in->get_value(node);
801 auto& gradient = _local_values_in->get_gradient(node);
802 value = 0.;
803 gradient = 0.;
804 _fe_cache->bind(node.finiteElement(), quad_rule, quad_proj, not intersection.conforming());
805 const auto& phi = _fe_cache->evaluateFunction(q);
806 const auto& jacphi = _fe_cache->evaluateJacobian(q);
807 for (std::size_t dof = 0; dof != node.size(); ++dof) {
808 value += lcoefficients(node, dof) * phi[dof];
809 gradient += lcoefficients(node, dof) * (jacphi[dof] * geojacinv_i)[0];
810 }
811 });
812
813 // contribution for each component
814 for (const auto& [outflow_i, source_i] : _outflow_i) {
815 auto outflow = std::invoke(outflow_i);
816 const auto& ltest_node_in = source_i.to_local_basis_node(ltest_in);
817 _fe_cache->bind(ltest_node_in.finiteElement(), quad_rule, quad_proj, not intersection.conforming());
818 const auto& psi_i = _fe_cache->evaluateFunction(q);
819 for (std::size_t dof = 0; dof != ltest_node_in.size(); ++dof)
820 lresidual_in.accumulate(ltest_node_in, dof, outflow * psi_i[dof] * factor);
821 }
822 }
823
824 if (not _outflow_o.empty()) {
825 auto quad_proj = [&](auto quad_pos){ return geo_in_o.global(quad_pos); };
826 const auto position_o = quad_proj(position_f);
827 if (not geojacinv_opt_o or not geo_o.affine())
828 geojacinv_opt_o.emplace(geo_o.jacobianInverse(position_o));
829 const auto& geojacinv_o = *geojacinv_opt_o;
830
831 // evaluate concentrations at quadrature point (outside part)
832 forEachLeafNode(ltrial_out.tree(), [&](const auto& node_out, auto path) {
833 const auto& node_in = PDELab::containerEntry(ltrial_in.tree(), path);
834 // take outside values unless they only exists inside
835 const auto& node = (node_out.size() != 0) ? node_out : node_in;
836 const auto& lcoefficients = (node_out.size() != 0) ? lcoefficients_out : lcoefficients_in;
837 auto& value = _local_values_out->get_value(node);
838 auto& gradient = _local_values_out->get_gradient(node);
839 value = 0.;
840 gradient = 0.;
841 _fe_cache->bind(node.finiteElement(), quad_rule, quad_proj, not intersection.conforming());
842 const auto& phi = _fe_cache->evaluateFunction(q);
843 const auto& jacphi = _fe_cache->evaluateJacobian(q);
844 for (std::size_t dof = 0; dof != node.size(); ++dof) {
845 value += lcoefficients(node, dof) * phi[dof];
846 gradient += lcoefficients(node, dof) * (jacphi[dof] * geojacinv_o)[0];
847 }
848 });
849
850 // contribution for each component
851 for (const auto& [outflow_o, source_o] : _outflow_o) {
852 auto outflow = std::invoke(outflow_o);
853 const auto& ltest_node_out = source_o.to_local_basis_node(ltest_out);
854 _fe_cache->bind(ltest_node_out.finiteElement(), quad_rule, quad_proj, not intersection.conforming());
855 const auto& psi_o = _fe_cache->evaluateFunction(q);
856 for (std::size_t dof = 0; dof != ltest_node_out.size(); ++dof)
857 lresidual_out.accumulate(ltest_node_out, dof, outflow * psi_o[dof] * factor);
858 }
859 }
860 }
861
862 _local_values_in->clear();
863 _local_values_out->clear();
864 _local_values_in->in_boundary = _local_values_in->in_skeleton = 0;
865 _local_values_out->in_boundary = _local_values_out->in_skeleton = 0;
866 }
867
869 const Dune::Concept::Intersection auto& intersection,
870 auto time,
871 const PDELab::Concept::LocalBasis auto& ltrial_in,
872 const PDELab::Concept::LocalConstContainer auto& llin_point_in,
873 const PDELab::Concept::LocalBasis auto& ltest_in,
874 const PDELab::Concept::LocalBasis auto& ltrial_out,
875 const PDELab::Concept::LocalConstContainer auto& llin_point_out,
876 const PDELab::Concept::LocalBasis auto& ltest_out,
877 auto& ljacobian_in_in,
878 auto& ljacobian_in_out,
879 auto& ljacobian_out_in,
880 auto& ljacobian_out_out) noexcept
881 {
882 if (ltrial_in.size() == 0 and ltrial_out.size() == 0)
883 return;
884
885 const auto& entity_i = intersection.inside();
886 const auto& geo_i = entity_i.geometry();
887 const auto& geo_in_i = intersection.geometryInInside();
888 // in case of a boundary, outside objects are an alias of the inside ones
889 const auto& entity_o = intersection.neighbor() ? intersection.outside() : entity_i;
890 const auto& geo_in_o = intersection.neighbor() ? intersection.geometryInOutside() : geo_in_i;
891 const auto& geo_o = entity_o.geometry();
892
893 auto domain_set_i = subDomains(entity_i);
894 auto domain_set_o = subDomains(entity_o);
895
896 if (intersection.neighbor() and domain_set_i == domain_set_o)
897 return; // not an intersection case
898
899 auto geo_f = intersection.geometry();
900
901 using Geometry = std::decay_t<decltype(entity_i.geometry())>;
902 std::optional<typename Geometry::JacobianInverse> geojacinv_opt_i, geojacinv_opt_o;
903
904 _local_values_in->time = _local_values_out->time = time;
905 _local_values_in->entity_volume = _local_values_out->entity_volume = geo_f.volume();
906 _local_values_in->in_boundary = _local_values_out->in_boundary = static_cast<double>(not intersection.neighbor());
907 _local_values_in->in_skeleton = _local_values_out->in_skeleton = static_cast<double>(intersection.neighbor());
908
909 // update local values w.r.t grid data
910 if (_grid_cell_data) {
911 _grid_cell_data->getData(entity_i, _local_values_in->cell_values, _local_values_in->cell_mask);
912 _grid_cell_data->getData(entity_o, _local_values_out->cell_values, _local_values_out->cell_mask);
913 }
914
915 _outflow_i.clear();
916 _outflow_o.clear();
917
918 // collect ouflow part for the inside compartment
919 if (ltrial_in.size() != 0)
920 forEachLeafNode(ltest_in.tree(), [&](const auto& ltest_node_in, auto path) {
921 // evaluate outflow only if component exists on this side but not on
922 // the outside entity
923 const auto& eq =
924 _local_values_in->get_equation(PDELab::containerEntry(ltrial_in.tree(), path));
925 auto compartment_i = eq.path[Indices::_1];
926 if (ltest_node_in.size() == 0 or eq.outflow.empty())
927 return;
928
929 // accumulate outflow part into residual
930 if (intersection.neighbor()) { // interior skeleton case
931 if (domain_set_o.contains(_compartment2domain[compartment_i]))
932 return;
933 for (std::size_t compartment_o = 0; compartment_o != _compartment2domain.size();
934 ++compartment_o) {
935 auto domain_o = _compartment2domain[compartment_o];
936 if (compartment_i != compartment_o and
937 (domain_set_o.contains(domain_o) or domain_set_i.contains(domain_o)) and
938 eq.outflow[compartment_o])
939 if (not eq.outflow[compartment_o].compartment_jacobian.empty())
940 _outflow_i.emplace_back(eq.outflow[compartment_o], eq);
941 }
942 } else if (eq.outflow[compartment_i]) { // boundary case
943 if (not eq.outflow[compartment_i].compartment_jacobian.empty())
944 _outflow_i.emplace_back(eq.outflow[compartment_i], eq);
945 }
946 });
947
948 // collect ouflow part for the outside compartment
949 if (intersection.neighbor() and ltrial_out.size() != 0)
950 forEachLeafNode(ltest_out.tree(), [&](const auto& ltest_node_out, auto path) {
951 // evaluate outflow only if component exists on this side but not on
952 // the outside entity
953 const auto& eq =
954 _local_values_out->get_equation(PDELab::containerEntry(ltrial_out.tree(), path));
955 auto compartment_o = eq.path[Indices::_1];
956 if (ltest_node_out.size() == 0 or
957 domain_set_i.contains(_compartment2domain[compartment_o]) or eq.outflow.empty())
958 return;
959
960 // accumulate outflow part into residual (interior skeleton case)
961 for (std::size_t compartment_i = 0; compartment_i != _compartment2domain.size();
962 ++compartment_i) {
963 auto domain_i = _compartment2domain[compartment_i];
964 if (compartment_i != compartment_o and
965 (domain_set_o.contains(domain_i) or domain_set_i.contains(domain_i)) and
966 eq.outflow[compartment_i])
967 if (not eq.outflow[compartment_i].compartment_jacobian.empty())
968 _outflow_o.emplace_back(eq.outflow[compartment_i], eq);
969 }
970 });
971
972 if (_outflow_i.empty() and _outflow_o.empty())
973 return;
974
975 auto intorder = integrationOrder(ltrial_in, ltrial_out);
976 const auto& quad_rule = QuadratureRules<DF, dim-1>::rule(geo_f.type(), intorder);
977
978 // loop over quadrature points
979 for (std::size_t q = 0; q != quad_rule.size(); ++q) {
980 const auto [position_f, weight] = quad_rule[q];
981 auto factor = weight * geo_f.integrationElement(position_f);
982 auto normal = intersection.unitOuterNormal(position_f);
983 _local_values_in->position = _local_values_out->position = geo_f.global(position_f);
984 _local_values_out->normal = -(_local_values_in->normal = normal);
985
986 if (not _outflow_i.empty()) {
987 auto quad_proj = [&](auto quad_pos){ return geo_in_i.global(quad_pos); };
988 const auto position_i = quad_proj(position_f);
989 if (not geojacinv_opt_i or not geo_i.affine())
990 geojacinv_opt_i.emplace(geo_i.jacobianInverse(position_i));
991 const auto& geojacinv_i = *geojacinv_opt_i;
992
993 // evaluate concentrations at quadrature point (inside part)
994 forEachLeafNode(ltrial_in.tree(), [&](const auto& node_in, auto path) {
995 const auto& node_out = PDELab::containerEntry(ltrial_out.tree(), path);
996 if (node_in.size() == 0 and node_out.size() == 0)
997 return;
998 // take inside values unless they only exists outside
999 const auto& node = (node_in.size() != 0) ? node_in : node_out;
1000 const auto& llin_point = (node_in.size() != 0) ? llin_point_in : llin_point_out;
1001 auto& value = _local_values_in->get_value(node);
1002 auto& gradient = _local_values_in->get_gradient(node);
1003 value = 0.;
1004 gradient = 0.;
1005 _fe_cache->bind(node.finiteElement(), quad_rule, quad_proj, not intersection.conforming());
1006 const auto& phi = _fe_cache->evaluateFunction(q);
1007 const auto& jacphi = _fe_cache->evaluateJacobian(q);
1008 for (std::size_t dof = 0; dof != node.size(); ++dof) {
1009 value += llin_point(node, dof) * phi[dof];
1010 gradient += llin_point(node, dof) * (jacphi[dof] * geojacinv_i)[0];
1011 }
1012 });
1013
1014 // contribution for each component
1015 for (const auto& [outflow_i, source_i] : _outflow_i) {
1016 const auto& ltest_node_in = source_i.to_local_basis_node(ltest_in);
1017 _fe_cache->bind(ltest_node_in.finiteElement(), quad_rule, quad_proj, not intersection.conforming());
1018 const auto& psi = _fe_cache->evaluateFunction(q);
1019 for (const auto& jacobian_entry : outflow_i.compartment_jacobian) {
1020 auto jac = jacobian_entry();
1021 bool do_self_basis = jacobian_entry.wrt.to_local_basis_node(ltrial_out).size() == 0;
1022 const auto& ltrial = do_self_basis ? ltrial_in : ltrial_out;
1023 auto& ljacobian = do_self_basis ? ljacobian_in_in : ljacobian_in_out;
1024 const auto& wrt_lbasis = jacobian_entry.wrt.to_local_basis_node(ltrial);
1025 _fe_cache->bind(wrt_lbasis.finiteElement(), quad_rule, quad_proj, not intersection.conforming());
1026 const auto& phi = _fe_cache->evaluateFunction(q);
1027 for (std::size_t dof_i = 0; dof_i != ltest_node_in.size(); ++dof_i)
1028 for (std::size_t dof_j = 0; dof_j != wrt_lbasis.size(); ++dof_j)
1029 ljacobian.accumulate(ltest_node_in,
1030 dof_i,
1031 wrt_lbasis,
1032 dof_j,
1033 jac * phi[dof_i] * psi[dof_j] * factor);
1034 }
1035 }
1036 }
1037
1038 if (not _outflow_o.empty()) {
1039 auto quad_proj = [&](auto quad_pos){ return geo_in_o.global(quad_pos); };
1040 const auto position_o = quad_proj(position_f);
1041 if (not geojacinv_opt_o or not geo_o.affine())
1042 geojacinv_opt_o.emplace(geo_o.jacobianInverse(position_o));
1043 const auto& geojacinv_o = *geojacinv_opt_o;
1044
1045 // evaluate concentrations at quadrature point (outside part)
1046 forEachLeafNode(ltrial_out.tree(), [&](const auto& node_out, auto path) {
1047 const auto& node_in = PDELab::containerEntry(ltrial_in.tree(), path);
1048 // take outside values unless they only exists inside
1049 const auto& node = (node_out.size() != 0) ? node_out : node_in;
1050 const auto& llin_point = (node_out.size() != 0) ? llin_point_out : llin_point_in;
1051 auto& value = _local_values_out->get_value(node);
1052 auto& gradient = _local_values_out->get_gradient(node);
1053 value = 0.;
1054 gradient = 0.;
1055 _fe_cache->bind(node.finiteElement(), quad_rule, quad_proj, not intersection.conforming());
1056 const auto& phi = _fe_cache->evaluateFunction(q);
1057 const auto& jacphi = _fe_cache->evaluateJacobian(q);
1058 for (std::size_t dof = 0; dof != node.size(); ++dof) {
1059 value += llin_point(node, dof) * phi[dof];
1060 gradient += llin_point(node, dof) * (jacphi[dof] * geojacinv_o)[0];
1061 }
1062 });
1063
1064 for (const auto& [outflow_o, source_o] : _outflow_o) {
1065 const auto& ltest_node_out = source_o.to_local_basis_node(ltest_out);
1066 _fe_cache->bind(ltest_node_out.finiteElement(), quad_rule, quad_proj, not intersection.conforming());
1067 const auto& psi = _fe_cache->evaluateFunction(q);
1068 for (const auto& jacobian_entry : outflow_o.compartment_jacobian) {
1069 auto jac = jacobian_entry();
1070 bool do_self_basis = jacobian_entry.wrt.to_local_basis_node(ltrial_in).size() == 0;
1071 const auto& ltrial = do_self_basis ? ltrial_out : ltrial_in;
1072 auto& ljacobian = do_self_basis ? ljacobian_out_out : ljacobian_out_in;
1073 const auto& wrt_lbasis = jacobian_entry.wrt.to_local_basis_node(ltrial);
1074 _fe_cache->bind(wrt_lbasis.finiteElement(), quad_rule, quad_proj, not intersection.conforming());
1075 const auto& phi = _fe_cache->evaluateFunction(q);
1076 for (std::size_t dof_i = 0; dof_i != ltest_node_out.size(); ++dof_i)
1077 for (std::size_t dof_j = 0; dof_j != wrt_lbasis.size(); ++dof_j)
1078 ljacobian.accumulate(ltest_node_out,
1079 dof_i,
1080 wrt_lbasis,
1081 dof_j,
1082 jac * phi[dof_i] * psi[dof_j] * factor);
1083 }
1084 }
1085 }
1086 }
1087
1088 _local_values_in->clear();
1089 _local_values_out->clear();
1090 _local_values_in->in_boundary = _local_values_in->in_skeleton = 0;
1091 _local_values_out->in_boundary = _local_values_out->in_skeleton = 0;
1092 }
1093
1095 const Dune::Concept::Intersection auto& intersection,
1096 auto time,
1097 const PDELab::Concept::LocalBasis auto& ltrial_in,
1098 const PDELab::Concept::LocalConstContainer auto& llin_point_in,
1099 const PDELab::Concept::LocalConstContainer auto& lapp_point_in,
1100 const PDELab::Concept::LocalBasis auto& ltest_in,
1101 const PDELab::Concept::LocalBasis auto& ltrial_out,
1102 const PDELab::Concept::LocalConstContainer auto& llin_point_out,
1103 const PDELab::Concept::LocalConstContainer auto& lapp_point_out,
1104 const PDELab::Concept::LocalBasis auto& ltest_out,
1105 PDELab::Concept::LocalMutableContainer auto& ljacobian_in,
1106 PDELab::Concept::LocalMutableContainer auto& ljacobian_out) noexcept
1107 {
1108 PseudoJacobian mat_ii{ ljacobian_in, lapp_point_in };
1109 PseudoJacobian mat_io{ ljacobian_in, lapp_point_out };
1110 PseudoJacobian mat_oi{ ljacobian_out, lapp_point_in };
1111 PseudoJacobian mat_oo{ ljacobian_out, lapp_point_out };
1112 if (localAssembleIsLinear())
1113 localAssembleSkeleton(intersection,
1114 time,
1115 ltrial_in,
1116 lapp_point_in,
1117 ltest_in,
1118 ltrial_out,
1119 lapp_point_out,
1120 ltest_out,
1121 ljacobian_in,
1122 ljacobian_out);
1123 else
1124 localAssembleJacobianSkeleton(intersection,
1125 time,
1126 ltrial_in,
1127 llin_point_in,
1128 ltest_in,
1129 ltrial_out,
1130 llin_point_out,
1131 ltest_out,
1132 mat_ii,
1133 mat_io,
1134 mat_oi,
1135 mat_oo);
1136 }
1137
1138 void localAssembleBoundary(const Dune::Concept::Intersection auto& intersection,
1139 auto time,
1140 const PDELab::Concept::LocalBasis auto& ltrial_in,
1141 const PDELab::Concept::LocalConstContainer auto& lcoefficients_in,
1142 const PDELab::Concept::LocalBasis auto& ltest_in,
1143 PDELab::Concept::LocalMutableContainer auto& lresidual_in) noexcept
1144 {
1145 localAssembleSkeleton(intersection,
1146 time,
1147 ltrial_in,
1148 lcoefficients_in,
1149 ltest_in,
1150 ltrial_in,
1151 lcoefficients_in,
1152 ltest_in,
1153 lresidual_in,
1154 lresidual_in);
1155 }
1156
1157 void localAssembleJacobianBoundary(const Dune::Concept::Intersection auto& intersection,
1158 auto time,
1159 const PDELab::Concept::LocalBasis auto& ltrial_in,
1160 const PDELab::Concept::LocalConstContainer auto& llin_point_in,
1161 const PDELab::Concept::LocalBasis auto& ltest_in,
1162 auto& ljacobian_ii) noexcept
1163 {
1164 localAssembleJacobianSkeleton(intersection,
1165 time,
1166 ltrial_in,
1167 llin_point_in,
1168 ltest_in,
1169 ltrial_in,
1170 llin_point_in,
1171 ltest_in,
1172 ljacobian_ii,
1173 ljacobian_ii,
1174 ljacobian_ii,
1175 ljacobian_ii);
1176 }
1177
1179 const Dune::Concept::Intersection auto& intersection,
1180 auto time,
1181 const PDELab::Concept::LocalBasis auto& ltrial_in,
1182 const PDELab::Concept::LocalConstContainer auto& llin_point_in,
1183 const PDELab::Concept::LocalConstContainer auto& lapp_point_in,
1184 const PDELab::Concept::LocalBasis auto& ltest_in,
1185 PDELab::Concept::LocalMutableContainer auto& ljacobian_in) noexcept
1186 {
1187 PseudoJacobian mat_ii{ ljacobian_in, lapp_point_in };
1188 if (localAssembleIsLinear())
1189 localAssembleBoundary(intersection, time, ltrial_in, lapp_point_in, ltest_in, ljacobian_in);
1190 else
1191 localAssembleJacobianBoundary(intersection, time, ltrial_in, llin_point_in, ltest_in, mat_ii);
1192 }
1193};
1194
1195} // namespace Dune::Copasi
1196
1197#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 &config, std::shared_ptr< const FunctorFactory< dim > > functor_factory, std::shared_ptr< const CellData< CellDataGridView, CellDataType > > grid_cell_data)
Definition local_equations.hh:307
static std::unique_ptr< LocalEquations > make_mass(const PDELab::Concept::LocalBasis auto &lbasis, const ParameterTree &config, std::shared_ptr< const FunctorFactory< dim > > functor_factory, std::shared_ptr< const CellData< CellDataGridView, CellDataType > > grid_cell_data)
Definition local_equations.hh:323
This class describes a PDELab local operator for diffusion reaction systems.
Definition local_operator.hh:42
LocalOperator(const PDELab::Concept::Basis auto &test_basis, Form lop_type, bool is_linear, 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:168
auto executionPolicy() const
Definition local_operator.hh:139
void localAssemblePatternVolume(const PDELab::Concept::LocalBasis auto &ltrial, const PDELab::Concept::LocalBasis auto &ltest, auto &lpattern) const noexcept
Pattern volume.
Definition local_operator.hh:247
Form
Definition local_operator.hh:127
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:381
static constexpr auto localAssembleDoSkeleton() noexcept
Definition local_operator.hh:134
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:1157
bool localAssembleSkipIntersection(const Dune::Concept::Intersection auto &intersection) const noexcept
Definition local_operator.hh:145
bool localAssembleDoBoundary() const noexcept
Definition local_operator.hh:143
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:1138
bool localAssembleIsLinear() const noexcept
Definition local_operator.hh:156
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:673
static constexpr std::true_type localAssembleDoVolume() noexcept
Definition local_operator.hh:132
void localAssembleJacobianSkeleton(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:868
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:1094
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:357
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:1178
void localAssembleJacobianVolume(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:505
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:474
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:304
Definition functor_factory.hh:24
Concept for dune multidomain grids.
Definition grid.hh:51
Definition factory.hh:28
constexpr bool is_bitflags_v
Alias for Bitflag indicator.
Definition bit_flags.hh:24