Skip to content
Snippets Groups Projects
Forked from gmsh / gmsh
16399 commits behind the upstream repository.
FunctionSpace.cpp 17.57 KiB
// $Id: FunctionSpace.cpp,v 1.7 2008-06-27 08:10:07 koen Exp $
//
// Copyright (C) 1997-2008 C. Geuzaine, J.-F. Remacle
//
// This program 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 2 of the License, or
// (at your option) any later version.
//
// This program 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 this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA.
// 
// Please report all bugs and problems to <gmsh@geuz.org>.

#include "FunctionSpace.h"
#include "GmshDefines.h"

Double_Matrix generatePascalTriangle(int order)
{
  Double_Matrix monomials((order + 1) * (order + 2) / 2, 2);
  int index = 0;
  for (int i = 0; i <= order; i++) {
    for (int j = 0; j <= i; j++) {
      monomials(index, 0) = i - j;
      monomials(index, 1) = j;
      index++;
    }
  }
  return monomials;
}

// generate the exterior hull of the Pascal triangle for Serendipity element

Double_Matrix generatePascalSerendipityTriangle(int order)
{
  Double_Matrix monomials(3 * order, 2);

  monomials(0, 0) = 0;
  monomials(0, 1) = 0;
  
  int index = 1;
  for (int i = 1; i <= order; i++) {
    if (i == order) {
      for (int j = 0; j <= i; j++) {
        monomials(index, 0) = i - j;
        monomials(index, 1) = j;
        index++;
      }
    }
    else {
      monomials(index, 0) = i;
      monomials(index, 1) = 0;
      index++;
      monomials(index, 0) = 0;
      monomials(index, 1) = i;
      index++;
    }
  }
  return monomials;
}

// generate the monomials subspace of all monomials of order exactly == p
Double_Matrix generateMonomialSubspace(int dim, int p)
{
  Double_Matrix monomials;
  
  switch (dim) {
  case 1:
    monomials = Double_Matrix(1, 1);
    monomials(0, 0) = p;
    break;
  case 2:
    monomials = Double_Matrix(p + 1, 2);
    for (int k = 0; k <= p; k++) {
      monomials(k, 0) = p - k;
      monomials(k, 1) = k;
    }
    break;
  case 3:
    monomials = Double_Matrix((p + 1) * (p + 2) / 2, 3);
    int index = 0;
    for (int i = 0; i <= p; i++) {
      for (int k = 0; k <= p - i; k++) {
        monomials(index, 0) = p - i - k;
        monomials(index, 1) = k;
        monomials(index, 2) = i;
        index++;
      }
    }
    break;
  }
  return monomials;
}

// generate external hull of the Pascal tetrahedron

Double_Matrix generatePascalSerendipityTetrahedron(int order)
{
  int nbMonomials = 4 + 6 * std::max(0, order - 1) + 
    4 * std::max(0, (order - 2) * (order - 1) / 2);
  Double_Matrix monomials(nbMonomials, 3);

  // order 0
  
  monomials.set_all(0);
 
  monomials(0, 0) = 0;
  monomials(0, 1) = 0;
  monomials(0, 2) = 0;

  int index = 1;
  for (int p = 1; p < order; p++) {
    for (int i = 0; i < 3; i++) {
      int j = (i + 1) % 3;
      int k = (i + 2) % 3;
      for (int ii = 0; ii < p; ii++) {
        monomials(index, i) = p - ii;
        monomials(index, j) = ii;
        monomials(index, k) = 0;
        index++;
      }
    }
  }
  Double_Matrix monomialsMaxOrder = generateMonomialSubspace(3, order);
  int nbMaxOrder = monomialsMaxOrder.size1();
    
  Double_Matrix(monomials.touchSubmatrix(index, nbMaxOrder, 0, 3)).memcpy(monomialsMaxOrder);
  return monomials;
}

// generate Pascal tetrahedron
Double_Matrix generatePascalTetrahedron(int order)
{
  int nbMonomials = (order + 1) * (order + 2) * (order + 3) / 6;

  Double_Matrix monomials(nbMonomials, 3);

  int index = 0;
  for (int p = 0; p <= order; p++) {
    Double_Matrix monOrder = generateMonomialSubspace(3, p);
    int nb = monOrder.size1();
    Double_Matrix(monomials.touchSubmatrix(index, nb, 0, 3)).memcpy(monOrder);
    index += nb;
  }

  return monomials;
}  

int nbdoftriangle(int order) { return (order + 1) * (order + 2) / 2; }
int nbdoftriangleserendip(int order) { return 3 * order; }

