3.5-git
DUNE for Multi-{Phase, Component, Scale, Physics, ...} flow and transport in porous media
cylinderintegration.hh
Go to the documentation of this file.
1// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2// vi: set et ts=4 sw=4 sts=4:
3/*****************************************************************************
4 * See the file COPYING for full copying permissions. *
5 * *
6 * This program is free software: you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation, either version 3 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
18 *****************************************************************************/
27#ifndef DUMUX_MULTIDOMAIN_EMBEDDED_CYLINDERINTEGRATOR_HH
28#define DUMUX_MULTIDOMAIN_EMBEDDED_CYLINDERINTEGRATOR_HH
29
30#include <cmath>
31#include <algorithm>
32#include <numeric>
33#include <iterator>
34#include <utility>
35#include <tuple>
36#include <limits>
37
38#include <dune/common/fvector.hh>
39
40#include <dumux/common/math.hh>
42
44
58template<class Scalar>
60{
61 using GlobalPosition = Dune::FieldVector<Scalar, 3>;
62
63public:
69 CylinderIntegration(const Scalar rStep)
70 {
71 rSamples_ = characteristicLengthToNumSamples_(rStep);
72 initialize_();
73 }
74
81 CylinderIntegration(const Scalar rStep, const Scalar zStep)
82 : zStepFixed_(true)
83 {
84 using std::ceil; using std::clamp;
85 rSamples_ = characteristicLengthToNumSamples_(rStep);
86 zSamples_ = characteristicLengthToNumSamples_(zStep);
87 initialize_();
88 }
89
97 void setGeometry(const GlobalPosition& bottomCenter,
98 const GlobalPosition& topCenter,
99 const Scalar radius,
100 int verbosity = 0)
101 {
102 const auto zAxis = (topCenter-bottomCenter);
103 const auto height = zAxis.two_norm();
104 const auto zAxisUnitVector = zAxis/height;
105
106 // compute step size in r-direction
107 const auto rStep = radius/Scalar(rSamples_);
108
109 // compute zSamples from r samples if not specified by the user
110 using std::ceil;
111 if (!zStepFixed_)
112 zSamples_ = std::max<std::size_t>(1, ceil(height/rStep));
113 const auto zStep = height/Scalar(zSamples_);
114
115 // compute offsets for index calculation
116 auto kOffset = numRingSamples_;
117 std::partial_sum(kOffset.begin(), kOffset.end(), kOffset.begin());
118
119 // compute total number of samples
120 numPoints_ = zSamples_*circleSamples_;
121 if (verbosity > 0)
122 std::cout << "CylinderIntegration -- number of integration points: " << numPoints_ << std::endl;
123
124 // resize integration points and integration elements
125 integrationElement_.resize(numPoints_);
126 points_.resize(numPoints_);
127
128 // reserve enough memory for the points on each ring
129 std::vector<GlobalPosition> ringPoints;
130 ringPoints.reserve(numRingSamples_.back());
131
132 // compute integration points and integration elements
133 for (std::size_t i = 0; i < zSamples_; ++i)
134 {
135 // for each cylinder slice i
136 auto sliceCenter = bottomCenter;
137 sliceCenter.axpy((Scalar(i)+0.5)*zStep, zAxisUnitVector);
138
139 // generate circle points for each ring j
140 for (std::size_t j = 0; j < rSamples_; ++j)
141 {
142 const auto r = (Scalar(j)+0.5)*rStep;
143 circlePoints(ringPoints, sincos_[j], sliceCenter, zAxisUnitVector, r);
144 const auto ringOffest = j > 0 ? kOffset[j-1] : 0;
145 const auto ringSamples = numRingSamples_[j];
146 // volume of each element in ring
147 // total ring volume is given by M_PI*rStep*rStep*zStep*((j+1)^2 - j^2)
148 const auto integrationElement = M_PI*rStep*rStep*zStep*(1.0 + 2.0*Scalar(j))/ringSamples;
149 for (std::size_t k = 0; k < ringSamples; ++k)
150 {
151 const std::size_t idx = k + ringOffest + circleSamples_*i;
152 points_[idx] = ringPoints[k];
153 integrationElement_[idx] = integrationElement;
154 }
155 }
156 }
157 }
158
160 Scalar integrationElement(std::size_t i) const
161 { return integrationElement_[i]; }
162
164 const GlobalPosition& integrationPoint(std::size_t i) const
165 { return points_[i]; }
166
168 std::size_t size() const
169 { return numPoints_; }
170
171private:
172 void initialize_()
173 {
174 // precompute number of cells in ring and total circle samples
175 circleSamples_ = 0;
176 numRingSamples_.resize(rSamples_);
177 for (int j = 0; j < rSamples_; ++j)
178 {
179 // number of cells in ring j
180 using std::floor;
181 numRingSamples_[j] = floor(2*M_PI*(Scalar(j)+0.5));
182 circleSamples_ += numRingSamples_[j];
183 }
184
185 // optimization because calling too many sin/cos can be really expensive
186 sincos_.resize(rSamples_);
187 for (int j = 0; j < rSamples_; ++j)
188 {
189 const auto numPoints = numRingSamples_[j];
190 sincos_[j].resize(2*numPoints);
191 Scalar t = 0 + 0.1;
192 for (std::size_t i = 0; i < numPoints; ++i)
193 {
194 using std::sin; using std::cos;
195 sincos_[j][2*i] = sin(t);
196 sincos_[j][2*i + 1] = cos(t);
197 t += 2*M_PI/numPoints;
198 if (t > 2*M_PI) t -= 2*M_PI;
199 }
200 }
201 }
202
203 // invert characteristic length (between 0 and 1)
204 // and make sure there is no integer wrap around for small ratios
205 std::size_t characteristicLengthToNumSamples_(const Scalar cl) const
206 {
207 using std::clamp; using std::min; using std::max; using std::ceil; using std::nexttoward;
208 const Scalar clampedCl = clamp(cl, std::numeric_limits<Scalar>::min(), 1.0);
209 const Scalar floatNumSamples = ceil(1.0/clampedCl);
210 const Scalar largestValidFloat = nexttoward(std::numeric_limits<std::size_t>::max(), 0.0);
211 return static_cast<std::size_t>(min(floatNumSamples, largestValidFloat));
212 }
213
214 bool zStepFixed_ = false;
215 std::size_t zSamples_, rSamples_, numPoints_, circleSamples_;
216 std::vector<Scalar> integrationElement_;
217 std::vector<GlobalPosition> points_;
218 std::vector<std::size_t> numRingSamples_;
219 std::vector<std::vector<Scalar>> sincos_;
220};
221
222namespace Detail {
224template<class GlobalPosition>
225inline bool pointInEllipse(const GlobalPosition& p,
226 const GlobalPosition& center,
227 const GlobalPosition& firstAxis,
228 const GlobalPosition& secondAxis,
229 const GlobalPosition& normal,
230 const typename GlobalPosition::value_type a,
231 const typename GlobalPosition::value_type b)
232{
233 const auto d = p-center;
234 // early return if point is not on the ellipse plane
235 if (d*normal > 1e-7*a)
236 return false;
237
238 // check if it's actually within the ellipse
239 const auto da = (d*firstAxis);
240 const auto db = (d*secondAxis);
241
242 return (da*da/(a*a) + db*db/(b*b) < 1.0);
243}
244
246template<class GlobalPosition>
247inline std::pair<std::vector<GlobalPosition>, typename GlobalPosition::value_type>
248ellipseIntegrationPoints(const GlobalPosition& center,
249 const GlobalPosition& firstUnitAxis,
250 const GlobalPosition& secondUnitAxis,
251 typename GlobalPosition::value_type a,
252 typename GlobalPosition::value_type b,
253 const GlobalPosition& normal,
254 typename GlobalPosition::value_type characteristicLength)
255{
256 // choose step size in a-direction
257 const auto aStep = characteristicLength;
258
259 using std::floor;
260 const std::size_t aSamples = std::max<std::size_t>(floor(2*a/aStep), 1);
261 const std::size_t bSamples = std::max<std::size_t>(floor(2*b/aStep), 1);
262 const auto bStep = 2*b / bSamples;
263
264 // reserve upper limit estimate for memory needed
265 std::vector<GlobalPosition> points;
266 points.reserve(aSamples*bSamples);
267
268 // walk over lattice grid and reject points outside the ellipse
269 auto startAB = center;
270 startAB.axpy(-a + 0.5*aStep, firstUnitAxis);
271 startAB.axpy(-b + 0.5*bStep, secondUnitAxis);
272 for (std::size_t as = 0; as < aSamples; ++as)
273 {
274 auto posA = startAB;
275 posA.axpy(as*aStep, firstUnitAxis);
276 for (std::size_t bs = 0; bs < bSamples; ++bs)
277 {
278 auto pos = posA;
279 pos.axpy(bs*bStep, secondUnitAxis);
280 if (pointInEllipse(pos, center, firstUnitAxis, secondUnitAxis, normal, a, b))
281 points.emplace_back(std::move(pos));
282 }
283 }
284 // return points and integration element
285 return {points, aStep*bStep};
286}
287} // end namespace Detail
288
300template<class Scalar>
302{
303 using GlobalPosition = Dune::FieldVector<Scalar, 3>;
304
305public:
311 explicit EllipticCylinderIntegration(const Scalar relCharLength)
312 : relCharLength_(relCharLength)
313 {}
314
323 void setGeometry(const GlobalPosition& bottomCenter,
324 const GlobalPosition& topCenter,
325 const GlobalPosition& firstAxis,
326 const GlobalPosition& secondAxis,
327 int verbosity = 0)
328 {
329 const auto a = firstAxis.two_norm();
330 const auto b = secondAxis.two_norm();
331 const auto characteristicLength = relCharLength_*a;
332
333 auto firstUnitAxis = firstAxis; firstUnitAxis /= a;
334 auto secondUnitAxis = secondAxis; secondUnitAxis /= b;
335 const auto normal = crossProduct(firstUnitAxis, secondUnitAxis);
336
337 auto zAxis = (topCenter-bottomCenter);
338 const auto length = zAxis.two_norm();
339 zAxis /= length;
340 const auto height = (zAxis*normal)*length;
341 using std::floor;
342 const std::size_t zSamples = std::max<std::size_t>(floor(height/characteristicLength), 1);
343 const auto zStep = length / Scalar(zSamples);
344 const auto zStepFactor = length/height;
345
346 // walk over lattice grid and reject points outside the ellipse
347 auto startZ = bottomCenter;
348 startZ.axpy(0.5*zStep, zAxis);
349 auto [ellipseIPs, ellipseIntegrationElement]
350 = Detail::ellipseIntegrationPoints(startZ, firstUnitAxis, secondUnitAxis, a, b, normal, characteristicLength);
351
352 // compute total number of points
353 const auto abPoints = ellipseIPs.size();
354 numPoints_ = abPoints*zSamples;
355
356 // first move in the first layer of ellipse integration points
357 points_.clear();
358 points_.reserve(numPoints_);
359 std::move(ellipseIPs.begin(), ellipseIPs.end(), std::back_inserter(points_));
360
361 // then extrude along the z axis
362 auto zStepVector = zAxis; zStepVector *= zStep;
363 for (std::size_t zs = 1; zs < zSamples; ++zs)
364 std::transform(points_.end() - abPoints, points_.end(), std::back_inserter(points_),
365 [&](const auto& p){ return p + zStepVector; });
366
367 // computation and error correction for integral element to obtain exact integral of constant functions
368 integrationElement_ = ellipseIntegrationElement*zStep/zStepFactor;
369 const auto meanLocalError = (height*a*b*M_PI - integrationElement_*numPoints_)/Scalar(numPoints_);
370 integrationElement_ += meanLocalError;
371
372 if (verbosity > 0)
373 {
374 std::cout << "EllipticCylinderIntegration -- number of integration points: " << numPoints_ <<"\n";
375 if (verbosity > 1)
376 std::cout << " -- a: " << a << ", b: " << b << ", h: " << height << "\n"
377 << " -- volume: " << integrationElement_*numPoints_ << " (expected " << height*a*b*M_PI << ")\n"
378 << " -- corrected a volume error of: " << meanLocalError/integrationElement_*100 << "%\n";
379 }
380 }
381
383 Scalar integrationElement(std::size_t i) const
384 { return integrationElement_; }
385
387 const GlobalPosition& integrationPoint(std::size_t i) const
388 { return points_[i]; }
389
391 std::size_t size() const
392 { return numPoints_; }
393
394private:
395 const Scalar relCharLength_;
396 std::size_t numPoints_;
397 Scalar integrationElement_;
398 std::vector<GlobalPosition> points_;
399};
400
411template<class Scalar>
413{
414 using GlobalPosition = Dune::FieldVector<Scalar, 3>;
415
416public:
422 explicit EllipseIntegration(const Scalar relCharLength)
423 : relCharLength_(relCharLength)
424 {}
425
433 void setGeometry(const GlobalPosition& center,
434 const GlobalPosition& firstAxis,
435 const GlobalPosition& secondAxis,
436 int verbosity = 0)
437 {
438 const auto a = firstAxis.two_norm();
439 const auto b = secondAxis.two_norm();
440 const auto characteristicLength = relCharLength_*a;
441
442 auto firstUnitAxis = firstAxis; firstUnitAxis /= a;
443 auto secondUnitAxis = secondAxis; secondUnitAxis /= b;
444 const auto normal = crossProduct(firstUnitAxis, secondUnitAxis);
445
446 // generate integration points
447 std::tie(points_, integrationElement_)
448 = Detail::ellipseIntegrationPoints(center, firstUnitAxis, secondUnitAxis, a, b, normal, characteristicLength);
449
450 // store number of sample points
451 const auto abPoints = points_.size();
452 numPoints_ = abPoints;
453
454 // computation and error correction for integral element to obtain exact integral of constant functions
455 const auto meanLocalError = (a*b*M_PI - integrationElement_*numPoints_)/Scalar(numPoints_);
456 integrationElement_ += meanLocalError;
457
458 if (verbosity > 0)
459 {
460 std::cout << "EllipseIntegration -- number of integration points: " << numPoints_ << "\n";
461 if (verbosity > 1)
462 std::cout << " -- a: " << a << ", b: " << b << "\n"
463 << " -- area: " << integrationElement_*numPoints_ << " (expected " << a*b*M_PI << ")\n"
464 << " -- corrected an area error of: " << meanLocalError/integrationElement_*100 << "%\n";
465 }
466 }
467
469 Scalar integrationElement(std::size_t i) const
470 { return integrationElement_; }
471
473 const GlobalPosition& integrationPoint(std::size_t i) const
474 { return points_[i]; }
475
477 std::size_t size() const
478 { return numPoints_; }
479
480private:
481 const Scalar relCharLength_;
482 std::size_t numPoints_;
483 Scalar integrationElement_;
484 std::vector<GlobalPosition> points_;
485};
486
487} // end namespace Dumux::EmbeddedCoupling
488
489#endif
Define some often used mathematical functions.
Helper function to compute points on a circle.
Vector normal(const Vector &v)
Create a vector normal to the given one (v is expected to be non-zero)
Definition: normal.hh:36
Dune::FieldVector< Scalar, 3 > crossProduct(const Dune::FieldVector< Scalar, 3 > &vec1, const Dune::FieldVector< Scalar, 3 > &vec2)
Cross product of two vectors in three-dimensional Euclidean space.
Definition: math.hh:654
void circlePoints(std::vector< GlobalPosition > &points, const std::vector< Scalar > &sincos, const GlobalPosition &center, const GlobalPosition &normal, const Scalar radius)
Definition: circlepoints.hh:50
Definition: circlepoints.hh:36
std::pair< std::vector< GlobalPosition >, typename GlobalPosition::value_type > ellipseIntegrationPoints(const GlobalPosition &center, const GlobalPosition &firstUnitAxis, const GlobalPosition &secondUnitAxis, typename GlobalPosition::value_type a, typename GlobalPosition::value_type b, const GlobalPosition &normal, typename GlobalPosition::value_type characteristicLength)
construct evenly distributed integration points on an ellipse
Definition: cylinderintegration.hh:248
bool pointInEllipse(const GlobalPosition &p, const GlobalPosition &center, const GlobalPosition &firstAxis, const GlobalPosition &secondAxis, const GlobalPosition &normal, const typename GlobalPosition::value_type a, const typename GlobalPosition::value_type b)
check if a point is in an ellipse
Definition: cylinderintegration.hh:225
Helper class to integrate over a cylinder domain.
Definition: cylinderintegration.hh:60
CylinderIntegration(const Scalar rStep, const Scalar zStep)
Constructor.
Definition: cylinderintegration.hh:81
void setGeometry(const GlobalPosition &bottomCenter, const GlobalPosition &topCenter, const Scalar radius, int verbosity=0)
Set the geometry of the cylinder.
Definition: cylinderintegration.hh:97
std::size_t size() const
The number of integration points.
Definition: cylinderintegration.hh:168
Scalar integrationElement(std::size_t i) const
The integration element of the ith integration point.
Definition: cylinderintegration.hh:160
const GlobalPosition & integrationPoint(std::size_t i) const
The ith integration point.
Definition: cylinderintegration.hh:164
CylinderIntegration(const Scalar rStep)
Constructor.
Definition: cylinderintegration.hh:69
Helper class to integrate over an elliptic cylinder domain.
Definition: cylinderintegration.hh:302
void setGeometry(const GlobalPosition &bottomCenter, const GlobalPosition &topCenter, const GlobalPosition &firstAxis, const GlobalPosition &secondAxis, int verbosity=0)
Set the geometry of elliptic cylinder.
Definition: cylinderintegration.hh:323
const GlobalPosition & integrationPoint(std::size_t i) const
obtain ith integration point
Definition: cylinderintegration.hh:387
Scalar integrationElement(std::size_t i) const
obtain ith integration element
Definition: cylinderintegration.hh:383
EllipticCylinderIntegration(const Scalar relCharLength)
Constructor.
Definition: cylinderintegration.hh:311
std::size_t size() const
number of samples points
Definition: cylinderintegration.hh:391
Helper class to integrate over an elliptic domain.
Definition: cylinderintegration.hh:413
const GlobalPosition & integrationPoint(std::size_t i) const
obtain ith integration point
Definition: cylinderintegration.hh:473
EllipseIntegration(const Scalar relCharLength)
Constructor.
Definition: cylinderintegration.hh:422
Scalar integrationElement(std::size_t i) const
obtain ith integration element
Definition: cylinderintegration.hh:469
void setGeometry(const GlobalPosition &center, const GlobalPosition &firstAxis, const GlobalPosition &secondAxis, int verbosity=0)
set geometry of an ellipse
Definition: cylinderintegration.hh:433
std::size_t size() const
number of integration points
Definition: cylinderintegration.hh:477