Line | Branch | Exec | Source |
---|---|---|---|
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 | // SPDX-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder | ||
5 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
6 | // | ||
7 | /*! | ||
8 | * \file | ||
9 | * \ingroup BoxDFMModel | ||
10 | * \brief Helper class constructing the dual grid finite volume geometries | ||
11 | * for the box discrete fracture model. | ||
12 | */ | ||
13 | #ifndef DUMUX_POROUSMEDIUMFLOW_BOXDFM_GEOMETRY_HELPER_HH | ||
14 | #define DUMUX_POROUSMEDIUMFLOW_BOXDFM_GEOMETRY_HELPER_HH | ||
15 | |||
16 | #include <dune/common/fvector.hh> | ||
17 | #include <dune/common/reservedvector.hh> | ||
18 | #include <dune/geometry/multilineargeometry.hh> | ||
19 | |||
20 | #include <dumux/discretization/box/boxgeometryhelper.hh> | ||
21 | |||
22 | namespace Dumux { | ||
23 | |||
24 | template <class ct> | ||
25 | struct BoxDfmMLGeometryTraits : public Dune::MultiLinearGeometryTraits<ct> | ||
26 | { | ||
27 | // we use static vectors to store the corners as we know | ||
28 | // the number of corners in advance (2^(mydim) corners (1<<(mydim)) | ||
29 | // However, on fracture scvs the number might be smaller (use ReservedVector) | ||
30 | template< int mydim, int cdim > | ||
31 | struct CornerStorage | ||
32 | { | ||
33 | using Type = Dune::ReservedVector< Dune::FieldVector< ct, cdim >, (1<<(mydim)) >; | ||
34 | }; | ||
35 | |||
36 | // we know all scvfs will have the same geometry type | ||
37 | template< int mydim > | ||
38 | struct hasSingleGeometryType | ||
39 | { | ||
40 | static const bool v = true; | ||
41 | static const unsigned int topologyId = Dune::GeometryTypes::cube(mydim).id(); | ||
42 | }; | ||
43 | }; | ||
44 | |||
45 | //! Create sub control volumes and sub control volume face geometries | ||
46 | template<class GridView, int dim, class ScvType, class ScvfType> | ||
47 | class BoxDfmGeometryHelper; | ||
48 | |||
49 | //! A class to create sub control volume and sub control volume face geometries per element | ||
50 | template <class GridView, class ScvType, class ScvfType> | ||
51 | class BoxDfmGeometryHelper<GridView, 2, ScvType, ScvfType> : public BoxGeometryHelper<GridView, 2, ScvType, ScvfType> | ||
52 | { | ||
53 | using ParentType = BoxGeometryHelper<GridView, 2, ScvType, ScvfType>; | ||
54 | |||
55 | using Intersection = typename GridView::Intersection; | ||
56 | using ScvfCornerStorage = typename ScvfType::Traits::CornerStorage; | ||
57 | |||
58 | static constexpr auto dim = GridView::dimension; | ||
59 | using Scalar = typename GridView::ctype; | ||
60 | |||
61 | public: | ||
62 | |||
63 | //! Pull up constructor of base class | ||
64 |
2/4✓ Branch 1 taken 1264764 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1264764 times.
✗ Branch 5 not taken.
|
5059056 | using ParentType::ParentType; |
65 | |||
66 | //! Get the corners of the (d-1)-dimensional fracture scvf | ||
67 | 129342 | ScvfCornerStorage getFractureScvfCorners(unsigned int localFacetIndex, | |
68 | unsigned int) const | ||
69 | { | ||
70 | 129342 | const auto& geo = this->elementGeometry(); | |
71 |
0/2✗ Branch 1 not taken.
✗ Branch 2 not taken.
|
129342 | const auto ref = referenceElement(geo); |
72 |
0/4✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
258684 | return ScvfCornerStorage({ geo.global(ref.position(localFacetIndex, 1)) }); |
73 | } | ||
74 | |||
75 | //! get fracture scvf normal vector (simply the unit vector of the edge) | ||
76 | //! The third argument is for compatibility reasons with the 3d case! | ||
77 | typename ScvfType::Traits::GlobalPosition | ||
78 | 258684 | fractureNormal(const ScvfCornerStorage& p, | |
79 | const Intersection& is, | ||
80 | unsigned int edgeIndexInIntersection) const | ||
81 | { | ||
82 | 258684 | const auto& geo = this->elementGeometry(); | |
83 | 258684 | const auto ref = referenceElement(geo); | |
84 |
2/2✓ Branch 0 taken 2142 times.
✓ Branch 1 taken 127200 times.
|
388026 | const auto v0 = ref.subEntity(is.indexInInside(), 1, 0, dim); |
85 |
2/2✓ Branch 0 taken 2142 times.
✓ Branch 1 taken 127200 times.
|
388026 | const auto v1 = ref.subEntity(is.indexInInside(), 1, 1, dim); |
86 | 258684 | auto normal = geo.corner(v1) - geo.corner(v0); | |
87 | 517368 | normal /= normal.two_norm(); | |
88 | 258684 | return normal; | |
89 | } | ||
90 | }; | ||
91 | |||
92 | //! A class to create sub control volume and sub control volume face geometries per element | ||
93 | template <class GridView, class ScvType, class ScvfType> | ||
94 | class BoxDfmGeometryHelper<GridView, 3, ScvType, ScvfType> : public BoxGeometryHelper<GridView, 3, ScvType, ScvfType> | ||
95 | { | ||
96 | using ParentType = BoxGeometryHelper<GridView, 3, ScvType, ScvfType>; | ||
97 | |||
98 | using Intersection = typename GridView::Intersection; | ||
99 | using ScvfCornerStorage = typename ScvfType::Traits::CornerStorage; | ||
100 | |||
101 | static constexpr auto dim = GridView::dimension; | ||
102 | static constexpr auto dimWorld = GridView::dimensionworld; | ||
103 | using Scalar = typename GridView::ctype; | ||
104 | |||
105 | public: | ||
106 | |||
107 | //! Pull up constructor of base class | ||
108 |
2/4✓ Branch 1 taken 136962 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 136962 times.
✗ Branch 5 not taken.
|
547848 | using ParentType::ParentType; |
109 | |||
110 | //! Create the sub control volume face geometries on an intersection marked as fracture | ||
111 | 83160 | ScvfCornerStorage getFractureScvfCorners(unsigned int localFacetIndex, | |
112 | unsigned int indexInFacet) const | ||
113 | { | ||
114 | 83160 | constexpr int facetCodim = 1; | |
115 | |||
116 | // we have to use the corresponding facet geometry as the intersection geometry | ||
117 | // might be rotated or flipped. This makes sure that the corners (dof location) | ||
118 | // and corresponding scvfs are sorted in the same way | ||
119 | using Dune::referenceElement; | ||
120 | 83160 | const auto& geo = this->elementGeometry(); | |
121 | 83160 | const auto type = referenceElement(geo).type(localFacetIndex, facetCodim); | |
122 |
1/2✓ Branch 0 taken 83160 times.
✗ Branch 1 not taken.
|
83160 | if (type == Dune::GeometryTypes::triangle) |
123 | { | ||
124 | using Corners = Detail::Box::ScvfCorners<Dune::GeometryTypes::triangle>; | ||
125 | 166320 | return Detail::Box::subEntityKeyToCornerStorage<ScvfCornerStorage>(geo, localFacetIndex, facetCodim, Corners::keys[indexInFacet]); | |
126 | } | ||
127 | ✗ | else if (type == Dune::GeometryTypes::quadrilateral) | |
128 | { | ||
129 | using Corners = Detail::Box::ScvfCorners<Dune::GeometryTypes::quadrilateral>; | ||
130 | ✗ | return Detail::Box::subEntityKeyToCornerStorage<ScvfCornerStorage>(geo, localFacetIndex, facetCodim, Corners::keys[indexInFacet]); | |
131 | } | ||
132 | else | ||
133 | ✗ | DUNE_THROW(Dune::NotImplemented, "Box fracture scvf geometries for dim=" << dim | |
134 | << " dimWorld=" << dimWorld | ||
135 | << " type=" << type); | ||
136 | } | ||
137 | |||
138 | //! get fracture scvf normal vector | ||
139 | typename ScvfType::Traits::GlobalPosition | ||
140 | 83160 | fractureNormal(const ScvfCornerStorage& scvfCorners, | |
141 | const Intersection& is, | ||
142 | unsigned int edgeIndexInIntersection) const | ||
143 | { | ||
144 | 83160 | const auto& geo = this->elementGeometry(); | |
145 | 83160 | const auto refElement = referenceElement(geo); | |
146 | |||
147 | // first get the intersection corners (maximum "4" is for quadrilateral face) | ||
148 | 83160 | typename ScvfType::Traits::GlobalPosition c[4]; | |
149 | |||
150 | 124740 | const auto corners = refElement.size(is.indexInInside(), 1, dim); | |
151 |
2/2✓ Branch 0 taken 249480 times.
✓ Branch 1 taken 83160 times.
|
332640 | for (int i = 0; i < corners; ++i) |
152 | { | ||
153 | 374220 | const auto vIdxLocal = refElement.subEntity(is.indexInInside(), 1, i, dim); | |
154 | 249480 | c[i] = geo.corner(vIdxLocal); | |
155 | } | ||
156 | |||
157 | // compute edge vector depending on number of corners | ||
158 | 83160 | const auto gridEdge = [&] () | |
159 | { | ||
160 | // triangles | ||
161 |
1/2✓ Branch 0 taken 83160 times.
✗ Branch 1 not taken.
|
83160 | if (corners == 3) |
162 | { | ||
163 | 27720 | if (edgeIndexInIntersection == 0) return c[1]-c[0]; | |
164 |
2/2✓ Branch 0 taken 27720 times.
✓ Branch 1 taken 27720 times.
|
55440 | else if (edgeIndexInIntersection == 1) return c[2]-c[0]; |
165 |
1/2✓ Branch 0 taken 27720 times.
✗ Branch 1 not taken.
|
27720 | else if (edgeIndexInIntersection == 2) return c[2]-c[1]; |
166 | ✗ | else DUNE_THROW(Dune::InvalidStateException, "Invalid edge index"); | |
167 | } | ||
168 | ✗ | else if (corners == 4) | |
169 | { | ||
170 | ✗ | if (edgeIndexInIntersection == 0) return c[2]-c[0]; | |
171 | ✗ | else if (edgeIndexInIntersection == 1) return c[3]-c[1]; | |
172 | ✗ | else if (edgeIndexInIntersection == 2) return c[1]-c[0]; | |
173 | ✗ | else if (edgeIndexInIntersection == 3) return c[3]-c[2]; | |
174 | ✗ | else DUNE_THROW(Dune::InvalidStateException, "Invalid edge index"); | |
175 | } | ||
176 | else | ||
177 | ✗ | DUNE_THROW(Dune::InvalidStateException, "Invalid face geometry"); | |
178 |
2/4✓ Branch 1 taken 27720 times.
✓ Branch 2 taken 55440 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
166320 | } (); |
179 | |||
180 | // compute lower edge of the scvf | ||
181 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 83160 times.
|
83160 | assert(scvfCorners.size() == 2); |
182 | 249480 | const auto scvfEdge = scvfCorners[1]-scvfCorners[0]; | |
183 | |||
184 | // compute scvf normal via 2 cross products | ||
185 | 83160 | const auto faceN = crossProduct(gridEdge, scvfEdge); | |
186 | 83160 | auto n = crossProduct(scvfEdge, faceN); | |
187 | 166320 | n /= n.two_norm(); | |
188 | 83160 | return n; | |
189 | } | ||
190 | }; | ||
191 | |||
192 | } // end namespace Dumux | ||
193 | |||
194 | #endif | ||
195 |