void nodepositionface0(int order, double *u,  double *v,  double *w)
{
  int ndofT = nbdoftriangle(order);
  if (order == 0) { u[0] = 0.; v[0] = 0.; return; }
  
  u[0]= 0.;    v[0]= 0.;    w[0] = 0.;
  u[1]= order; v[1]= 0;     w[1] = 0.;
  u[2]= 0.;    v[2]= order; w[2] = 0.;

  // edges
  for (int k = 0; k < (order - 1); k++){
    u[3 + k] = k + 1; 
    v[3 + k] =0.; 
    w[3 + k] = 0.;

    u[3 + order - 1 + k] = order - 1 - k ; 
    v[3 + order - 1 + k] = k + 1;
    w[3 + order - 1 + k] = 0.;

    u[3 + 2 * (order - 1) + k] = 0.;
    v[3 + 2 * (order - 1) + k] = order - 1 - k;
    w[3 + 2 * (order - 1) + k] = 0.;
  }
  if (order > 2){
    int nbdoftemp = nbdoftriangle(order - 3);
    nodepositionface0(order - 3, &u[3 + 3 * (order - 1)], &v[3 + 3 * (order - 1)], 
                      &w[3 + 3* (order - 1)]);
    for (int k = 0; k < nbdoftemp; k++){
      u[3 + k + 3 * (order - 1)] = u[3 + k + 3 * (order - 1)] * (order - 3) + 1.;
      v[3 + k + 3 * (order - 1)] = v[3 + k + 3 * (order - 1)] * (order - 3) + 1.;
      w[3 + k + 3 * (order - 1)] = w[3 + k + 3 * (order - 1)] * (order - 3);
    }
  }
  for (int k = 0; k < ndofT; k++){
    u[k] = u[k] / order;
    v[k] = v[k] / order;
    w[k] = w[k] / order;
  }
}

void nodepositionface1(int order,  double *u,  double *v,  double *w)
{
   int ndofT = nbdoftriangle(order);
   if (order == 0) { u[0] = 0.; v[0] = 0.; return; }
   
   u[0]= 0.;    v[0]= 0.;  w[0] = 0.;
   u[1]= order; v[1]= 0;   w[1] = 0.;
   u[2]= 0.;    v[2]= 0.;  w[2] = order;
   // edges
   for (int k = 0; k < (order - 1); k++){
     u[3 + k] = k + 1; 
     v[3 + k] = 0.; 
     w[3 + k] = 0.;
     
     u[3 + order - 1 + k] = order - 1 - k;
     v[3 + order - 1 + k] = 0.;
     w[3 + order - 1+ k ] = k + 1; 
     
     u[3 + 2 * (order - 1) + k] = 0. ;
     v[3 + 2 * (order - 1) + k] = 0.;
     w[3 + 2 * (order - 1) + k] = order - 1 - k; 
   }
   if (order > 2){
     int nbdoftemp = nbdoftriangle(order - 3);
     nodepositionface1(order - 3, &u[3 + 3 * (order - 1)], &v[3 + 3 * (order -1 )],
                       &w[3 + 3 * (order - 1)]);
     for (int k = 0; k < nbdoftemp; k++){
       u[3 + k + 3 * (order - 1)] = u[3 + k + 3 * (order - 1)] * (order - 3) + 1.;
       v[3 + k + 3 * (order - 1)] = v[3 + k + 3 * (order - 1)] * (order - 3);
       w[3 + k + 3 * (order - 1)] = w[3 + k + 3 * (order - 1)] * (order - 3) + 1.;
     }
   }
   for (int k = 0; k < ndofT; k++){
     u[k] = u[k] / order;
     v[k] = v[k] / order;
     w[k] = w[k] / order;
   }
}

