copied the templates
This commit is contained in:
commit
46636593b5
63 changed files with 570970 additions and 0 deletions
891
mgrid_2/elements.h
Normal file
891
mgrid_2/elements.h
Normal file
|
|
@ -0,0 +1,891 @@
|
|||
#pragma once
|
||||
|
||||
#include "geom.h"
|
||||
#include "getmatrix.h"
|
||||
#include "userset.h"
|
||||
#include "utils.h"
|
||||
#include "vdop.h"
|
||||
|
||||
#include <array>
|
||||
//#include <cassert>
|
||||
#include <type_traits>
|
||||
#include <variant> // class variant
|
||||
#include <vector>
|
||||
|
||||
//!< linear test functions for P1_3D element
|
||||
static
|
||||
std::array<std::function<double(double,double,double)>,4> const
|
||||
phi_P1_3d{
|
||||
[](double xi_0,double xi_1,double xi_2) {return 1-xi_0-xi_1-xi_2;},
|
||||
[](double xi_0,double,double) {return xi_0;},
|
||||
[](double,double xi_1,double) {return xi_1;},
|
||||
[](double,double,double xi_2) {return xi_2;}
|
||||
};
|
||||
|
||||
//!< derivatives of linear test functions for P1_3D element
|
||||
static
|
||||
std::array<std::array<std::function<double(double,double,double)>,3> ,4> const
|
||||
//dphi_P1_3d{
|
||||
//[](double,double,double) {return -1.0;}, [](double,double,double) {return -1.0;}, [](double,double,double) {return -1.0;} ,
|
||||
//[](double,double,double) {return 1.0;}, [](double,double,double) {return 0.0;}, [](double,double,double) {return 0.0;} ,
|
||||
//[](double,double,double) {return 0.0;}, [](double,double,double) {return 1.0;}, [](double,double,double) {return 0.0;} ,
|
||||
//[](double,double,double) {return 0.0;}, [](double,double,double) {return 0.0;}, [](double,double,double) {return 1.0;}
|
||||
//};
|
||||
// Always use double {{}} for initializer list for array, see https://stackoverflow.com/questions/22501368/why-wasnt-a-double-curly-braces-syntax-preferred-for-constructors-taking-a-std
|
||||
dphi_P1_3d{{
|
||||
{{ [](double,double,double) {return -1.0;}, [](double,double,double) {return -1.0;}, [](double,double,double) {return -1.0;} }},
|
||||
{{ [](double,double,double) {return 1.0;}, [](double,double,double) {return 0.0;}, [](double,double,double) {return 0.0;} }},
|
||||
{{ [](double,double,double) {return 0.0;}, [](double,double,double) {return 1.0;}, [](double,double,double) {return 0.0;} }},
|
||||
{{ [](double,double,double) {return 0.0;}, [](double,double,double) {return 0.0;}, [](double,double,double) {return 1.0;} }}
|
||||
}};
|
||||
|
||||
//!< quadratic test functions for P2_3D element
|
||||
static
|
||||
std::array<std::function<double(double,double,double)>,10> const
|
||||
phi_P2_3d{
|
||||
[](double xi_0,double xi_1,double xi_2) {return phi_P1_3d[0](xi_0,xi_1,xi_2)*(2*phi_P1_3d[0](xi_0,xi_1,xi_2)-1);},
|
||||
[](double xi_0,double xi_1,double xi_2) {return phi_P1_3d[1](xi_0,xi_1,xi_2)*(2*phi_P1_3d[1](xi_0,xi_1,xi_2)-1);},
|
||||
[](double xi_0,double xi_1,double xi_2) {return phi_P1_3d[2](xi_0,xi_1,xi_2)*(2*phi_P1_3d[2](xi_0,xi_1,xi_2)-1);},
|
||||
[](double xi_0,double xi_1,double xi_2) {return phi_P1_3d[3](xi_0,xi_1,xi_2)*(2*phi_P1_3d[3](xi_0,xi_1,xi_2)-1);},
|
||||
[](double xi_0,double xi_1,double xi_2) {return 4*phi_P1_3d[0](xi_0,xi_1,xi_2)*phi_P1_3d[1](xi_0,xi_1,xi_2);},
|
||||
[](double xi_0,double xi_1,double xi_2) {return 4*phi_P1_3d[1](xi_0,xi_1,xi_2)*phi_P1_3d[2](xi_0,xi_1,xi_2);},
|
||||
[](double xi_0,double xi_1,double xi_2) {return 4*phi_P1_3d[0](xi_0,xi_1,xi_2)*phi_P1_3d[2](xi_0,xi_1,xi_2);},
|
||||
[](double xi_0,double xi_1,double xi_2) {return 4*phi_P1_3d[0](xi_0,xi_1,xi_2)*phi_P1_3d[3](xi_0,xi_1,xi_2);},
|
||||
[](double xi_0,double xi_1,double xi_2) {return 4*phi_P1_3d[1](xi_0,xi_1,xi_2)*phi_P1_3d[3](xi_0,xi_1,xi_2);},
|
||||
[](double xi_0,double xi_1,double xi_2) {return 4*phi_P1_3d[2](xi_0,xi_1,xi_2)*phi_P1_3d[3](xi_0,xi_1,xi_2);},
|
||||
};
|
||||
|
||||
|
||||
// static polymorphism ?
|
||||
// yes, via template argument for FEM_Matrix_2<ELEM>
|
||||
// R.Grimm: https://www.modernescpp.com/index.php/dynamic-and-static-polymorphism
|
||||
/**
|
||||
* Basis class for finite elements.
|
||||
*/
|
||||
class Element
|
||||
{
|
||||
public:
|
||||
constexpr Element(int ndim, int nvertices, int ndof)
|
||||
: _ndim(ndim), _nvertices(nvertices), _ndof(ndof) {}
|
||||
|
||||
virtual ~Element();
|
||||
//~Element() = default;
|
||||
|
||||
/**
|
||||
* @return Space dimension.
|
||||
*/
|
||||
constexpr int nDim_loc() const
|
||||
{return _ndim;}
|
||||
|
||||
/**
|
||||
* @return number of geometric vertices defining the element
|
||||
*/
|
||||
constexpr int nVertices_loc() const
|
||||
{return _nvertices;}
|
||||
|
||||
/**
|
||||
* @return number of degrees of freedem in element
|
||||
*/
|
||||
constexpr int nDOFs_loc() const
|
||||
{return _ndof;}
|
||||
|
||||
/**
|
||||
* Derives the global indices for the DOFs in each element from the
|
||||
* global indices of geometric vertices @p ia_geom.
|
||||
*
|
||||
* @param[in] ia_geom global geometric vertex indices[nelem*_nvertices]
|
||||
*
|
||||
* @return global indices for the DOFs [nelem*_ndof]
|
||||
*/
|
||||
virtual
|
||||
std::vector<int> getGlobalDOFIndices(std::vector<int> const &ia_geom) const = 0;
|
||||
|
||||
/**
|
||||
* Allocates the local finite element matrix.
|
||||
*
|
||||
* @return matrix [_ndof][_ndof]
|
||||
*/
|
||||
std::vector<std::vector<double>> createElemMatrix() const
|
||||
{
|
||||
return std::vector<std::vector<double>>(_ndof,std::vector<double>(_ndof,0.0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates the local finite element load vector.
|
||||
*
|
||||
* @return vector [_ndof]
|
||||
*/
|
||||
std::vector<double> createElemVector() const
|
||||
{
|
||||
return std::vector<double>(_ndof,0.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the element stiffness matrix @p ske and the element load vector @p fe
|
||||
* of one triangular element with linear shape functions.
|
||||
* @param[in] ial node indices of the element vertices [_nvertices]
|
||||
* @param[in] xc vector of node coordinates with x(2*k,2*k+1) as coordinates of node k
|
||||
* @param[out] ske element stiffness matrix [_ndof][_ndof]
|
||||
* @param[out] fe element load vector [_ndof]
|
||||
*/
|
||||
void CalcLaplace(
|
||||
int const ial[3],
|
||||
double const xc[],
|
||||
std::vector<std::vector<double>> &ske,
|
||||
std::vector<double> &fe) const;
|
||||
|
||||
/**
|
||||
* Calculates the element stiffness matrix @p ske and the element load vector @p fe
|
||||
* of one triangular element with linear shape functions.
|
||||
* @p fe will be calculated according to function @p f_func (x,y,z)
|
||||
* @param[in] ial node indices of the element vertices [_nvertices]
|
||||
* @param[in] xc vector of node coordinates with x(2*k,2*k+1) as coordinates of node k
|
||||
* @param[out] ske element stiffness matrix [_ndof][_ndof]
|
||||
* @param[out] fe element load vector [_ndof]
|
||||
* @param[out] f_func function @p f_func (x,y,z)
|
||||
*/
|
||||
void CalcLaplace(
|
||||
int const ial[3], double const xc[],
|
||||
std::vector<std::vector<double>> &ske, std::vector<double> &fe,
|
||||
const std::function<double(double, double, double)> &f_func
|
||||
) const;
|
||||
|
||||
private:
|
||||
int const _ndim; //!< space dimension {2,3}
|
||||
int const _nvertices; //!< number of geometric vertices
|
||||
int const _ndof; //!< number of degrees of freedom;
|
||||
// TODO: Add polynomial degree?
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Linear triangular P1-element.
|
||||
* The local vertex numbering follows Jung/Langer, Tabelle 4.3 (page 256).
|
||||
*/
|
||||
class P1_2d: public Element
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @param[in] ndof_v degrees of freedom per vertex
|
||||
*/
|
||||
constexpr
|
||||
P1_2d(int ndof_v=1)
|
||||
: Element(2,3,3*ndof_v) { assert(0 == nDOFs_loc() % nVertices_loc() ); }
|
||||
|
||||
virtual ~P1_2d() override {}
|
||||
|
||||
/**
|
||||
* Derives the global indices for the DOFs in each element from the
|
||||
* global indices of geometric vertices @p ia_geom.
|
||||
*
|
||||
* @param[in] ia_geom global geometric vertex indices[nelem*_nvertices]
|
||||
*
|
||||
* @return global indices for the DOFs [nelem*_ndof]
|
||||
*/
|
||||
std::vector<int> getGlobalDOFIndices(std::vector<int> const &ia_geom) const override;
|
||||
|
||||
/**
|
||||
* Calculates the element stiffness matrix @p ske and the element load vector @p fe
|
||||
* of one triangular element with linear shape functions.
|
||||
* @param[in] ial node indices of the element vertices [_nvertices]
|
||||
* @param[in] xc vector of node coordinates with x(2*k,2*k+1) as coordinates of node k
|
||||
* @param[out] ske element stiffness matrix [_ndof][_ndof]
|
||||
* @param[out] fe element load vector [_ndof]
|
||||
*/
|
||||
void CalcLaplace(
|
||||
int const ial[3],
|
||||
double const xc[],
|
||||
std::vector<std::vector<double>> &ske,
|
||||
std::vector<double> &fe) const;
|
||||
|
||||
/**
|
||||
* Calculates the element stiffness matrix @p ske and the element load vector @p fe
|
||||
* of one triangular element with linear shape functions.
|
||||
* @p fe will be calculated according to function @p f_func (x,y)
|
||||
* @param[in] ial node indices of the element vertices [_nvertices]
|
||||
* @param[in] xc vector of node coordinates with x(2*k,2*k+1) as coordinates of node k
|
||||
* @param[out] ske element stiffness matrix [_ndof][_ndof]
|
||||
* @param[out] fe element load vector [_ndof]
|
||||
* @param[out] f_func function @p f_func (x,y)
|
||||
*/
|
||||
void CalcLaplace(
|
||||
int const ial[3], double const xc[],
|
||||
std::vector<std::vector<double>> &ske, std::vector<double> &fe,
|
||||
const std::function<double(double, double)> &f_func
|
||||
) const;
|
||||
};
|
||||
|
||||
///**
|
||||
//* Linear triangular P1-element.
|
||||
//* The local vertex numbering follows Jung/Langer, Tabelle 4.3 (page 256).
|
||||
//*/
|
||||
//class P1_2d_1dof: public P1_2d
|
||||
//{
|
||||
//public:
|
||||
///**
|
||||
//* one degree of freedom per vertex
|
||||
//*/
|
||||
////constexpr P1_2d_1dof()
|
||||
////: Element(2,3,3*1) { }
|
||||
//P1_2d_1dof()
|
||||
//: P1_2d(1) { }
|
||||
|
||||
//virtual ~P1_2d_1dof() override {}
|
||||
//};
|
||||
|
||||
/**
|
||||
* Linear tetrahedral P1-element.
|
||||
* The local vertex numbering follows Jung/Langer, Tabelle 4.3 (page 256).
|
||||
*/
|
||||
class P1_3d: public Element
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @param[in] ndof_v degrees of freedom per vertex
|
||||
*/
|
||||
constexpr
|
||||
P1_3d(int ndof_v=1)
|
||||
: Element(3,4,4*ndof_v) { assert(0 == nDOFs_loc() % nVertices_loc() ); }
|
||||
|
||||
virtual ~P1_3d() override {}
|
||||
|
||||
/**
|
||||
* Derives the global indices for the DOFs in each element from the
|
||||
* global indices of geometric vertices @p ia_geom.
|
||||
*
|
||||
* @param[in] ia_geom global geometric vertex indices[nelem*_nvertices]
|
||||
*
|
||||
* @return global indices for the DOFs [nelem*_ndof]
|
||||
*/
|
||||
std::vector<int> getGlobalDOFIndices(std::vector<int> const &ia_geom) const override;
|
||||
|
||||
/**
|
||||
* Calculates the element stiffness matrix @p ske and the element load vector @p fe
|
||||
* of one triangular element with linear shape functions.
|
||||
* @param[in] ial node indices of the element vertices [_nvertices]
|
||||
* @param[in] xc vector of node coordinates with x(2*k,2*k+1) as coordinates of node k
|
||||
* @param[out] ske element stiffness matrix [_ndof][_ndof]
|
||||
* @param[out] fe element load vector [_ndof]
|
||||
*/
|
||||
void CalcLaplace(int const ial[4], double const xc[], std::vector<std::vector<double>> &ske, std::vector<double> &fe) const;
|
||||
|
||||
/**
|
||||
* Calculates the element stiffness matrix @p ske and the element load vector @p fe
|
||||
* of one triangular element with linear shape functions.
|
||||
* @p fe will be calculated according to function @p f_func (x,y,z)
|
||||
* @param[in] ial node indices of the element vertices [_nvertices]
|
||||
* @param[in] xc vector of node coordinates with x(2*k,2*k+1) as coordinates of node k
|
||||
* @param[out] ske element stiffness matrix [_ndof][_ndof]
|
||||
* @param[out] fe element load vector [_ndof]
|
||||
* @param[out] f_func function @p f_func (x,y,z)
|
||||
*/
|
||||
void CalcLaplace(
|
||||
int const ial[4], double const xc[],
|
||||
std::vector<std::vector<double>> &ske, std::vector<double> &fe,
|
||||
const std::function<double(double, double, double)> &f_func ) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Quadratic tetrahedral P2-element.
|
||||
*/
|
||||
class P2_3d: public Element
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @param[in] ndof_v degrees of freedom per vertex
|
||||
*/
|
||||
constexpr
|
||||
P2_3d(int ndof_v=1)
|
||||
: Element(3,10,10*ndof_v) { assert(0 == nDOFs_loc() % nVertices_loc() ); }
|
||||
|
||||
virtual ~P2_3d() override {}
|
||||
|
||||
/**
|
||||
* Derives the global indices for the DOFs in each element from the
|
||||
* global indices of geometric vertices @p ia_geom.
|
||||
*
|
||||
* @param[in] ia_geom global geometric vertex indices[nelem*_nvertices]
|
||||
*
|
||||
* @return global indices for the DOFs [nelem*_ndof]
|
||||
*/
|
||||
std::vector<int> getGlobalDOFIndices(std::vector<int> const &ia_geom) const override;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Combined P1-P2 tetrahedral element with vector values in P2 vertices.
|
||||
*
|
||||
* Local numbering of dofs: 4 scalar values (linear test function) followed
|
||||
* by 10 vector values (quadratic test function) consisting of 3 components.
|
||||
*
|
||||
*
|
||||
*/
|
||||
class P1_2vec_3d: public Element
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @param[in] ndof_v max. degrees of freedom per vertex
|
||||
*/
|
||||
constexpr
|
||||
P1_2vec_3d(int ndof_v=3)
|
||||
: Element(3,10,4+10*ndof_v) { }
|
||||
|
||||
virtual ~P1_2vec_3d() override {}
|
||||
|
||||
/**
|
||||
* Derives the global indices for the DOFs in each element from the
|
||||
* global indices of geometric vertices @p ia_geom.
|
||||
*
|
||||
* @param[in] ia_geom global geometric vertex indices[nelem*_nvertices]
|
||||
*
|
||||
* @return global indices for the DOFs [nelem*_ndof]
|
||||
*/
|
||||
std::vector<int> getGlobalDOFIndices(std::vector<int> const &ia_geom) const override;
|
||||
|
||||
/**
|
||||
* Calculates the element stiffness matrix @p ske and the element load vector @p fe
|
||||
* of one triangular element with linear shape functions.
|
||||
* @param[in] ial node indices of the element vertices [_nvertices]
|
||||
* @param[in] xc vector of node coordinates with x(2*k,2*k+1) as coordinates of node k
|
||||
* @param[out] ske element stiffness matrix [_ndof][_ndof]
|
||||
* @param[out] fe element load vector [_ndof]
|
||||
* @param[out] f_func function @p f_func (x,y,z)
|
||||
*/
|
||||
void CalcLaplace(int const ial[10], double const xc[],
|
||||
std::vector<std::vector<double>> &ske, std::vector<double> &fe,
|
||||
const std::function<double(double, double, double)> &f_func ) const;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Copies only the first @p col1 columns from matrix @p v2 [n* @p col2] with @p col1<= @p col2.
|
||||
*
|
||||
* @param[in] v2 data[n*col2]
|
||||
* @param[in] col1 #columns in output
|
||||
* @param[in] col2 #columns in input
|
||||
* @return data[n*col1]
|
||||
*/
|
||||
std::vector<int> shrinkColumns(std::vector<int> const &v2, int col2, int col1);
|
||||
|
||||
/**
|
||||
* Merges data @p v1 and data @p into a new vector/matrix.
|
||||
*
|
||||
* @param[in] v1 data[n*col1]
|
||||
* @param[in] col1 #columns in @p v1
|
||||
* @param[in] v2 data[n*col2]
|
||||
* @param[in] col2 #columns in @p v2
|
||||
* @return data[n*(col1+col2)]
|
||||
*/
|
||||
std::vector<int> mergeColumns(std::vector<int> const &v1, int col1, std::vector<int> const &v2, int col2);
|
||||
|
||||
/**
|
||||
* An index vector @p vin for scalar values is transferred into
|
||||
* an index vector for @p ndof_v DOFs per scalar.
|
||||
* An optional index @p offset is applied (useful for merging two index vectors).
|
||||
*
|
||||
* @param[in] vin index[n]
|
||||
* @param[in] ndof_v #DOFs per original index
|
||||
* @param[in] offset index offset for output indices
|
||||
* @return index[n*ndof_v]
|
||||
*
|
||||
* @see mergeColumns
|
||||
*/
|
||||
std::vector<int> indexVectorBlowUp(std::vector<int> const &vin, int ndof_v=1, int offset=0);
|
||||
|
||||
|
||||
//######################################################################
|
||||
|
||||
/**
|
||||
* FEM Matrix in CRS format (compressed row storage; also named CSR),
|
||||
* see an <a href="https://en.wikipedia.org/wiki/Sparse_matrix">introduction</a>.
|
||||
*/
|
||||
template <class ELEM=P1_3d()>
|
||||
class FEM_Matrix_2: public CRS_Matrix
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Initializes the CRS matrix structure from the given discretization in @p mesh.
|
||||
*
|
||||
* The sparse matrix pattern is generated but the values are 0.
|
||||
*
|
||||
* @param[in] mesh given discretization
|
||||
*
|
||||
* @warning A reference to the discretization @p mesh is stored inside this class.
|
||||
* Therefore, changing @p mesh outside requires also
|
||||
* to call method @p Derive_Matrix_Pattern explicitly.
|
||||
*
|
||||
* @see Derive_Matrix_Pattern
|
||||
*/
|
||||
explicit FEM_Matrix_2(Mesh const & mesh, ELEM const &elem=P1_3d());
|
||||
//explicit FEM_Matrix_2(Mesh const & mesh);
|
||||
|
||||
FEM_Matrix_2(FEM_Matrix_2 const &) = default;
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~FEM_Matrix_2() override;
|
||||
|
||||
/**
|
||||
* Read DOF connectivity information (g1,g2,g3)_i.
|
||||
* @return connectivity vector [nelems*ndofs].
|
||||
*/
|
||||
[[nodiscard]] const std::vector<int> &GetConnectivity() const
|
||||
{
|
||||
return _ia_dof;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read DOF connectivity information (g1,g2,g3)_i.
|
||||
* @return connectivity vector [nelems*ndofs].
|
||||
*/
|
||||
[[nodiscard]] const std::vector<int> &GetConnectivityGeom() const
|
||||
{
|
||||
return _mesh.GetConnectivity();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines all node to node connections from the dof based mesh.
|
||||
*
|
||||
* Faster than @p Node2NodeGraph_1().
|
||||
*
|
||||
* @return vector[k][] containing all connections of dof k, including to itself.
|
||||
*/
|
||||
std::vector<std::vector<int>> Node2NodeGraph() const
|
||||
{
|
||||
//std::cout << Nelems() << " " << NdofsElement() << " " << GetConnectivity().size() << std::endl;
|
||||
return ::Node2NodeGraph(Nelems(),NdofsElement(),GetConnectivity());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether the mesh is compatible with the choosen finite element type.
|
||||
* @return true iff compatible.
|
||||
*/
|
||||
bool CheckCompatibility() const;
|
||||
|
||||
constexpr int Nelems() const
|
||||
{ return _mesh.Nelems();}
|
||||
|
||||
//constexpr int Nnodes() const
|
||||
//{ return _mesh.Nnodes();}
|
||||
|
||||
constexpr int NdofsElement() const
|
||||
{ return _elem.nDOFs_loc(); }
|
||||
|
||||
constexpr int NverticesElement() const
|
||||
{ return _elem.nVertices_loc(); }
|
||||
|
||||
constexpr int Ndims() const
|
||||
{ return _elem.nDim_loc(); }
|
||||
|
||||
/**
|
||||
* Generates the sparse matrix pattern and overwrites the existing pattern.
|
||||
*
|
||||
* The sparse matrix pattern is generated but the values are 0.
|
||||
*/
|
||||
void Derive_Matrix_Pattern();
|
||||
//{
|
||||
////Derive_Matrix_Pattern_slow();
|
||||
//Derive_Matrix_Pattern_fast();
|
||||
//CheckRowSum();
|
||||
//}
|
||||
//void Derive_Matrix_Pattern_fast();
|
||||
//void Derive_Matrix_Pattern_slow();
|
||||
|
||||
|
||||
/**
|
||||
* Calculates the entries of f.e. stiffness matrix for the Laplace operator
|
||||
* and load/rhs vector @p f.
|
||||
* No memory is allocated.
|
||||
*
|
||||
* @param[in,out] f (preallocated) rhs/load vector
|
||||
* @warning Only linear elements (P1_2d, P1_3d) are supported.
|
||||
* @see P1_2d
|
||||
* @see P1_3d
|
||||
*/
|
||||
void CalculateLaplace(std::vector<double> &f);
|
||||
|
||||
/**
|
||||
* Calculates the entries of f.e. stiffness matrix for the Laplace operator in 2D
|
||||
* and load/rhs vector @p f according to function @p f_func,
|
||||
*
|
||||
* @param[in,out] f (preallocated) rhs/load vector
|
||||
* @param[in] f_func function f(x,y)
|
||||
*/
|
||||
void CalculateLaplace(
|
||||
std::vector<double> &f,
|
||||
std::function<double(double, double)> const &f_func);
|
||||
|
||||
/**
|
||||
* Calculates the entries of f.e. stiffness matrix for the Laplace operator in 3D
|
||||
* and load/rhs vector @p f according to function @p f_func,
|
||||
*
|
||||
* @param[in,out] f (preallocated) rhs/load vector
|
||||
* @param[in] f_func function f(x,y,z)
|
||||
*/
|
||||
void CalculateLaplace(
|
||||
std::vector<double> &f,
|
||||
std::function<double(double, double, double)> const &f_func);
|
||||
|
||||
/**
|
||||
* Adds the element stiffness matrix @p ske and the element load vector @p fe
|
||||
* of one triangular element with linear shape functions to the appropriate positions in
|
||||
* the stiffness matrix, stored as CSR matrix K(@p sk,@p id, @p ik).
|
||||
*
|
||||
* @param[in] ial node indices of the three element vertices
|
||||
* @param[in] ske element stiffness matrix
|
||||
* @param[in] fe element load vector
|
||||
* @param[in,out] f distributed local vector storing the right hand side
|
||||
*
|
||||
* @warning Algorithm assumes linear triangular elements (ndof_e==3).
|
||||
*/
|
||||
//void AddElem_3(int const ial[3], double const ske[3][3], double const fe[3], std::vector<double> &f);
|
||||
void AddElem(
|
||||
int const ial[],
|
||||
std::vector<std::vector<double>> const& ske,
|
||||
std::vector<double> const &fe,
|
||||
std::vector<double> &f);
|
||||
|
||||
/**
|
||||
* Applies Dirichlet boundary conditions to stiffness matrix and to load vector @p f.
|
||||
* The <a href="https://www.jstor.org/stable/2005611?seq=1#metadata_info_tab_contents">penalty method</a>
|
||||
* is used for incorporating the given values @p u.
|
||||
*
|
||||
* @param[in] u (global) vector with Dirichlet data
|
||||
* @param[in,out] f load vector
|
||||
*/
|
||||
void ApplyDirichletBC(std::vector<double> const &u, std::vector<double> &f);
|
||||
|
||||
/**
|
||||
* Applies Dirichlet boundary conditions to stiffness matrix and to load vector @p f.
|
||||
* The <a href="https://www.jstor.org/stable/2005611?seq=1#metadata_info_tab_contents">penalty method</a>
|
||||
* is used for incorporating the given values @p u
|
||||
* at the surface of the bounding box [@p xl, @p xh]x[@p yl, @p yh]x[@p zl, @p zh].
|
||||
*
|
||||
* Skipping @p zl and @p zh reduced the box to a rectangle in 2D.
|
||||
*
|
||||
* @param[in] u (global) vector with Dirichlet data
|
||||
* @param[in,out] f load vector
|
||||
* @param[in] xl lower value x-bounds
|
||||
* @param[in] xh higher value x-bounds
|
||||
* @param[in] yl lower value y-bounds
|
||||
* @param[in] yh higher value y-bounds
|
||||
* @param[in] zl lower value z-bounds
|
||||
* @param[in] zh higher value z-bounds
|
||||
*/
|
||||
void ApplyDirichletBC_Box(std::vector<double> const &u, std::vector<double> &f,
|
||||
double xl, double xh, double yl, double yh,
|
||||
double zl=-std::numeric_limits<double>::infinity(),
|
||||
double zh= std::numeric_limits<double>::infinity() );
|
||||
|
||||
private:
|
||||
Mesh const & _mesh; //!< discretization (contains element connectivity regarding geometric vertices)
|
||||
ELEM const _elem; //!< element type
|
||||
std::vector<int> _ia_dof; //!< element connectivity regarding DOFs in element
|
||||
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
template <class ELEM>
|
||||
FEM_Matrix_2<ELEM>::FEM_Matrix_2(Mesh const &mesh, ELEM const &elem)
|
||||
//FEM_Matrix_2<ELEM>::FEM_Matrix_2(Mesh const &mesh)
|
||||
: CRS_Matrix(), _mesh(mesh), _elem(elem), _ia_dof()
|
||||
//: CRS_Matrix(), _mesh(mesh), _elem(ELEM()), _ia_dof()
|
||||
{
|
||||
assert(CheckCompatibility());
|
||||
|
||||
_ia_dof = _elem.getGlobalDOFIndices(GetConnectivityGeom());
|
||||
Derive_Matrix_Pattern(); // requires _ia_dof
|
||||
return;
|
||||
}
|
||||
|
||||
template <class ELEM>
|
||||
FEM_Matrix_2<ELEM>::~FEM_Matrix_2()
|
||||
{}
|
||||
|
||||
template <class ELEM>
|
||||
bool FEM_Matrix_2<ELEM>::CheckCompatibility() const
|
||||
{
|
||||
int const ndim_fem = _elem.nDim_loc();
|
||||
int const nDOF_fem = _elem.nDOFs_loc();
|
||||
int const nvert_fem = _elem.nVertices_loc();
|
||||
|
||||
int const ndim = _mesh.Ndims();
|
||||
int const nDOF = _mesh.NdofsElement();
|
||||
int const nvert= _mesh.NverticesElement();
|
||||
std::cout << "## FEM_Matrix_2::CheckCompatibility() ##" << std::endl;
|
||||
|
||||
bool ok{true};
|
||||
if (ndim_fem!=ndim)
|
||||
{
|
||||
std::cout << "Space dimensions mismatch: " << ndim << " vs. " << ndim_fem << std::endl;
|
||||
ok = false;
|
||||
}
|
||||
if (ok && nvert_fem!=nvert)
|
||||
{
|
||||
std::cout << "#local vertices mismatch: " << nvert << " vs. " << nvert_fem << std::endl;
|
||||
ok = false;
|
||||
}
|
||||
if (ok && nDOF_fem < nDOF)
|
||||
{
|
||||
std::cout << "#local DOFs to small: " << nDOF << " vs. " << nDOF_fem << std::endl;
|
||||
ok = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class ELEM>
|
||||
void FEM_Matrix_2<ELEM>::Derive_Matrix_Pattern()
|
||||
{
|
||||
if (_ia_dof.empty())
|
||||
{
|
||||
_ia_dof = _elem.getGlobalDOFIndices(GetConnectivityGeom());
|
||||
}
|
||||
std::cout << "\n############ FEM_Matrix::Derive_Matrix_Pattern ";
|
||||
MyTimer tstart; //tstart.tic();
|
||||
|
||||
//int const nelem(Nelems());
|
||||
//int const ndof_e(NdofsElement());
|
||||
auto const &ia(GetConnectivity());
|
||||
|
||||
//std::cout << ia << std::endl;
|
||||
|
||||
// Determine the number of matrix rows
|
||||
//_nrows = *max_element(ia.cbegin(), ia.cbegin() + ndof_e * nelem);
|
||||
_nrows = *max_element(ia.cbegin(), ia.cend()); // GH??
|
||||
++_nrows; // node numberng: 0 ... nnode-1
|
||||
//assert(*min_element(ia.cbegin(), ia.cbegin() + ndof_e * nelem) == 0); // numbering starts with 0 ?
|
||||
assert(*min_element(ia.cbegin(), ia.cend()) == 0); // numbering starts with 0 ? // GH??
|
||||
|
||||
// CSR data allocation
|
||||
_id.resize(_nrows + 1); // Allocate memory for CSR row pointer
|
||||
//##########################################################################
|
||||
//auto const v2v = _mesh.Node2NodeGraph(); // GH TODO: Hier Node2NodeGraph mit _ia_dof
|
||||
auto const v2v = Node2NodeGraph(); // GH TODO: Hier Node2NodeGraph mit _ia_dof
|
||||
_nnz = 0; // number of connections
|
||||
_id[0] = 0; // start of matrix row zero
|
||||
for (size_t v = 0; v < v2v.size(); ++v ) {
|
||||
_id[v + 1] = _id[v] + v2v[v].size();
|
||||
_nnz += v2v[v].size();
|
||||
}
|
||||
assert(_nnz == _id[_nrows]); // GH: TODO
|
||||
_sk.resize(_nnz,-12345.0); // Allocate memory for CSR values vector
|
||||
|
||||
// CSR data allocation
|
||||
_ik.resize(_nnz); // Allocate memory for CSR column index vector
|
||||
// Copy column indices
|
||||
int kk = 0;
|
||||
for (const auto & v : v2v) {
|
||||
for (size_t vi = 0; vi < v.size(); ++vi) {
|
||||
_ik[kk] = v[vi];
|
||||
++kk;
|
||||
}
|
||||
}
|
||||
_ncols = *max_element(_ik.cbegin(), _ik.cend()); // maximal column number
|
||||
++_ncols; // node numbering: 0 ... nnode-1
|
||||
//cout << _nrows << " " << _ncols << endl;
|
||||
assert(_ncols == _nrows);
|
||||
|
||||
std::cout << "finished in " << tstart.toc() << " sec. ########\n";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
template <class ELEM>
|
||||
void FEM_Matrix_2<ELEM>::AddElem(int const ial[], std::vector<std::vector<double>> const &ske, std::vector<double> const &fe, std::vector<double> &f)
|
||||
{
|
||||
//assert(NdofsElement() == 3); // only for triangular, linear elements
|
||||
for (int i = 0; i < NdofsElement(); ++i) {
|
||||
const int ii = ial[i]; // row ii (global index)
|
||||
for (int j = 0; j < NdofsElement(); ++j) { // no symmetry assumed
|
||||
const int jj = ial[j]; // column jj (global index)
|
||||
const int ip = fetch(ii, jj); // find column entry jj in row ii
|
||||
#ifndef NDEBUG // compiler option -DNDEBUG switches off the check
|
||||
if (ip < 0) { // no entry found !!
|
||||
std::cout << "Error in AddElem: (" << ii << "," << jj << ") ["
|
||||
<< ial[0] << "," << ial[1] << "," << ial[2] << "]\n";
|
||||
assert(ip >= 0);
|
||||
}
|
||||
#endif
|
||||
#pragma omp atomic
|
||||
_sk[ip] += ske[i][j];
|
||||
}
|
||||
#pragma omp atomic
|
||||
f[ii] += fe[i];
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELEM>
|
||||
void FEM_Matrix_2<ELEM>::CalculateLaplace(
|
||||
std::vector<double> &f,
|
||||
std::function<double(double, double)> const &f_func) // 2D
|
||||
{
|
||||
assert(_elem.nDim_loc()==2); // 2D
|
||||
std::cout << "\n############ FEM_Matrix::CalculateLaplace f_func 2D";
|
||||
//std::cout << _nnz << " vs. " << _id[_nrows] << " " << _nrows<< std::endl;
|
||||
assert(_nnz == _id[_nrows]);
|
||||
|
||||
MyTimer tstart; //tstart.tic();
|
||||
fill(_sk.begin(),_sk.end(),0.0); // set matrix entries to zero
|
||||
fill( f.begin(), f.end(),0.0); // set rhs vector entries to zero
|
||||
|
||||
auto const nelem = Nelems();
|
||||
auto const nvert_e = NverticesElement(); // geom. vertices per element
|
||||
auto const ndof_e = NdofsElement(); // DOFs per element
|
||||
auto const &ia_geom = GetConnectivityGeom(); // geometric connectivity
|
||||
auto const &ia_crs = GetConnectivity(); // DOF connectivity in CRS matrix
|
||||
auto const &xc = _mesh.GetCoords(); // Coordinates
|
||||
|
||||
#pragma omp parallel
|
||||
{
|
||||
auto ske(_elem.createElemMatrix());
|
||||
auto fe (_elem.createElemVector());
|
||||
//#pragma omp parallel for private(ske,fe) // GH: doesn't work correctly with vector<vector<double>>
|
||||
#pragma omp for
|
||||
for (int i = 0; i < nelem; ++i) { // Loop over all elements
|
||||
_elem.CalcLaplace(ia_geom.data() + nvert_e * i, xc.data(), ske, fe,f_func);
|
||||
AddElem(ia_crs.data() + ndof_e * i, ske, fe, f);
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "finished in " << tstart.toc() << " sec. ########\n";
|
||||
//Debug();
|
||||
return;
|
||||
}
|
||||
|
||||
template <class ELEM>
|
||||
void FEM_Matrix_2<ELEM>::CalculateLaplace(
|
||||
std::vector<double> &f,
|
||||
std::function<double(double, double, double)> const &f_func) // 3D
|
||||
//const std::variant<std::function<double(double, double)>, std::function<double(double, double, double)>> &f_func)
|
||||
{
|
||||
assert(_elem.nDim_loc()==3); // 3D
|
||||
std::cout << "\n############ FEM_Matrix::CalculateLaplace f_func 3D";
|
||||
//std::cout << _nnz << " vs. " << _id[_nrows] << " " << _nrows<< std::endl;
|
||||
assert(_nnz == _id[_nrows]);
|
||||
|
||||
MyTimer tstart; //tstart.tic();
|
||||
fill(_sk.begin(),_sk.end(),0.0); // set matrix entries to zero
|
||||
fill( f.begin(), f.end(),0.0); // set rhs vector entries to zero
|
||||
|
||||
auto const nelem = Nelems();
|
||||
auto const nvert_e = NverticesElement(); // geom. vertices per element
|
||||
auto const ndof_e = NdofsElement(); // DOFs per element
|
||||
auto const &ia_geom = GetConnectivityGeom(); // geometric connectivity
|
||||
auto const &ia_crs = GetConnectivity(); // DOF connectivity in CRS matrix
|
||||
auto const &xc = _mesh.GetCoords(); // Coordinates
|
||||
|
||||
#pragma omp parallel
|
||||
{
|
||||
auto ske(_elem.createElemMatrix());
|
||||
auto fe (_elem.createElemVector());
|
||||
//#pragma omp parallel for private(ske,fe) // GH: doesn't work correctly with vector<vector<double>>
|
||||
#pragma omp for
|
||||
for (int i = 0; i < nelem; ++i) { // Loop over all elements
|
||||
_elem.CalcLaplace(ia_geom.data() + nvert_e * i, xc.data(), ske, fe,f_func);
|
||||
AddElem(ia_crs.data() + ndof_e * i, ske, fe, f);
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "finished in " << tstart.toc() << " sec. ########\n";
|
||||
//Debug();
|
||||
return;
|
||||
}
|
||||
|
||||
// only for linear elements and scalar functions
|
||||
template <class ELEM>
|
||||
void FEM_Matrix_2<ELEM>::CalculateLaplace(std::vector<double> &f)
|
||||
{
|
||||
std::cout << "\n############ FEM_Matrix::CalculateLaplace ";
|
||||
if constexpr(std::is_same<ELEM,P1_3d>::value)
|
||||
{
|
||||
CalculateLaplace(f, rhs_lap3 ); // 3D standard rhs
|
||||
}
|
||||
else if constexpr(std::is_same<ELEM,P1_2d>::value)
|
||||
{
|
||||
CalculateLaplace(f, rhs_lap2 ); // 2D standard rhs
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 2D-part: copy from getmatrix.cpp
|
||||
|
||||
template <class ELEM>
|
||||
void FEM_Matrix_2<ELEM>::ApplyDirichletBC(std::vector<double> const &u, std::vector<double> &f)
|
||||
{
|
||||
if (2==_elem.nDim_loc())
|
||||
{
|
||||
auto const idx = _mesh.Index_DirichletNodes(); // GH: not available in 3D
|
||||
int const nidx = idx.size();
|
||||
|
||||
for (int i = 0; i < nidx; ++i) {
|
||||
int const row = idx[i];
|
||||
for (int ij = _id[row]; ij < _id[row + 1]; ++ij) {
|
||||
int const col = _ik[ij];
|
||||
if (col == row) {
|
||||
_sk[ij] = 1.0;
|
||||
f[row] = u[row];
|
||||
}
|
||||
else {
|
||||
int const id1 = fetch(col, row); // Find entry (col,row)
|
||||
assert(id1 >= 0);
|
||||
f[col] -= _sk[id1] * u[row];
|
||||
_sk[id1] = 0.0;
|
||||
_sk[ij] = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "FEM_Matrix_2<ELEM>::ApplyDirichletBC not implemented in 3D" << std::endl;
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
template <class ELEM>
|
||||
void FEM_Matrix_2<ELEM>::ApplyDirichletBC_Box(std::vector<double> const &u, std::vector<double> &f,
|
||||
double xl, double xh, double yl, double yh, double zl, double zh )
|
||||
{
|
||||
std::vector<int> idx;
|
||||
if (3==_mesh.Ndims())
|
||||
{
|
||||
idx = _mesh.Index_DirichletNodes_Box(xl, xh, yl, yh, zl, zh);
|
||||
//std::cout << "#### 3D idx: " << idx << std::endl;
|
||||
}
|
||||
else if (2==_mesh.Ndims())
|
||||
{
|
||||
idx = _mesh.Index_DirichletNodes_Box(xl, xh, yl, yh);
|
||||
//std::cout << "#### 2D idx: " << idx << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
|
||||
double const PENALTY = 1e6;
|
||||
int const nidx = idx.size();
|
||||
|
||||
for (int row=0; row<nidx; ++row)
|
||||
{
|
||||
int const k = idx[row];
|
||||
int const id1 = fetch(k, k); // Find diagonal entry of row
|
||||
assert(id1 >= 0);
|
||||
_sk[id1] += PENALTY; // matrix weighted scaling feasible
|
||||
f[k] += PENALTY * u[k];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue