69 using Scalar =
typename MDTraits::Scalar;
70 using SolutionVector =
typename MDTraits::SolutionVector;
71 using PointSourceData =
typename ParentType::PointSourceTraits::PointSourceData;
73 static constexpr auto bulkIdx =
typename MDTraits::template SubDomain<0>::Index();
74 static constexpr auto lowDimIdx =
typename MDTraits::template SubDomain<1>::Index();
77 template<std::
size_t id>
using SubDomainTypeTag =
typename MDTraits::template SubDomain<id>::TypeTag;
80 template<std::
size_t id>
using GridView =
typename GridGeometry<id>::GridView;
81 template<std::
size_t id>
using Element =
typename GridView<id>::template Codim<0>::Entity;
84 using GlobalPosition =
typename Element<bulkIdx>::Geometry::GlobalCoordinate;
86 template<std::
size_t id>
87 static constexpr bool isBox()
90 static_assert(!isBox<bulkIdx>() && !isBox<lowDimIdx>(),
"The kernel coupling method is only implemented for the tpfa method");
91 static_assert(Dune::Capabilities::isCartesian<typename GridView<bulkIdx>::Grid>::v,
"The kernel coupling method is only implemented for structured grids");
94 bulkDim = GridView<bulkIdx>::dimension,
95 lowDimDim = GridView<lowDimIdx>::dimension,
96 dimWorld = GridView<bulkIdx>::dimensionworld
100 template <
typename T,
typename ...Ts>
101 using VariableKernelWidthDetector =
decltype(std::declval<T>().kernelWidthFactor(std::declval<Ts>()...));
103 template<
class T,
typename ...Args>
104 static constexpr bool hasKernelWidthFactor()
105 {
return Dune::Std::is_detected<VariableKernelWidthDetector, T, Args...>::value; }
110 using ParentType::ParentType;
112 void init(std::shared_ptr<Problem<bulkIdx>> bulkProblem,
113 std::shared_ptr<Problem<lowDimIdx>> lowDimProblem,
114 const SolutionVector&
curSol)
121 DUNE_THROW(Dune::NotImplemented,
"The current intersection detection may likely fail for refined grids.");
130 template<std::
size_t id,
class JacobianPattern>
133 extendedSourceStencil_.extendJacobianPattern(*
this, domainI, pattern);
143 template<std::
size_t i,
class LocalAssemblerI,
class JacobianMatrixDiagBlock,
class Gr
idVariables>
145 const LocalAssemblerI& localAssemblerI,
146 const typename LocalAssemblerI::LocalResidual::ElementResidualVector&,
147 JacobianMatrixDiagBlock& A,
148 GridVariables& gridVariables)
150 extendedSourceStencil_.evalAdditionalDomainDerivatives(*
this, domainI, localAssemblerI, A, gridVariables);
167 std::cout <<
"[coupling] Initializing the integration point source data structures..." << std::endl;
170 prepareDataStructures_();
171 std::cout <<
"[coupling] Resized data structures." << std::endl;
173 const auto& bulkGridGeometry = this->
problem(bulkIdx).gridGeometry();
174 const auto& lowDimGridGeometry = this->
problem(lowDimIdx).gridGeometry();
178 static const double characteristicRelativeLength =
getParam<double>(
"MixedDimension.KernelIntegrationCRL", 0.1);
181 static const bool writeIntegrationPointsToFile =
getParam<bool>(
"MixedDimension.WriteIntegrationPointsToFile",
false);
182 if (writeIntegrationPointsToFile)
184 std::ofstream ipPointFile(
"kernel_points.log", std::ios::trunc);
185 ipPointFile <<
"x,y,z\n";
186 std::cout <<
"[coupling] Initialized kernel_points.log." << std::endl;
189 for (
const auto& is : intersections(this->
glue()))
192 const auto& inside = is.targetEntity(0);
194 const auto intersectionGeometry = is.geometry();
195 const auto lowDimElementIdx = lowDimGridGeometry.elementMapper().index(inside);
200 const auto radius = this->
problem(lowDimIdx).spatialParams().radius(lowDimElementIdx);
201 const auto kernelWidthFactor = kernelWidthFactor_(this->
problem(lowDimIdx).spatialParams(), lowDimElementIdx);
202 const auto kernelWidth = kernelWidthFactor*
radius;
203 const auto a = intersectionGeometry.corner(0);
204 const auto b = intersectionGeometry.corner(1);
208 for (
int outsideIdx = 0; outsideIdx < is.numDomainNeighbors(); ++outsideIdx)
219 const auto& outside = is.domainEntity(outsideIdx);
220 const auto bulkElementIdx = bulkGridGeometry.elementMapper().index(outside);
221 const auto surfaceFactor = computeBulkSource(intersectionGeometry,
radius, kernelWidth,
id, lowDimElementIdx, bulkElementIdx, cylIntegration, is.numDomainNeighbors());
224 const auto center = intersectionGeometry.center();
225 this->
pointSources(lowDimIdx).emplace_back(
center,
id, surfaceFactor, intersectionGeometry.volume(), lowDimElementIdx);
226 this->
pointSources(lowDimIdx).back().setEmbeddings(is.numDomainNeighbors());
230 PointSourceData psData;
233 if constexpr (isBox<lowDimIdx>())
235 using ShapeValues = std::vector<Dune::FieldVector<Scalar, 1> >;
236 const auto lowDimGeometry = this->
problem(lowDimIdx).gridGeometry().element(lowDimElementIdx).geometry();
237 ShapeValues shapeValues;
239 psData.addLowDimInterpolation(shapeValues, this->
vertexIndices(lowDimIdx, lowDimElementIdx), lowDimElementIdx);
243 psData.addLowDimInterpolation(lowDimElementIdx);
247 if constexpr (isBox<bulkIdx>())
249 using ShapeValues = std::vector<Dune::FieldVector<Scalar, 1> >;
250 const auto bulkGeometry = this->
problem(bulkIdx).gridGeometry().element(bulkElementIdx).geometry();
251 ShapeValues shapeValues;
253 psData.addBulkInterpolation(shapeValues, this->
vertexIndices(bulkIdx, bulkElementIdx), bulkElementIdx);
257 psData.addBulkInterpolation(bulkElementIdx);
269 if constexpr (isBox<bulkIdx>())
271 const auto& vertices = this->
vertexIndices(bulkIdx, bulkElementIdx);
273 vertices.begin(), vertices.end());
277 this->
couplingStencils(lowDimIdx)[lowDimElementIdx].push_back(bulkElementIdx);
283 makeUniqueStencil_();
286 DUNE_THROW(Dune::InvalidStateException,
"Kernel method shouldn't have point sources in the bulk domain but only volume sources!");
288 std::cout <<
"[coupling] Finished preparing manager in " << watch.elapsed() <<
" seconds." << std::endl;
295 lowDimVolumeInBulkElement_.resize(this->
gridView(bulkIdx).size(0));
297 const auto& lowDimGridGeometry = this->
problem(lowDimIdx).gridGeometry();
298 const auto& bulkGridGeometry = this->
problem(bulkIdx).gridGeometry();
301 for (
const auto& is : intersections(this->
glue()))
304 const auto& inside = is.targetEntity(0);
305 const auto intersectionGeometry = is.geometry();
306 const auto lowDimElementIdx = lowDimGridGeometry.elementMapper().index(inside);
309 const auto radius = this->
problem(lowDimIdx).spatialParams().radius(lowDimElementIdx);
310 for (
int outsideIdx = 0; outsideIdx < is.numDomainNeighbors(); ++outsideIdx)
312 const auto& outside = is.domainEntity(outsideIdx);
313 const auto bulkElementIdx = bulkGridGeometry.elementMapper().index(outside);
314 lowDimVolumeInBulkElement_[bulkElementIdx] += intersectionGeometry.volume()*M_PI*
radius*
radius;
328 return this->
problem(lowDimIdx).spatialParams().radius(data.lowDimElementIdx());
335 const auto eIdx = this->
problem(bulkIdx).gridGeometry().elementMapper().index(element);
336 return lowDimVolumeInBulkElement_[eIdx];
343 const auto totalVolume = element.geometry().volume();
348 const std::vector<std::size_t>&
bulkSourceIds(GridIndex<bulkIdx> eIdx,
int scvIdx = 0)
const
349 {
return bulkSourceIds_[eIdx][scvIdx]; }
353 {
return bulkSourceWeights_[eIdx][scvIdx]; }
357 {
return fluxScalingFactor_[id]; }
364 const typename ParentType::template CouplingStencils<bulkIdx>::mapped_type&
367 const auto& sourceStencils = extendedSourceStencil_.stencil();
368 if (
auto stencil = sourceStencils.find(eIdx); stencil != sourceStencils.end())
369 return stencil->second;
376 template<
class Line,
class CylIntegration>
377 Scalar computeBulkSource(
const Line& line,
const Scalar radius,
const Scalar kernelWidth,
378 std::size_t
id, GridIndex<lowDimIdx> lowDimElementIdx, GridIndex<bulkIdx> coupledBulkElementIdx,
379 const CylIntegration& cylIntegration,
int embeddings)
382 static const auto bulkParamGroup = this->problem(bulkIdx).paramGroup();
386 const auto cylSamples = cylIntegration.size();
387 const auto& a = line.corner(0);
388 const auto& b = line.corner(1);
391 static const auto writeIntegrationPointsToFile =
getParam<bool>(
"MixedDimension.WriteIntegrationPointsToFile",
false);
392 if (writeIntegrationPointsToFile)
394 std::ofstream ipPointFile(
"kernel_points.log", std::ios::app);
395 for (
int i = 0; i < cylSamples; ++i)
397 const auto& point = cylIntegration.integrationPoint(i);
399 ipPointFile << point[0] <<
"," << point[1] <<
"," << point[2] <<
'\n';
403 Scalar integral = 0.0;
404 for (
int i = 0; i < cylSamples; ++i)
406 const auto& point = cylIntegration.integrationPoint(i);
413 assert(bulkElementIdx < this->problem(bulkIdx).gridGeometry().gridView().size(0));
414 const auto localWeight = evalConstKernel_(a, b, point, radius, kernelWidth)*cylIntegration.integrationElement(i)/Scalar(embeddings);
415 integral += localWeight;
416 if (!bulkSourceIds_[bulkElementIdx][0].empty() &&
id == bulkSourceIds_[bulkElementIdx][0].back())
418 bulkSourceWeights_[bulkElementIdx][0].back() += localWeight;
422 bulkSourceIds_[bulkElementIdx][0].emplace_back(
id);
423 bulkSourceWeights_[bulkElementIdx][0].emplace_back(localWeight);
424 addBulkSourceStencils_(bulkElementIdx, lowDimElementIdx, coupledBulkElementIdx);
430 const auto length = (a-b).two_norm()/Scalar(embeddings);
431 return integral/length;
434 void prepareDataStructures_()
439 bulkSourceIds_.clear();
440 bulkSourceWeights_.clear();
441 extendedSourceStencil_.stencil().clear();
444 this->precomputeVertexIndices(bulkIdx);
445 this->precomputeVertexIndices(lowDimIdx);
447 bulkSourceIds_.resize(this->gridView(bulkIdx).size(0));
448 bulkSourceWeights_.resize(this->gridView(bulkIdx).size(0));
454 this->pointSourceData().reserve(this->glue().size());
455 this->averageDistanceToBulkCell().reserve(this->glue().size());
456 fluxScalingFactor_.reserve(this->glue().size());
459 const auto numBulkElements = this->gridView(bulkIdx).size(0);
460 for (GridIndex<bulkIdx> bulkElementIdx = 0; bulkElementIdx < numBulkElements; ++bulkElementIdx)
462 this->couplingStencils(bulkIdx)[bulkElementIdx].reserve(10);
463 extendedSourceStencil_.stencil()[bulkElementIdx].reserve(10);
464 bulkSourceIds_[bulkElementIdx][0].reserve(10);
465 bulkSourceWeights_[bulkElementIdx][0].reserve(10);
470 void makeUniqueStencil_()
473 for (
auto&& stencil : extendedSourceStencil_.stencil())
475 std::sort(stencil.second.begin(), stencil.second.end());
476 stencil.second.erase(std::unique(stencil.second.begin(), stencil.second.end()), stencil.second.end());
479 if constexpr (isBox<bulkIdx>())
481 const auto& indices = this->vertexIndices(bulkIdx, stencil.first);
482 stencil.second.erase(std::remove_if(stencil.second.begin(), stencil.second.end(),
483 [&](
auto i){ return std::find(indices.begin(), indices.end(), i) != indices.end(); }),
484 stencil.second.end());
489 stencil.second.erase(std::remove_if(stencil.second.begin(), stencil.second.end(),
490 [&](
auto i){ return i == stencil.first; }),
491 stencil.second.end());
496 using namespace Dune::Hybrid;
497 forEach(integralRange(Dune::index_constant<2>{}), [&](
const auto domainIdx)
499 for (
auto&& stencil : this->couplingStencils(domainIdx))
501 std::sort(stencil.second.begin(), stencil.second.end());
502 stencil.second.erase(std::unique(stencil.second.begin(), stencil.second.end()), stencil.second.end());
508 void addBulkSourceStencils_(GridIndex<bulkIdx> bulkElementIdx, GridIndex<lowDimIdx> coupledLowDimElementIdx, GridIndex<bulkIdx> coupledBulkElementIdx)
511 if constexpr (isBox<lowDimIdx>())
513 const auto& vertices = this->vertexIndices(lowDimIdx, coupledLowDimElementIdx);
514 this->couplingStencils(bulkIdx)[bulkElementIdx].insert(this->couplingStencils(bulkIdx)[bulkElementIdx].end(),
515 vertices.begin(), vertices.end());
520 auto& s = this->couplingStencils(bulkIdx)[bulkElementIdx];
521 s.push_back(coupledLowDimElementIdx);
526 if constexpr (isBox<bulkIdx>())
528 const auto& vertices = this->vertexIndices(bulkIdx, coupledBulkElementIdx);
529 extendedSourceStencil_.stencil()[bulkElementIdx].insert(extendedSourceStencil_.stencil()[bulkElementIdx].end(),
530 vertices.begin(), vertices.end());
534 auto& s = extendedSourceStencil_.stencil()[bulkElementIdx];
535 s.push_back(coupledBulkElementIdx);
540 Scalar evalConstKernel_(
const GlobalPosition& a,
541 const GlobalPosition& b,
542 const GlobalPosition& point,
544 const Scalar rho)
const noexcept
547 const auto ab = b - a;
548 const auto t = (point - a)*ab/ab.two_norm2();
551 if (t < 0.0 || t > 1.0)
555 auto proj = a; proj.axpy(t, ab);
556 const auto radiusSquared = (proj - point).two_norm2();
558 if (radiusSquared > rho*rho)
561 return 1.0/(M_PI*rho*rho);
567 template<
class SpatialParams>
568 auto kernelWidthFactor_(
const SpatialParams& spatialParams,
unsigned int eIdx)
569 -> std::enable_if_t<hasKernelWidthFactor<SpatialParams, unsigned int>(), Scalar>
570 {
return spatialParams.kernelWidthFactor(eIdx); }
575 template<
class SpatialParams>
576 auto kernelWidthFactor_(
const SpatialParams& spatialParams,
unsigned int eIdx)
577 -> std::enable_if_t<!hasKernelWidthFactor<SpatialParams, unsigned int>(), Scalar>
579 static const Scalar kernelWidthFactor =
getParam<Scalar>(
"MixedDimension.KernelWidthFactor");
580 return kernelWidthFactor;
584 EmbeddedCoupling::ExtendedSourceStencil<ThisType> extendedSourceStencil_;
586 std::vector<Scalar> lowDimVolumeInBulkElement_;
588 std::vector<std::array<std::vector<std::size_t>, isBox<bulkIdx>() ? 1<<bulkDim : 1>> bulkSourceIds_;
590 std::vector<std::array<std::vector<Scalar>, isBox<bulkIdx>() ? 1<<bulkDim : 1>> bulkSourceWeights_;
593 std::vector<Scalar> fluxScalingFactor_;