void nodepositionface2(int order,  double *u,  double *v,  double *w)
{
   int ndofT = nbdoftriangle(order);
   if (order == 0) { u[0] = 0.; v[0] = 0.; return; }
   
   u[0]= order; v[0]= 0.;    w[0] = 0.;
   u[1]= 0.;    v[1]= order; w[1] = 0.;
   u[2]= 0.;    v[2]= 0.;    w[2] = order;
   // edges
   for (int k = 0; k < (order - 1); k++){
     u[3 + k] = order - 1 - k;
     v[3 + k] = k + 1;
     w[3 + k] = 0.;

     u[3 + order - 1 + k] = 0.;
     v[3 + order - 1 + k] = order - 1 - k;
     w[3 + order - 1 + k] = k + 1; 
     
     u[3 + 2 * (order - 1) + k] = k + 1;
     v[3 + 2 * (order - 1) + k] = 0.;
     w[3 + 2 * (order - 1) + k] = order - 1 - k; 
   }
   if (order > 2){
     int nbdoftemp = nbdoftriangle(order - 3);
     nodepositionface2(order - 3, &u[3 + 3 * (order - 1)], &v[3 + 3 * (order - 1)],
                       &w[3 + 3 * (order - 1)]);
     for (int k = 0; k < nbdoftemp; k++){
       u[3 + k + 3 * (order - 1)] = u[3 + k + 3 * (order - 1)] * (order - 3) + 1.;
       v[3 + k + 3 * (order - 1)] = v[3 + k + 3 * (order - 1)] * (order - 3) + 1.;
       w[3 + k + 3 * (order - 1)] = w[3 + k + 3 * (order - 1)] * (order - 3) + 1.;
     }
   }
   for (int k = 0; k < ndofT; k++){
     u[k] = u[k] / order;
     v[k] = v[k] / order;
     w[k] = w[k] / order;
   }
}

void nodepositionface3(int order, double *u, double *v,  double *w)
{
   int ndofT = nbdoftriangle(order);
   if (order == 0) { u[0] = 0.; v[0] = 0.; return; }
   
   u[0]= 0.; v[0]= 0.;    w[0] = 0.;
   u[1]= 0.; v[1]= order; w[1] = 0.;
   u[2]= 0.; v[2]= 0.;    w[2] = order;
   // edges
   for (int k = 0; k < (order - 1); k++){
     u[3 + k] = 0.;
     v[3 + k]= k + 1;
     w[3 + k] = 0.;

     u[3 + order - 1 + k] = 0.;
     v[3 + order - 1 + k] = order - 1 - k;
     w[3 + order - 1 + k] = k + 1; 
     
     u[3 + 2 * (order - 1) + k] = 0.;
     v[3 + 2 * (order - 1) + k] = 0.;
     w[3 + 2 * (order - 1) + k] = order - 1 - k;
   }
   if (order > 2){
     int nbdoftemp = nbdoftriangle(order - 3);
     nodepositionface3(order - 3, &u[3 + 3 * (order - 1)], &v[3 + 3 * (order - 1)],
                       &w[3 + 3 * (order - 1)]);
     for (int k = 0; k < nbdoftemp; k++){
       u[3 + k + 3 * (order - 1)] = u[3 + k + 3 * (order - 1)] * (order - 3);
       v[3 + k + 3 * (order - 1)] = v[3 + k + 3 * (order - 1)] * (order - 3) + 1.;
       w[3 + k + 3 * (order - 1)] = w[3 + k + 3 * (order - 1)] * (order - 3) + 1.;
     }
   }
   for (int k = 0; k < ndofT; k++){
     u[k] = u[k] / order;
     v[k] = v[k] / order;
     w[k] = w[k] / order;
   }
}

