190 lines
7.1 KiB
C++
190 lines
7.1 KiB
C++
/*
|
|
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
|
|
|
This file is part of the Open Porous Media project (OPM).
|
|
|
|
OPM is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
OPM is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifndef OPM_FACEQUADRATURE_HEADER_INCLUDED
|
|
#define OPM_FACEQUADRATURE_HEADER_INCLUDED
|
|
|
|
#include <opm/common/ErrorMacros.hpp>
|
|
#include <opm/core/grid.h>
|
|
#include <cmath>
|
|
|
|
namespace Opm
|
|
{
|
|
|
|
|
|
namespace {
|
|
/// Calculates the cross product of two 3-vectors, represented as
|
|
/// 3-element arrays. Calculates res = a X b. The res array must
|
|
/// already be allocated with room for 3 elements.
|
|
inline void cross(const double* a, const double* b, double* res)
|
|
{
|
|
res[0] = a[1]*b[2] - a[2]*b[1];
|
|
res[1] = a[2]*b[0] - a[0]*b[2];
|
|
res[2] = a[0]*b[1] - a[1]*b[0];
|
|
}
|
|
|
|
/// Calculates the area of a triangle consisting of 3 vertices
|
|
/// with 3-dimensional coordinates
|
|
inline double triangleArea3d(const double* p0,
|
|
const double* p1,
|
|
const double* p2)
|
|
{
|
|
double a[3] = { p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2] };
|
|
double b[3] = { p2[0] - p0[0], p2[1] - p0[1], p2[2] - p0[2] };
|
|
double cr[3];
|
|
cross(a, b, cr);
|
|
return 0.5*std::sqrt(cr[0]*cr[0] + cr[1]*cr[1] + cr[2]*cr[2]);
|
|
}
|
|
} // anonymous namespace
|
|
|
|
|
|
/// A class providing numerical quadrature for faces.
|
|
/// In general: \int_{face} g(x) dx = \sum_{i=0}^{n-1} w_i g(x_i).
|
|
/// Note that this class does multiply weights by face area,
|
|
/// so weights always sum to face area.
|
|
///
|
|
/// Degree 1 method:
|
|
/// Midpoint (centroid) method.
|
|
/// n = 1, w_0 = face area, x_0 = face centroid
|
|
///
|
|
/// Degree 2 method for 2d:
|
|
/// Simpson's method (actually this is degree 3).
|
|
///
|
|
/// Degree 2 method for 3d:
|
|
/// Based on subdivision of the face into triangles,
|
|
/// with the centroid as a common vertex, and the triangle
|
|
/// edge midpoint rule.
|
|
/// Triangle i consists of the centroid C, nodes N_i and N_{i+1}.
|
|
/// Its area is A_i.
|
|
/// n = 2 * nn (nn = num nodes in face)
|
|
/// For i = 0..(nn-1):
|
|
/// w_i = 1/3 A_i.
|
|
/// w_{nn+i} = 1/3 A_{i-1} + 1/3 A_i
|
|
/// x_i = (N_i + N_{i+1})/2
|
|
/// x_{nn+i} = (C + N_i)/2
|
|
/// All N and A indices are interpreted cyclic, modulus nn.
|
|
class FaceQuadrature
|
|
{
|
|
public:
|
|
FaceQuadrature(const UnstructuredGrid& grid,
|
|
const int face,
|
|
const int degree)
|
|
: grid_(grid), face_(face), degree_(degree)
|
|
{
|
|
if (grid_.dimensions > 3) {
|
|
OPM_THROW(std::runtime_error, "FaceQuadrature only implemented for up to 3 dimensions.");
|
|
}
|
|
if (degree_ > 2) {
|
|
OPM_THROW(std::runtime_error, "FaceQuadrature exact for polynomial degrees > 2 not implemented.");
|
|
}
|
|
}
|
|
|
|
int numQuadPts() const
|
|
{
|
|
if (degree_ < 2 || grid_.dimensions < 2) {
|
|
return 1;
|
|
}
|
|
// Degree 2 case.
|
|
if (grid_.dimensions == 2) {
|
|
return 3;
|
|
} else {
|
|
return 2 * (grid_.face_nodepos[face_ + 1] - grid_.face_nodepos[face_]);
|
|
}
|
|
}
|
|
|
|
void quadPtCoord(const int index, double* coord) const
|
|
{
|
|
const int dim = grid_.dimensions;
|
|
const double* fc = grid_.face_centroids + dim*face_;
|
|
if (degree_ < 2 || dim < 2) {
|
|
std::copy(fc, fc + dim, coord);
|
|
return;
|
|
}
|
|
// Degree 2 case.
|
|
const int nn = grid_.face_nodepos[face_ + 1] - grid_.face_nodepos[face_];
|
|
const int* fnodes = grid_.face_nodes + grid_.face_nodepos[face_];
|
|
const double* nc = grid_.node_coordinates;
|
|
if (dim == 2) {
|
|
assert(nn == 2);
|
|
const double* pa[3] = { nc + dim*fnodes[0], fc, nc + dim*fnodes[1] };
|
|
std::copy(pa[index], pa[index] + dim, coord);
|
|
return;
|
|
}
|
|
assert(dim == 3);
|
|
if (index < nn) {
|
|
// Boundary edge midpoint.
|
|
const int node0 = fnodes[index];
|
|
const int node1 = fnodes[(index + 1)%nn];
|
|
for (int dd = 0; dd < dim; ++dd) {
|
|
coord[dd] = 0.5*(nc[dim*node0 + dd] + nc[dim*node1 + dd]);
|
|
}
|
|
} else {
|
|
// Interiour edge midpoint.
|
|
// Recall that index is now in [nn, 2*nn).
|
|
const int node = fnodes[index - nn];
|
|
for (int dd = 0; dd < dim; ++dd) {
|
|
coord[dd] = 0.5*(nc[dim*node + dd] + fc[dd]);
|
|
}
|
|
}
|
|
}
|
|
|
|
double quadPtWeight(const int index) const
|
|
{
|
|
if (degree_ < 2) {
|
|
return grid_.face_areas[face_];
|
|
}
|
|
// Degree 2 case.
|
|
const int dim = grid_.dimensions;
|
|
if (dim == 2) {
|
|
const double simpsonw[3] = { 1.0/6.0, 4.0/6.0, 1.0/6.0 };
|
|
return grid_.face_areas[face_]*simpsonw[index];
|
|
}
|
|
assert(dim == 3);
|
|
const double* fc = grid_.face_centroids + dim*face_;
|
|
const int nn = grid_.face_nodepos[face_ + 1] - grid_.face_nodepos[face_];
|
|
const int* fnodes = grid_.face_nodes + grid_.face_nodepos[face_];
|
|
const double* nc = grid_.node_coordinates;
|
|
if (index < nn) {
|
|
// Boundary edge midpoint.
|
|
const int node0 = fnodes[index];
|
|
const int node1 = fnodes[(index + 1)%nn];
|
|
const double area = triangleArea3d(nc + dim*node1, nc + dim*node0, fc);
|
|
return area / 3.0;
|
|
} else {
|
|
// Interiour edge midpoint.
|
|
// Recall that index is now in [nn, 2*nn).
|
|
const int node0 = fnodes[(index - 1) % nn];
|
|
const int node1 = fnodes[index - nn];
|
|
const int node2 = fnodes[(index + 1) % nn];
|
|
const double area0 = triangleArea3d(nc + dim*node1, nc + dim*node0, fc);
|
|
const double area1 = triangleArea3d(nc + dim*node2, nc + dim*node1, fc);
|
|
return (area0 + area1) / 3.0;
|
|
}
|
|
}
|
|
|
|
private:
|
|
const UnstructuredGrid& grid_;
|
|
const int face_;
|
|
const int degree_;
|
|
};
|
|
|
|
} // namespace Opm
|
|
|
|
#endif // OPM_FACEQUADRATURE_HEADER_INCLUDED
|