58 using Scalar =
typename MDTraits::Scalar;
59 using SolutionVector =
typename MDTraits::SolutionVector;
60 using PointSourceData =
typename BaseType::PointSourceTraits::PointSourceData;
62 static constexpr auto bulkIdx =
typename MDTraits::template SubDomain<0>::Index();
63 static constexpr auto lowDimIdx =
typename MDTraits::template SubDomain<1>::Index();
66 template<std::
size_t id>
using SubDomainTypeTag =
typename MDTraits::template SubDomain<id>::TypeTag;
69 template<std::
size_t id>
using GridView =
typename GridGeometry<id>::GridView;
70 template<std::
size_t id>
using Element =
typename GridView<id>::template Codim<0>::Entity;
73 using GlobalPosition =
typename Element<bulkIdx>::Geometry::GlobalCoordinate;
75 template<std::
size_t id>
76 static constexpr bool isBox()
80 static constexpr bool bulkIsCartesian
81 = Dune::Capabilities::isCartesian<typename GridView<bulkIdx>::Grid>::v;
84 bulkDim = GridView<bulkIdx>::dimension,
85 lowDimDim = GridView<lowDimIdx>::dimension,
86 dimWorld = GridView<bulkIdx>::dimensionworld
90 template <
typename T,
typename ...Ts>
91 using VariableKernelWidthDetector =
decltype(std::declval<T>().kernelWidthFactor(std::declval<Ts>()...));
93 template<
class T,
typename ...Args>
94 static constexpr bool hasKernelWidthFactor()
95 {
return Dune::Std::is_detected<VariableKernelWidthDetector, T, Args...>::value; }
100 using ParentType::ParentType;
102 void init(std::shared_ptr<Problem<bulkIdx>> bulkProblem,
103 std::shared_ptr<Problem<lowDimIdx>> lowDimProblem,
104 const SolutionVector&
curSol)
110 DUNE_THROW(Dune::NotImplemented,
"The current intersection detection may likely fail for refined grids.");
119 template<std::
size_t id,
class JacobianPattern>
122 extendedSourceStencil_.extendJacobianPattern(*
this, domainI, pattern);
132 template<std::
size_t i,
class LocalAssemblerI,
class JacobianMatrixDiagBlock,
class Gr
idVariables>
134 const LocalAssemblerI& localAssemblerI,
135 const typename LocalAssemblerI::LocalResidual::ElementResidualVector&,
136 JacobianMatrixDiagBlock& A,
137 GridVariables& gridVariables)
139 extendedSourceStencil_.evalAdditionalDomainDerivatives(*
this, domainI, localAssemblerI, A, gridVariables);
156 std::cout <<
"[coupling] Initializing the integration point source data structures..." << std::endl;
159 prepareDataStructures_();
160 std::cout <<
"[coupling] Resized data structures." << std::endl;
162 const auto& bulkGridGeometry = this->
problem(bulkIdx).gridGeometry();
163 const auto& lowDimGridGeometry = this->
problem(lowDimIdx).gridGeometry();
167 static const double characteristicRelativeLength =
getParam<double>(
"MixedDimension.KernelIntegrationCRL", 0.1);
170 static const bool writeIntegrationPointsToFile =
getParam<bool>(
"MixedDimension.WriteIntegrationPointsToFile",
false);
171 if (writeIntegrationPointsToFile)
173 std::ofstream ipPointFile(
"kernel_points.log", std::ios::trunc);
174 ipPointFile <<
"x,y,z\n";
175 std::cout <<
"[coupling] Initialized kernel_points.log." << std::endl;
178 for (
const auto& is : intersections(this->
glue()))
181 const auto& inside = is.targetEntity(0);
183 const auto intersectionGeometry = is.geometry();
184 const auto lowDimElementIdx = lowDimGridGeometry.elementMapper().index(inside);
189 const auto radius = this->
problem(lowDimIdx).spatialParams().radius(lowDimElementIdx);
190 const auto kernelWidthFactor = kernelWidthFactor_(this->
problem(lowDimIdx).spatialParams(), lowDimElementIdx);
191 const auto kernelWidth = kernelWidthFactor*
radius;
192 const auto a = intersectionGeometry.corner(0);
193 const auto b = intersectionGeometry.corner(1);
197 for (
int outsideIdx = 0; outsideIdx < is.numDomainNeighbors(); ++outsideIdx)
208 const auto& outside = is.domainEntity(outsideIdx);
209 const auto bulkElementIdx = bulkGridGeometry.elementMapper().index(outside);
210 const auto surfaceFactor = computeBulkSource(intersectionGeometry,
radius, kernelWidth,
id, lowDimElementIdx, bulkElementIdx, cylIntegration, is.numDomainNeighbors());
213 const auto center = intersectionGeometry.center();
214 this->
pointSources(lowDimIdx).emplace_back(
center,
id, surfaceFactor, intersectionGeometry.volume(), lowDimElementIdx);
215 this->
pointSources(lowDimIdx).back().setEmbeddings(is.numDomainNeighbors());
219 PointSourceData psData;
222 if constexpr (isBox<lowDimIdx>())
224 using ShapeValues = std::vector<Dune::FieldVector<Scalar, 1> >;
225 const auto lowDimGeometry = this->
problem(lowDimIdx).gridGeometry().element(lowDimElementIdx).geometry();
226 ShapeValues shapeValues;
228 psData.addLowDimInterpolation(shapeValues, this->
vertexIndices(lowDimIdx, lowDimElementIdx), lowDimElementIdx);
232 psData.addLowDimInterpolation(lowDimElementIdx);
236 if constexpr (isBox<bulkIdx>())
238 using ShapeValues = std::vector<Dune::FieldVector<Scalar, 1> >;
239 const auto bulkGeometry = this->
problem(bulkIdx).gridGeometry().element(bulkElementIdx).geometry();
240 ShapeValues shapeValues;
242 psData.addBulkInterpolation(shapeValues, this->
vertexIndices(bulkIdx, bulkElementIdx), bulkElementIdx);
246 psData.addBulkInterpolation(bulkElementIdx);
258 if constexpr (isBox<bulkIdx>())
260 const auto& vertices = this->
vertexIndices(bulkIdx, bulkElementIdx);
262 vertices.begin(), vertices.end());
266 this->
couplingStencils(lowDimIdx)[lowDimElementIdx].push_back(bulkElementIdx);
272 makeUniqueStencil_();
275 DUNE_THROW(Dune::InvalidStateException,
"Kernel method shouldn't have point sources in the bulk domain but only volume sources!");
277 std::cout <<
"[coupling] Finished preparing manager in " << watch.elapsed() <<
" seconds." << std::endl;
287 const std::vector<std::size_t>&
bulkSourceIds(GridIndex<bulkIdx> eIdx,
int scvIdx = 0)
const
288 {
return bulkSourceIds_[eIdx][scvIdx]; }
292 {
return bulkSourceWeights_[eIdx][scvIdx]; }
296 {
return fluxScalingFactor_[id]; }
303 const typename BaseType::template CouplingStencils<bulkIdx>::mapped_type&
306 const auto& sourceStencils = extendedSourceStencil_.stencil();
307 if (
auto stencil = sourceStencils.find(eIdx); stencil != sourceStencils.end())
308 return stencil->second;
315 template<
class Line,
class CylIntegration>
316 Scalar computeBulkSource(
const Line& line,
const Scalar radius,
const Scalar kernelWidth,
317 std::size_t
id, GridIndex<lowDimIdx> lowDimElementIdx, GridIndex<bulkIdx> coupledBulkElementIdx,
318 const CylIntegration& cylIntegration,
int embeddings)
324 const auto& min = this->problem(bulkIdx).gridGeometry().bBoxMin();
325 const auto& max = this->problem(bulkIdx).gridGeometry().bBoxMax();
326 std::array<int, bulkDim> cells{};
327 if constexpr (bulkIsCartesian)
329 static const auto bulkParamGroup = this->problem(bulkIdx).paramGroup();
331 cells = cartesianCells;
333 const auto cylSamples = cylIntegration.size();
334 const auto& a =
line.corner(0);
335 const auto& b =
line.corner(1);
338 static const auto writeIntegrationPointsToFile =
getParam<bool>(
"MixedDimension.WriteIntegrationPointsToFile",
false);
339 if (writeIntegrationPointsToFile)
341 std::ofstream ipPointFile(
"kernel_points.log", std::ios::app);
342 for (
int i = 0; i < cylSamples; ++i)
344 const auto& point = cylIntegration.integrationPoint(i);
346 ipPointFile << point[0] <<
"," << point[1] <<
"," << point[2] <<
'\n';
351 for (
int i = 0; i < cylSamples; ++i)
353 const auto& point = cylIntegration.integrationPoint(i);
356 const auto bulkElementIdx = bulkElementIndex_(point, min, max, cells);
357 if (bulkElementIdx == invalidElement_)
359 assert(bulkElementIdx < this->problem(bulkIdx).gridGeometry().gridView().size(0));
360 const auto localWeight = evalConstKernel_(a, b, point, radius, kernelWidth)*cylIntegration.integrationElement(i)/
Scalar(embeddings);
361 integral += localWeight;
363 if constexpr (isBox<bulkIdx>())
365 using ShapeValues = std::vector<Dune::FieldVector<Scalar, 1> >;
366 const auto& gg = this->problem(bulkIdx).gridGeometry();
367 ShapeValues shapeValues;
368 this->getShapeValues(bulkIdx, gg, gg.element(bulkElementIdx).geometry(), point, shapeValues);
370 bool addedStencil =
false;
371 for (
int v = 0; v < static_cast<int>(shapeValues.size()); ++v)
373 const auto w = localWeight*shapeValues[v][0];
374 if (!bulkSourceIds_[bulkElementIdx][v].empty() &&
id == bulkSourceIds_[bulkElementIdx][v].back())
375 bulkSourceWeights_[bulkElementIdx][v].back() +=
w;
378 bulkSourceIds_[bulkElementIdx][v].emplace_back(
id);
379 bulkSourceWeights_[bulkElementIdx][v].emplace_back(
w);
384 addBulkSourceStencils_(bulkElementIdx, lowDimElementIdx, coupledBulkElementIdx);
388 if (!bulkSourceIds_[bulkElementIdx][0].empty() &&
id == bulkSourceIds_[bulkElementIdx][0].back())
389 bulkSourceWeights_[bulkElementIdx][0].back() += localWeight;
392 bulkSourceIds_[bulkElementIdx][0].emplace_back(
id);
393 bulkSourceWeights_[bulkElementIdx][0].emplace_back(localWeight);
394 addBulkSourceStencils_(bulkElementIdx, lowDimElementIdx, coupledBulkElementIdx);
401 const auto length = (a-b).two_norm()/
Scalar(embeddings);
402 return integral/length;
405 static constexpr GridIndex<bulkIdx> invalidElement_ = std::numeric_limits<GridIndex<bulkIdx>>::max();
414 GridIndex<bulkIdx> bulkElementIndex_(
const GlobalPosition& point,
415 const GlobalPosition& min,
416 const GlobalPosition& max,
417 const std::array<int, bulkDim>& cells)
const
419 if constexpr (bulkIsCartesian)
424 point, this->problem(bulkIdx).gridGeometry().boundingBoxTree(),
true);
425 return candidates.empty() ? invalidElement_ : GridIndex<bulkIdx>(candidates[0]);
429 void prepareDataStructures_()
434 bulkSourceIds_.clear();
435 bulkSourceWeights_.clear();
436 extendedSourceStencil_.stencil().clear();
439 this->precomputeVertexIndices(bulkIdx);
440 this->precomputeVertexIndices(lowDimIdx);
442 bulkSourceIds_.resize(this->gridView(bulkIdx).size(0));
443 bulkSourceWeights_.resize(this->gridView(bulkIdx).size(0));
449 this->pointSourceData().reserve(this->glue().size());
450 this->averageDistanceToBulkCell().reserve(this->glue().size());
451 fluxScalingFactor_.reserve(this->glue().size());
454 const auto numBulkElements = this->gridView(bulkIdx).size(0);
455 for (GridIndex<bulkIdx> bulkElementIdx = 0; bulkElementIdx < numBulkElements; ++bulkElementIdx)
457 this->couplingStencils(bulkIdx)[bulkElementIdx].reserve(10);
458 extendedSourceStencil_.stencil()[bulkElementIdx].reserve(10);
459 for (
auto& ids : bulkSourceIds_[bulkElementIdx]) ids.reserve(10);
460 for (
auto&
w : bulkSourceWeights_[bulkElementIdx])
w.reserve(10);
465 void makeUniqueStencil_()
468 for (
auto&& stencil : extendedSourceStencil_.stencil())
470 std::sort(stencil.second.begin(), stencil.second.end());
471 stencil.second.erase(std::unique(stencil.second.begin(), stencil.second.end()), stencil.second.end());
474 if constexpr (isBox<bulkIdx>())
476 const auto& indices = this->vertexIndices(bulkIdx, stencil.first);
477 stencil.second.erase(std::remove_if(stencil.second.begin(), stencil.second.end(),
478 [&](
auto i){ return std::find(indices.begin(), indices.end(), i) != indices.end(); }),
479 stencil.second.end());
484 stencil.second.erase(std::remove_if(stencil.second.begin(), stencil.second.end(),
485 [&](
auto i){ return i == stencil.first; }),
486 stencil.second.end());
491 using namespace Dune::Hybrid;
492 forEach(integralRange(Dune::index_constant<2>{}), [&](
const auto domainIdx)
494 for (
auto&& stencil : this->couplingStencils(domainIdx))
496 std::sort(stencil.second.begin(), stencil.second.end());
497 stencil.second.erase(std::unique(stencil.second.begin(), stencil.second.end()), stencil.second.end());
503 void addBulkSourceStencils_(GridIndex<bulkIdx> bulkElementIdx, GridIndex<lowDimIdx> coupledLowDimElementIdx, GridIndex<bulkIdx> coupledBulkElementIdx)
506 if constexpr (isBox<lowDimIdx>())
508 const auto& vertices = this->vertexIndices(lowDimIdx, coupledLowDimElementIdx);
509 this->couplingStencils(bulkIdx)[bulkElementIdx].insert(this->couplingStencils(bulkIdx)[bulkElementIdx].end(),
510 vertices.begin(), vertices.end());
515 auto& s = this->couplingStencils(bulkIdx)[bulkElementIdx];
516 s.push_back(coupledLowDimElementIdx);
521 if constexpr (isBox<bulkIdx>())
523 const auto& vertices = this->vertexIndices(bulkIdx, coupledBulkElementIdx);
524 extendedSourceStencil_.stencil()[bulkElementIdx].insert(extendedSourceStencil_.stencil()[bulkElementIdx].end(),
525 vertices.begin(), vertices.end());
529 auto& s = extendedSourceStencil_.stencil()[bulkElementIdx];
530 s.push_back(coupledBulkElementIdx);
535 Scalar evalConstKernel_(
const GlobalPosition& a,
536 const GlobalPosition& b,
537 const GlobalPosition& point,
539 const Scalar rho)
const noexcept
542 const auto ab = b - a;
543 const auto t = (point - a)*ab/ab.two_norm2();
546 if (t < 0.0 || t > 1.0)
550 auto proj = a; proj.axpy(t, ab);
551 const auto radiusSquared = (proj - point).two_norm2();
553 if (radiusSquared > rho*rho)
556 return 1.0/(M_PI*rho*rho);
562 template<
class SpatialParams>
563 auto kernelWidthFactor_(
const SpatialParams& spatialParams,
unsigned int eIdx)
564 -> std::enable_if_t<hasKernelWidthFactor<SpatialParams, unsigned int>(),
Scalar>
565 {
return spatialParams.kernelWidthFactor(eIdx); }
570 template<
class SpatialParams>
571 auto kernelWidthFactor_(
const SpatialParams& spatialParams,
unsigned int eIdx)
572 -> std::enable_if_t<!hasKernelWidthFactor<SpatialParams, unsigned int>(),
Scalar>
575 return kernelWidthFactor;
579 EmbeddedCoupling::ExtendedSourceStencil<ThisType> extendedSourceStencil_;
582 std::vector<std::array<std::vector<std::size_t>, isBox<bulkIdx>() ? 1<<bulkDim : 1>> bulkSourceIds_;
584 std::vector<std::array<std::vector<Scalar>, isBox<bulkIdx>() ? 1<<bulkDim : 1>> bulkSourceWeights_;
587 std::vector<Scalar> fluxScalingFactor_;