248 const PDELab::Concept::LocalBasis
auto&
ltest,
252 const auto& ltrial_node = PDELab::containerEntry(ltrial.tree(), ltest_node.path());
253 const auto& eq = _local_values_in->get_equation(ltrial_node);
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);
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);
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);
287 for (
const auto& diffusion : eq.cross_diffusion) {
288 const auto& wrt_lbasis = diffusion.wrt.to_local_basis_node(ltrial);
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);
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);
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
314 forEachLeafNode(ltest_in.tree(), [&](
const auto& ltest_node_in) {
315 if (ltest_node_in.size() == 0)
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);
333 if (not intersection.neighbor())
336 forEachLeafNode(ltest_out.tree(), [&](
const auto& ltest_node_out) {
337 if (ltest_node_out.size() == 0)
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);
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
387 if (ltrial.size() == 0)
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;
395 _local_values_in->time = time;
396 _local_values_in->entity_volume = geo.volume();
397 _local_values_in->in_volume = 1;
401 _grid_cell_data->getData(entity, _local_values_in->cell_values, _local_values_in->cell_mask);
403 auto intorder = integrationOrder(ltrial);
404 const auto& quad_rule = QuadratureRules<DF, dim>::rule(geo.type(), intorder);
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);
416 forEachLeafNode(ltrial.tree(), [&](
const auto& node) {
417 if (node.size() == 0)
419 auto& value = _local_values_in->get_value(node);
420 auto& gradient = _local_values_in->get_gradient(node);
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];
433 forEachLeafNode(ltest.tree(), [&](
const auto& ltest_node) {
434 if (ltest_node.size() == 0)
437 _local_values_in->get_equation(PDELab::containerEntry(ltrial.tree(), ltest_node.path()));
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);
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);
453 _local_values_in->clear();
454 _local_values_in->in_volume = 0;
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
511 if (ltrial.size() == 0)
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;
519 _local_values_in->time = time;
520 _local_values_in->entity_volume = geo.volume();
521 _local_values_in->in_volume = 1;
523 auto intorder = integrationOrder(ltrial);
524 const auto& quad_rule = QuadratureRules<DF, dim>::rule(geo.type(), intorder);
528 _grid_cell_data->getData(entity, _local_values_in->cell_values, _local_values_in->cell_mask);
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);
540 forEachLeafNode(ltrial.tree(), [&](
const auto& node) {
541 if (node.size() == 0)
543 auto& value = _local_values_in->get_value(node);
544 auto& gradient = _local_values_in->get_gradient(node);
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];
557 forEachLeafNode(ltest.tree(), [&](
const auto& ltest_node) {
558 if (ltest_node.size() == 0)
561 _local_values_in->get_equation(PDELab::containerEntry(ltrial.tree(), ltest_node.path()));
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);
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);
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);
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,
602 jac * eq.value * phi[dof_i] * psi[dof_j] * factor);
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,
617 -dot(vel * phi[dof_i][0], (jacpsi[dof_j] * geojacinv)[0]) * factor);
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,
632 -phi[dof_i] * dot(adv_flux, (jacpsi[dof_j] * geojacinv)[0]) * factor);
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);
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);
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,
662 phi[dof_i] * dot(diffusive_flux, (jacpsi[dof_j] * geojacinv)[0]) * factor);
668 _local_values_in->clear();
669 _local_values_in->in_volume = 1;
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
684 if (ltrial_in.size() == 0 and ltrial_out.size() == 0)
687 const auto& entity_i = intersection.inside();
688 const auto& geo_i = entity_i.geometry();
689 const auto& geo_in_i = intersection.geometryInInside();
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();
695 auto domain_set_i = subDomains(entity_i);
696 auto domain_set_o = subDomains(entity_o);
698 if (intersection.neighbor() and domain_set_i == domain_set_o)
701 auto geo_f = intersection.geometry();
703 using Geometry = std::decay_t<
decltype(entity_i.geometry())>;
704 std::optional<typename Geometry::JacobianInverse> geojacinv_opt_i, geojacinv_opt_o;
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());
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);
721 if (ltrial_in.size() != 0)
722 forEachLeafNode(ltest_in.tree(), [&](
const auto& ltest_node_in,
auto path) {
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())
732 if (intersection.neighbor()) {
733 if (domain_set_o.contains(_compartment2domain[compartment_i]))
735 for (std::size_t compartment_o = 0; compartment_o != _compartment2domain.size();
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);
743 }
else if (eq.outflow[compartment_i]) {
744 _outflow_i.emplace_back(eq.outflow[compartment_i], eq);
749 if (intersection.neighbor() and ltrial_out.size() != 0)
750 forEachLeafNode(ltest_out.tree(), [&](
const auto& ltest_node_out,
auto path) {
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())
761 for (std::size_t compartment_i = 0; compartment_i != _compartment2domain.size();
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);
771 if (_outflow_i.empty() and _outflow_o.empty())
774 auto intorder = integrationOrder(ltrial_in, ltrial_out);
775 const auto& quad_rule = QuadratureRules<DF, dim-1>::rule(geo_f.type(), intorder);
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);
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;
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)
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);
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];
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);
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;
832 forEachLeafNode(ltrial_out.tree(), [&](
const auto& node_out,
auto path) {
833 const auto& node_in = PDELab::containerEntry(ltrial_in.tree(), path);
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);
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];
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);
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;
869 const Dune::Concept::Intersection
auto& intersection,
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
882 if (ltrial_in.size() == 0 and ltrial_out.size() == 0)
885 const auto& entity_i = intersection.inside();
886 const auto& geo_i = entity_i.geometry();
887 const auto& geo_in_i = intersection.geometryInInside();
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();
893 auto domain_set_i = subDomains(entity_i);
894 auto domain_set_o = subDomains(entity_o);
896 if (intersection.neighbor() and domain_set_i == domain_set_o)
899 auto geo_f = intersection.geometry();
901 using Geometry = std::decay_t<
decltype(entity_i.geometry())>;
902 std::optional<typename Geometry::JacobianInverse> geojacinv_opt_i, geojacinv_opt_o;
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());
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);
919 if (ltrial_in.size() != 0)
920 forEachLeafNode(ltest_in.tree(), [&](
const auto& ltest_node_in,
auto path) {
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())
930 if (intersection.neighbor()) {
931 if (domain_set_o.contains(_compartment2domain[compartment_i]))
933 for (std::size_t compartment_o = 0; compartment_o != _compartment2domain.size();
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);
942 }
else if (eq.outflow[compartment_i]) {
943 if (not eq.outflow[compartment_i].compartment_jacobian.empty())
944 _outflow_i.emplace_back(eq.outflow[compartment_i], eq);
949 if (intersection.neighbor() and ltrial_out.size() != 0)
950 forEachLeafNode(ltest_out.tree(), [&](
const auto& ltest_node_out,
auto path) {
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())
961 for (std::size_t compartment_i = 0; compartment_i != _compartment2domain.size();
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);
972 if (_outflow_i.empty() and _outflow_o.empty())
975 auto intorder = integrationOrder(ltrial_in, ltrial_out);
976 const auto& quad_rule = QuadratureRules<DF, dim-1>::rule(geo_f.type(), intorder);
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);
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;
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)
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);
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];
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,
1033 jac * phi[dof_i] * psi[dof_j] * factor);
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;
1046 forEachLeafNode(ltrial_out.tree(), [&](
const auto& node_out,
auto path) {
1047 const auto& node_in = PDELab::containerEntry(ltrial_in.tree(), path);
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);
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];
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,
1082 jac * phi[dof_i] * psi[dof_j] * factor);
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;