diff --git a/dumux/multidomain/embedded/couplingmanager1d3d.hh b/dumux/multidomain/embedded/couplingmanager1d3d.hh index a1824dbeb6a9802f164c612bb67597e237aab159..711d7ee3609ee8f47261c702af936820b020427f 100644 --- a/dumux/multidomain/embedded/couplingmanager1d3d.hh +++ b/dumux/multidomain/embedded/couplingmanager1d3d.hh @@ -30,6 +30,7 @@ #include <vector> #include <dune/common/timer.hh> +#include <dune/common/exceptions.hh> #include <dune/geometry/quadraturerules.hh> #include <dumux/common/properties.hh> @@ -953,11 +954,13 @@ public: // reserve memory for data this->pointSourceData().reserve(this->glue().size()); this->averageDistanceToBulkCell().reserve(this->glue().size()); + fluxScalingFactor_.reserve(this->glue().size()); // generate a bunch of random vectors and values for // Monte-carlo integration on the cylinder defined by line and radius - const int samples = getParam<int>("MixedDimension.NumMCSamples"); - CylinderIntegration<Scalar,CylinderIntegrationMethod::spaced> cylIntegration(samples); + static const bool useCylinderIntegration = getParam<bool>("MixedDimension.UseCylinderIntegration"); + const int samples = useCylinderIntegration ? getParam<int>("MixedDimension.NumMCSamples") : 1; + CylinderIntegration<Scalar, CylinderIntegrationMethod::spaced> cylIntegration(samples); static const auto kernelWidthFactor = getParam<Scalar>("MixedDimension.KernelWidthFactor"); for (const auto& is : intersections(this->glue())) @@ -975,11 +978,18 @@ public: const auto radius = this->problem(lowDimIdx).spatialParams().radius(lowDimElementIdx); const auto kernelWidth = kernelWidthFactor*radius; + const auto a = intersectionGeometry.corner(0); + const auto b = intersectionGeometry.corner(1); // we can have multiple 3d elements per 1d intersection, for evaluation taking one of them should be fine for (std::size_t outsideIdx = 0; outsideIdx < is.neighbor(0); ++outsideIdx) { // compute source id + // for each id we have + // (1) a point source for the 1d domain + // (2) a bulk source weight for the each element in the 3d domain (the fraction of the total source/sink for the 1d element with the point source) + // TODO: i.e. one integration of the kernel should be enough (for each of the weights it's weight/embeddings) + // (3) the flux scaling factor for each outside element, i.e. each id const auto id = this->idCounter_++; // place a point source at the intersection center @@ -991,9 +1001,8 @@ public: const std::size_t bulkElementIdx = bulkFvGridGeometry.elementMapper().index(outside); // compute the weights for the bulk volume sources - static const bool useCylinderIntegration = getParam<bool>("MixedDimension.UseCylinderIntegration"); if (useCylinderIntegration) - cylIntegration.setGeometry(intersectionGeometry.corner(0), intersectionGeometry.corner(1), kernelWidth); + cylIntegration.setGeometry(a, b, kernelWidth); computeBulkSource(intersectionGeometry, radius, kernelWidth, id, lowDimElementIdx, bulkElementIdx, cylIntegration, is.neighbor(0)); // pre compute additional data used for the evaluation of @@ -1031,8 +1040,22 @@ public: // publish point source data in the global vector this->pointSourceData().emplace_back(std::move(psData)); - // compute average distance to bulk cell - this->averageDistanceToBulkCell().push_back(this->computeDistance(outside.geometry(), center)); + // compute the average flux scaling factor + const auto outsideGeometry = outside.geometry(); + static const int fluxScalingFactorIntOrder = getParam<int>("MixedDimension.FluxScalingIntegrationOrder", 5); + const auto& quadBulk = Dune::QuadratureRules<Scalar, bulkDim>::rule(outsideGeometry.type(), fluxScalingFactorIntOrder); + // iterate over all quadrature points + Scalar avgMinDist = 0.0; + for (auto&& qpBulk : quadBulk) + { + const auto globalPosBulk = outsideGeometry.global(qpBulk.position()); + const auto d = computeMinDistance_(a, b, globalPosBulk); + avgMinDist += d*qpBulk.weight(); + } + + // add it to the internal data vector + this->averageDistanceToBulkCell().push_back(avgMinDist); + fluxScalingFactor_.push_back(this->problem(bulkIdx).fluxScalingFactor(avgMinDist, radius)); // export the lowdim coupling stencil // we insert all vertices / elements and make it unique later @@ -1143,17 +1166,21 @@ public: } //! return all source ids for a bulk elements - const std::vector<std::size_t> bulkSourceIds(std::size_t eIdx) const - { return bulkSourceIds_[eIdx]; } + const std::vector<std::size_t> bulkSourceIds(std::size_t eIdx, std::size_t scvIdx = 0) const + { return bulkSourceIds_[eIdx][scvIdx]; } //! return all source ids for a bulk elements - const std::vector<Scalar> bulkSourceWeights(std::size_t eIdx) const - { return bulkSourceWeights_[eIdx]; } + const std::vector<Scalar> bulkSourceWeights(std::size_t eIdx, std::size_t scvIdx = 0) const + { return bulkSourceWeights_[eIdx][scvIdx]; } + + //! The flux scaling factor for a source with id + Scalar fluxScalingFactor(std::size_t id) const + { return fluxScalingFactor_[id]; } // \} private: - + //! compute the kernel distributed sources and add stencils template<class Line, class CylIntegration> void computeBulkSource(const Line& line, const Scalar radius, const Scalar kernelWidth, std::size_t id, std::size_t lowDimElementIdx, std::size_t coupledBulkElementIdx, @@ -1161,11 +1188,11 @@ private: { // Monte-carlo integration on the cylinder defined by line and radius const auto numBulkElements = this->gridView(bulkIdx).size(0); - std::vector<Scalar> weights(numBulkElements, 0.0); - std::vector<bool> visited(numBulkElements, false); + constexpr std::size_t numScv = isBox<bulkIdx>() ? 1<<bulkDim : 1; + std::vector<std::array<Scalar, numScv>> weights(numBulkElements, std::array<Scalar, numScv>{}); + std::vector<std::bitset<numScv>> visited(numBulkElements, std::bitset<numScv>{}); Scalar integral = 0.0; - static const bool useCylinderIntegration = getParam<bool>("MixedDimension.UseCylinderIntegration"); if (useCylinderIntegration) { @@ -1182,9 +1209,24 @@ private: // const auto bulkIndices = intersectingEntities(point, this->problem(bulkIdx).fvGridGeometry().boundingBoxTree(), true); if (is.first) { - const auto bulkIdx = is.second; - visited[bulkIdx] = true; - weights[bulkIdx] += weight; + const auto bulkElementIdx = is.second; + if (isBox<bulkIdx>()) + { + const auto dist = point-min; + const auto diag = max-min; + std::bitset<3> corner; + for (int i = 0; i < 3; ++i) + corner[i] = bool(int(std::round(dist[i]/diag[i]))); + + std::size_t scvIdx = corner.to_ulong(); + visited[bulkElementIdx][scvIdx] = true; + weights[bulkElementIdx][scvIdx] += weight; + } + else + { + visited[bulkElementIdx][0] = true; + weights[bulkElementIdx][0] += weight; + } } } } @@ -1202,37 +1244,74 @@ private: const auto weight = integrator.integrate(kernelFunc, cylinder); if (weight > 1e-35) { - visited[bulkElementIdx] = true; - weights[bulkElementIdx] += weight; + visited[bulkElementIdx][0] = true; + weights[bulkElementIdx][0] += weight; integral += weight; } } } - const auto length = line.volume(); - std::cout << "integration error in %: " << std::abs(length-integral)/length*100 << std::endl; + // const auto length = line.volume(); + // std::cout << "integration error in %: " << std::abs(length-integral)/length*100 << std::endl; // const auto correctionFactor = length/integral; - for (auto bulkElementIdx = 0; bulkElementIdx < numBulkElements; ++bulkElementIdx) + for (const auto& element : elements(this->problem(bulkIdx).fvGridGeometry().gridView())) { - if (!visited[bulkElementIdx]) + const auto bulkElementIdx = this->problem(bulkIdx).fvGridGeometry().elementMapper().index(element); + if (!isBox<bulkIdx>() && !visited[bulkElementIdx][0]) continue; - bulkSourceIds_[bulkElementIdx].push_back(id); - bulkSourceWeights_[bulkElementIdx].push_back(weights[bulkElementIdx]/embeddings);//*correctionFactor); - - if (isBox<lowDimIdx>()) + if (isBox<bulkIdx>()) { - const auto& vertices = this->vertexIndices(lowDimIdx, lowDimElementIdx); - this->couplingStencils(bulkIdx)[bulkElementIdx].insert(this->couplingStencils(bulkIdx)[bulkElementIdx].end(), - vertices.begin(), vertices.end()); + auto fvGeometry = localView(this->problem(bulkIdx).fvGridGeometry()); + fvGeometry.bindElement(element); + for (const auto& scv : scvs(fvGeometry)) + { + const auto scvIdx = scv.indexInElement(); + if (!visited[bulkElementIdx][scvIdx]) + continue; + + bulkSourceIds_[bulkElementIdx][scvIdx].push_back(id); + bulkSourceWeights_[bulkElementIdx][scvIdx].push_back(weights[bulkElementIdx][scvIdx]/embeddings);//*correctionFactor); + addBulkSourceStencils_(bulkElementIdx, lowDimElementIdx, coupledBulkElementIdx); + } } else { - this->couplingStencils(bulkIdx)[bulkElementIdx].push_back(lowDimElementIdx); + bulkSourceIds_[bulkElementIdx][0].push_back(id); + bulkSourceWeights_[bulkElementIdx][0].push_back(weights[bulkElementIdx][0]/embeddings);//*correctionFactor); + addBulkSourceStencils_(bulkElementIdx, lowDimElementIdx, coupledBulkElementIdx); } + } + + } + + //! add additional stencil entries for the bulk element + void addBulkSourceStencils_(std::size_t bulkElementIdx, std::size_t coupledLowDimElementIdx, std::size_t coupledBulkElementIdx) + { + // add the lowdim element to the coupling stencil of this bulk element + if (isBox<lowDimIdx>()) + { + const auto& vertices = this->vertexIndices(lowDimIdx, coupledLowDimElementIdx); + this->couplingStencils(bulkIdx)[bulkElementIdx].insert(this->couplingStencils(bulkIdx)[bulkElementIdx].end(), + vertices.begin(), vertices.end()); - // the extended source stencil, every 3d element with a source is coupled to the element where the 3d quantities are measured + } + else + { + this->couplingStencils(bulkIdx)[bulkElementIdx].push_back(coupledLowDimElementIdx); + } + + // the extended source stencil, every 3d element with a source is coupled to + // the element/dofs where the 3d quantities are measured + if (isBox<bulkIdx>()) + { + const auto& vertices = this->vertexIndices(bulkIdx, coupledBulkElementIdx); + extendedSourceStencil_.stencil()[bulkElementIdx].insert(extendedSourceStencil_.stencil()[bulkElementIdx].end(), + vertices.begin(), vertices.end()); + } + else + { extendedSourceStencil_.stencil()[bulkElementIdx].push_back(coupledBulkElementIdx); } } @@ -1242,7 +1321,7 @@ private: const GlobalPosition& b, const GlobalPosition& point, const Scalar R, - const Scalar rho) const noexcept + const Scalar rho) const { // projection of point onto line a + t*(b-a) const auto ab = b - a; @@ -1259,26 +1338,27 @@ private: if (r > rho) return 0.0; - // custom kernel (constant) - return 1.0/(M_PI*rho*rho); - - // custom kernel (linear) - // const auto lnR = std::log(R); - // const auto lnRho = std::log(rho); - // return (18.0*r*lnR - 18.0*r*lnRho + 9*r - 12.0*rho*lnR + 12.0*rho*lnRho - 4*rho)/(2*M_PI*rho*rho*rho); - - // custom kernel (quadratic) - // const auto lnR = std::log(R); - // const auto lnRho = std::log(rho); - // return (8.0*r*r*lnR - 8.0*r*r*lnRho + 4.0*r*r - 4.0*rho*rho*lnR + 4.0*rho*rho*lnRho - rho*rho)/(M_PI*rho*rho*rho*rho); - - // cubic kernel - // const auto r2 = r*r; - // const auto r3 = r2*r; - // const Scalar w2 = rho*rho; - // const Scalar w3 = w2*rho; - // const Scalar k = 10.0/3.0/M_PI/w2; - // return k*(2.0*r3/w3 - 3.0*r2/w2 + 1.0); + static const std::string type = getParam<std::string>("MixedDimension.KernelType"); + if (type == "Const") + return 1.0/(M_PI*rho*rho); + else if (type == "Cubic") + { + const auto r2 = r*r; const auto r3 = r*r*r; + const auto rho2 = rho*rho; const auto rho3 = rho2*rho; + return 10.0*(2.0*r3/rho3 - 3.0*r2/rho2 + 1.0)/(3.0*M_PI*rho2); + } + + DUNE_THROW(Dune::NotImplemented, "Kernel type: " << type); + } + + inline Scalar computeMinDistance_(const GlobalPosition& a, const GlobalPosition& b, const GlobalPosition& x3d) const + { + // r is the minimum distance to the line through a and b + const auto ab = b - a; + const auto t = (x3d - a)*ab/ab.two_norm2(); + auto proj = a; proj.axpy(t, ab); + const auto r = (proj - x3d).two_norm(); + return r; } //! the extended source stencil object for kernel coupling @@ -1286,9 +1366,11 @@ private: //! vector for the volume fraction of the lowdim domain in the bulk domain cells std::vector<Scalar> lowDimVolumeInBulkElement_; //! kernel sources to integrate for each bulk element - std::vector<std::vector<std::size_t>> bulkSourceIds_; + std::vector<std::array<std::vector<size_t>, isBox<bulkIdx>() ? 1<<bulkDim : 1>> bulkSourceIds_; //! the integral of the kernel for each point source / integration point, i.e. weight for the source - std::vector<std::vector<Scalar>> bulkSourceWeights_; + std::vector<std::array<std::vector<Scalar>, isBox<bulkIdx>() ? 1<<bulkDim : 1>> bulkSourceWeights_; + //! the flux scaling factor for the respective source id + std::vector<Scalar> fluxScalingFactor_; }; } // end namespace Dumux diff --git a/dumux/multidomain/embedded/couplingmanagerbase.hh b/dumux/multidomain/embedded/couplingmanagerbase.hh index 963501ca10bdfa838f448ae8a1762df5f94226f9..9f64773075b238f625af97c7f2d06a4c6df1ceae 100644 --- a/dumux/multidomain/embedded/couplingmanagerbase.hh +++ b/dumux/multidomain/embedded/couplingmanagerbase.hh @@ -487,7 +487,7 @@ protected: } template<class Geometry, class GlobalPosition> - Scalar computeDistance(const Geometry& geometry, const GlobalPosition& p) + Scalar computeDistance(const Geometry& geometry, const GlobalPosition& p) const { Scalar avgDist = 0.0; const auto& quad = Dune::QuadratureRules<Scalar, bulkDim>::rule(geometry.type(), 5); @@ -499,6 +499,20 @@ protected: return avgDist; } + template<class Geometry, class GlobalPosition> + Scalar computeDistanceToCenter(const Geometry& geometry, const GlobalPosition& p) const + { + const auto a = geometry.corner(0); + const auto b = geometry.corner(1); + const auto ab = b - a; + const auto t = (p - a)*ab/ab.two_norm2(); + + // compute distance + auto proj = a; proj.axpy(t, ab); + const auto r = (proj - p).two_norm(); + return r; + } + //! Return reference to point source data vector member std::vector<PointSourceData>& pointSourceData() { return pointSourceData_; } diff --git a/dumux/multidomain/embedded/cylinderintegration.hh b/dumux/multidomain/embedded/cylinderintegration.hh index c13f37247430b50b6df643b47d63cc4777667cd2..a608b987e9f9f3a5937925cc7877a39cee4865cd 100644 --- a/dumux/multidomain/embedded/cylinderintegration.hh +++ b/dumux/multidomain/embedded/cylinderintegration.hh @@ -166,9 +166,6 @@ public: } } } - - const auto exactIntegral = M_PI*radius*radius*height; - std::cout << "Integration error in %: " << std::abs(exactIntegral-integral)/exactIntegral*100.0 << std::endl; } Scalar integrationElement(std::size_t i) const