Double_Matrix gmshGeneratePointsTetrahedron(int order, bool serendip) 
{
  int nbPoints = 
    (serendip ?
     4 +  6 * std::max(0, order - 1) + 4 * std::max(0, (order - 2) * (order - 1) / 2) :
     (order + 1) * (order + 2) * (order + 3) / 6);
  
  Double_Matrix point(nbPoints, 3);

  double overOrder = (order == 0 ? 1. : 1. / order);

  point(0, 0) = 0.;
  point(0, 1) = 0.;
  point(0, 2) = 0.;

  if (order > 0) {
    point(1, 0) = order;
    point(1, 1) = 0;
    point(1, 2) = 0;
    
    point(2, 0) = 0.;
    point(2, 1) = order;
    point(2, 2) = 0.;
    
    point(3, 0) = 0.;
    point(3, 1) = 0.;
    point(3, 2) = order;

    // edges e5 and e6 switched in original version
    
    if (order > 1) {
      for (int k = 0; k < (order - 1); k++) {
        point(4 + k, 0) = k + 1;
        point(4 +      order - 1  + k, 0) = order - 1 - k;
        point(4 + 2 * (order - 1) + k, 0) = 0.; 
        point(4 + 3 * (order - 1) + k, 0) = 0.;
        // point(4 + 4 * (order - 1) + k, 0) = order - 1 - k;
        // point(4 + 5 * (order - 1) + k, 0) = 0.;
        point(4 + 4 * (order - 1) + k, 0) = 0.;
        point(4 + 5 * (order - 1) + k, 0) = order - 1 - k;
        
        point(4 + k, 1) = 0.;
        point(4 +      order - 1  + k, 1) = k + 1;
        point(4 + 2 * (order - 1) + k, 1) = order - 1 - k; 
        point(4 + 3 * (order - 1) + k, 1) = 0.;
        //         point(4 + 4 * (order - 1) + k, 1) = 0.;
        //         point(4 + 5 * (order - 1) + k, 1) = order - 1 - k;
        point(4 + 4 * (order - 1) + k, 1) = order - 1 - k;
        point(4 + 5 * (order - 1) + k, 1) = 0.;
        
        point(4 + k, 2) = 0.;
        point(4 +      order - 1  + k, 2) = 0.;
        point(4 + 2 * (order - 1) + k, 2) = 0.; 
        point(4 + 3 * (order - 1) + k, 2) = k + 1;
        point(4 + 4 * (order - 1) + k, 2) = k + 1;
        point(4 + 5 * (order - 1) + k, 2) = k + 1; 
      }
      
      if (order > 2) {
        int ns = 4 + 6 * (order - 1);
        int nbdofface = nbdoftriangle(order - 3);
        
        double *u = new double[nbdofface];
        double *v = new double[nbdofface];
        double *w = new double[nbdofface];
        
        nodepositionface0(order - 3, u, v, w);

        for (int i = 0; i < nbdofface; i++){
          point(ns + i, 0) = u[i] * (order - 3) + 1.;
          point(ns + i, 1) = v[i] * (order - 3) + 1.;
          point(ns + i, 2) = w[i] * (order - 3);
        }
        
        ns = ns + nbdofface;

        nodepositionface1(order - 3, u, v, w);
        
        for (int i=0; i < nbdofface; i++){
          point(ns + i, 0) = u[i] * (order - 3) + 1.;
          point(ns + i, 1) = v[i] * (order - 3) ;
          point(ns + i, 2) = w[i] * (order - 3) + 1.;
        }
        
        ns = ns + nbdofface;

        nodepositionface2(order - 3, u, v, w);
        
        for (int i = 0; i < nbdofface; i++){
          point(ns + i, 0) = u[i] * (order - 3) + 1.;
          point(ns + i, 1) = v[i] * (order - 3) + 1.;
          point(ns + i, 2) = w[i] * (order - 3) + 1.;
        }

        ns = ns + nbdofface;

        nodepositionface3(order - 3, u, v, w);

        for (int i = 0; i < nbdofface; i++){
          point(ns + i, 0) = u[i] * (order - 3);
          point(ns + i, 1) = v[i] * (order - 3) + 1.;
          point(ns + i, 2) = w[i] * (order - 3) + 1.;
        }

        ns = ns + nbdofface;

        delete [] u;
        delete [] v;
        delete [] w;
        
        if (!serendip && order > 3) {
  
          Double_Matrix interior = gmshGeneratePointsTetrahedron(order - 4, false);
          for (int k = 0; k < interior.size1(); k++) {
            point(ns + k, 0) = 1. + interior(k, 0) * (order - 4);
            point(ns + k, 1) = 1. + interior(k, 1) * (order - 4);
            point(ns + k, 2) = 1. + interior(k, 2) * (order - 4);
          }
        }
      }
    }
  }
  
  point.scale(overOrder);  
  return point;
}

Double_Matrix gmshGeneratePointsTriangle(int order, bool serendip) 
{
  int nbPoints = serendip ? 3 * order : (order + 1) * (order + 2) / 2;
  Double_Matrix point(nbPoints, 2);
  
  point(0, 0) = 0;
  point(0, 1) = 0;
  
  double dd = 1. / order;

  if (order > 0) {
    point(1, 0) = 1;
    point(1, 1) = 0;
    point(2, 0) = 0;
    point(2, 1) = 1;
    
    int index = 3;
    
    if (order > 1) {
      
      double ksi = 0;
      double eta = 0;
      
      for (int i = 0; i < order - 1; i++, index++) {
        ksi += dd;
        point(index, 0) = ksi;
        point(index, 1) = eta;
      }
      
      ksi = 1.;
      
      for (int i = 0; i < order - 1; i++, index++) {
        ksi -= dd;
        eta += dd;
        point(index, 0) = ksi;
        point(index, 1) = eta;
      } 
        
      eta = 1.;
      ksi = 0.;

      for (int i = 0; i < order - 1; i++, index++) {
        eta -= dd;
        point(index, 0) = ksi;
        point(index, 1) = eta;
      } 

      if (order > 2 && !serendip) {
        Double_Matrix inner = gmshGeneratePointsTriangle(order - 3, serendip);
        inner.scale(1. - 3. * dd);
        inner.add(dd);
        Double_Matrix(point.touchSubmatrix(index, nbPoints - index, 0, 2)).memcpy(inner);
      }
    }
  }
  return point;  
}

