#pragma once #include "geom.h" #include "getmatrix.h" #include "userset.h" #include "utils.h" #include "vdop.h" #include //#include #include #include // class variant #include //!< linear test functions for P1_3D element static std::array,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,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,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 // 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 getGlobalDOFIndices(std::vector const &ia_geom) const = 0; /** * Allocates the local finite element matrix. * * @return matrix [_ndof][_ndof] */ std::vector> createElemMatrix() const { return std::vector>(_ndof,std::vector(_ndof,0.0)); } /** * Allocates the local finite element load vector. * * @return vector [_ndof] */ std::vector createElemVector() const { return std::vector(_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> &ske, std::vector &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> &ske, std::vector &fe, const std::function &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 getGlobalDOFIndices(std::vector 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> &ske, std::vector &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> &ske, std::vector &fe, const std::function &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 getGlobalDOFIndices(std::vector 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> &ske, std::vector &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> &ske, std::vector &fe, const std::function &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 getGlobalDOFIndices(std::vector 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 getGlobalDOFIndices(std::vector 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> &ske, std::vector &fe, const std::function &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 shrinkColumns(std::vector 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 mergeColumns(std::vector const &v1, int col1, std::vector 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 indexVectorBlowUp(std::vector const &vin, int ndof_v=1, int offset=0); //###################################################################### /** * FEM Matrix in CRS format (compressed row storage; also named CSR), * see an introduction. */ template 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 &GetConnectivity() const { return _ia_dof; } /** * Read DOF connectivity information (g1,g2,g3)_i. * @return connectivity vector [nelems*ndofs]. */ [[nodiscard]] const std::vector &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> 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 &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 &f, std::function 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 &f, std::function 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 &f); void AddElem( int const ial[], std::vector> const& ske, std::vector const &fe, std::vector &f); /** * Applies Dirichlet boundary conditions to stiffness matrix and to load vector @p f. * The penalty method * 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 const &u, std::vector &f); /** * Applies Dirichlet boundary conditions to stiffness matrix and to load vector @p f. * The penalty method * 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 const &u, std::vector &f, double xl, double xh, double yl, double yh, double zl=-std::numeric_limits::infinity(), double zh= std::numeric_limits::infinity() ); private: Mesh const & _mesh; //!< discretization (contains element connectivity regarding geometric vertices) ELEM const _elem; //!< element type std::vector _ia_dof; //!< element connectivity regarding DOFs in element }; //###################################################################### template FEM_Matrix_2::FEM_Matrix_2(Mesh const &mesh, ELEM const &elem) //FEM_Matrix_2::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 FEM_Matrix_2::~FEM_Matrix_2() {} template bool FEM_Matrix_2::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 void FEM_Matrix_2::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 void FEM_Matrix_2::AddElem(int const ial[], std::vector> const &ske, std::vector const &fe, std::vector &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 void FEM_Matrix_2::CalculateLaplace( std::vector &f, std::function 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> #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 void FEM_Matrix_2::CalculateLaplace( std::vector &f, std::function const &f_func) // 3D //const std::variant, std::function> &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> #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 void FEM_Matrix_2::CalculateLaplace(std::vector &f) { std::cout << "\n############ FEM_Matrix::CalculateLaplace "; if constexpr(std::is_same::value) { CalculateLaplace(f, rhs_lap3 ); // 3D standard rhs } else if constexpr(std::is_same::value) { CalculateLaplace(f, rhs_lap2 ); // 2D standard rhs } else { assert(false); } } // 2D-part: copy from getmatrix.cpp template void FEM_Matrix_2::ApplyDirichletBC(std::vector const &u, std::vector &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::ApplyDirichletBC not implemented in 3D" << std::endl; assert(false); } return; } template void FEM_Matrix_2::ApplyDirichletBC_Box(std::vector const &u, std::vector &f, double xl, double xh, double yl, double yh, double zl, double zh ) { std::vector 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= 0); _sk[id1] += PENALTY; // matrix weighted scaling feasible f[k] += PENALTY * u[k]; } return; }