Double_Matrix generateLagrangeMonomialCoefficients(const Double_Matrix& monomial,
                                                   const Double_Matrix& point) 
{
  if (monomial.size1() != point.size1()) throw;
  if (monomial.size2() != point.size2()) throw;
  
  int ndofs = monomial.size1();
  int dim   = monomial.size2();
  
  Double_Matrix Vandermonde(ndofs, ndofs);
  
  for (int i = 0; i < ndofs; i++) {
    for (int j = 0; j < ndofs; j++) {
      double dd = 1.;
      for (int k = 0; k < dim; k++) dd *= pow(point(i, k), monomial(j, k));
      Vandermonde(i, j) = dd;
    }
  }
  
  // check for independence
  
  double det = Vandermonde.determinant();

  if (det == 0.0) throw;

  Double_Matrix coefficient(ndofs, ndofs);
  
  for (int i = 0; i < ndofs; i++) {
    for (int j = 0; j < ndofs; j++) {
      int f = (i + j) % 2 == 0 ? 1 : -1;
      Double_Matrix cofactor = Vandermonde.cofactor(i, j);
      coefficient(i, j) = f * cofactor.determinant() / det;
    }
  }

  Vandermonde.set_all(0.);
  
  for (int i = 0; i < ndofs; i++) {
    for (int j = 0; j < ndofs; j++) {
      double dd = 1.;
      for (int k = 0; k < dim; k++) dd *= pow(point(i, k), monomial(j, k));
      for (int k = 0; k < ndofs; k++) {
        Vandermonde(i, k) += coefficient(k, j) * dd;
      }
    }
  }
  return coefficient;
}

std::map<int, gmshFunctionSpace> gmshFunctionSpaces::fs;

const gmshFunctionSpace &gmshFunctionSpaces::find(int tag) 
{
  std::map<int,gmshFunctionSpace>::const_iterator it = fs.find(tag);
  if (it != fs.end()) return it->second;
  
  gmshFunctionSpace F;
  
  switch (tag){
  case MSH_TRI_3 :
    F.monomials = generatePascalTriangle(1);
    F.points =    gmshGeneratePointsTriangle(1, false);
    break;
  case MSH_TRI_6 :
    F.monomials = generatePascalTriangle(2);
    F.points =    gmshGeneratePointsTriangle(2, false);
    break;
  case MSH_TRI_9 :
    F.monomials = generatePascalSerendipityTriangle(3);
    F.points =    gmshGeneratePointsTriangle(3, true);
    break;
  case MSH_TRI_10 :
    F.monomials = generatePascalTriangle(3);
    F.points =    gmshGeneratePointsTriangle(3, false);
    break;
  case MSH_TRI_12 :
    F.monomials = generatePascalSerendipityTriangle(4);
    F.points =    gmshGeneratePointsTriangle(4, true);
    break;
  case MSH_TRI_15 :
    F.monomials = generatePascalTriangle(4);
    F.points =    gmshGeneratePointsTriangle(4, false);
    break;
  case MSH_TRI_15I :
    F.monomials = generatePascalSerendipityTriangle(5);
    F.points =    gmshGeneratePointsTriangle(5, true);
    break;
  case MSH_TRI_21 :
    F.monomials = generatePascalTriangle(5);
    F.points =    gmshGeneratePointsTriangle(5, false);
    break;
  case MSH_TET_4 :
    F.monomials = generatePascalTetrahedron(1);
    F.points =    gmshGeneratePointsTetrahedron(1, false);
    break;
  case MSH_TET_10 :
    F.monomials = generatePascalTetrahedron(2);
    F.points =    gmshGeneratePointsTetrahedron(2, false);
    break;
  case MSH_TET_20 :
    F.monomials = generatePascalTetrahedron(3);
    F.points =    gmshGeneratePointsTetrahedron(3, false);
    break;
  case MSH_TET_35 :
    F.monomials = generatePascalTetrahedron(4);
    F.points =    gmshGeneratePointsTetrahedron(4, false);
    break;
  case MSH_TET_56 :
    F.monomials = generatePascalTetrahedron(5);
    F.points =    gmshGeneratePointsTetrahedron(5, false);
    break;
  default :
    throw;
  }  
  F.coefficients = generateLagrangeMonomialCoefficients(F.monomials, F.points);
  fs.insert(std::make_pair(tag, F));
  return fs[tag];
}