// GetDP - Copyright (C) 1997-2022 P. Dular and C. Geuzaine, University of Liege
//
// See the LICENSE.txt file for license information. Please report all
// issues on https://gitlab.onelab.info/getdp/getdp/issues.
//
// Contributor(s):
//   Patrick Lefevre
//

#include <math.h>
#include "GetDPConfig.h"
#include "ProData.h"
#include "ProDefine.h"
#include "Get_Geometry.h"
#include "GeoData.h"
#include "BF.h"
#include "Gauss.h"
#include "Message.h"

#define THESIGN(a) ((a) >= 0 ? 1 : -1)
#define SQU(a) ((a) * (a))
#define HYPOT(a, b) (sqrt((a) * (a) + (b) * (b)))

/* ------------------------------------------------------------------------ */
/*  G e t _ N o d e s C o o r d i n a t e s O f E l e m e n t               */
/* ------------------------------------------------------------------------ */

void Get_NodesCoordinatesOfElement(struct Element *Element)
{
  if(Element->NumLastElementForNodesCoordinates != Element->Num) {
    Element->NumLastElementForNodesCoordinates = Element->Num;
    Geo_GetNodesCoordinates(Element->GeoElement->NbrNodes,
                            Element->GeoElement->NumNodes, Element->x,
                            Element->y, Element->z);
  }
}

/* ------------------------------------------------------------------------ */
/*  G e t _ B F G e o E l e m e n t */
/* ------------------------------------------------------------------------ */

void Get_BFGeoElement(struct Element *Element, double u, double v, double w)
{
  int i;

  for(i = 0; i < Element->GeoElement->NbrNodes; i++) {
    BF_Node(Element, i + 1, u, v, w, &(Element->n[i]));
    BF_GradNode(Element, i + 1, u, v, w, Element->dndu[i]);
  }
}

/* ------------------------------------------------------------------------ */
/*  G e t _ J a c o b i a n F u n c t i o n                                 */
/* ------------------------------------------------------------------------ */

void *Get_JacobianFunction(int Type_Jacobian, int Type_Element,
                           int *Type_Dimension)
{
  switch(Type_Jacobian) {
  case JACOBIAN_VOL:

    switch(Type_Element) {
    case POINT_ELEMENT:
      *Type_Dimension = DIM_0D;
      return ((void *)JacobianVol0D);

    case LINE:
    case LINE_2:
    case LINE_3:
    case LINE_4: *Type_Dimension = DIM_1D; return ((void *)JacobianVol1D);

    case TRIANGLE:
    case TRIANGLE_2:
    case TRIANGLE_3:
    case TRIANGLE_4:
    case QUADRANGLE:
    case QUADRANGLE_2:
    case QUADRANGLE_2_8N:
    case QUADRANGLE_3:
    case QUADRANGLE_4: *Type_Dimension = DIM_2D; return ((void *)JacobianVol2D);

    case TETRAHEDRON:
    case TETRAHEDRON_2:
    case TETRAHEDRON_3:
    case TETRAHEDRON_4:
    case HEXAHEDRON:
    case HEXAHEDRON_2:
    case HEXAHEDRON_2_20N:
    case HEXAHEDRON_3:
    case HEXAHEDRON_4:
    case PRISM:
    case PRISM_2:
    case PRISM_2_15N:
    case PRISM_3:
    case PRISM_4:
    case PYRAMID:
    case PYRAMID_2:
    case PYRAMID_2_13N:
    case PYRAMID_3: // case PYRAMID_4     :
      *Type_Dimension = DIM_3D;
      return ((void *)JacobianVol3D);

    default:
      Message::Error("Unknown Jacobian Vol for Element Type (%s)",
                     Get_StringForDefine(Element_Type, Type_Element));
    }

  case JACOBIAN_VOL_SPH_SHELL:

    switch(Type_Element) {
    case TRIANGLE:
    case TRIANGLE_2:
    case TRIANGLE_3:
    case TRIANGLE_4:
    case QUADRANGLE:
    case QUADRANGLE_2:
    case QUADRANGLE_2_8N:
    case QUADRANGLE_3:
    case QUADRANGLE_4:
      *Type_Dimension = DIM_2D;
      return ((void *)JacobianVolSphShell2D);

    case TETRAHEDRON:
    case TETRAHEDRON_2:
    case TETRAHEDRON_3:
    case TETRAHEDRON_4:
    case HEXAHEDRON:
    case HEXAHEDRON_2:
    case HEXAHEDRON_2_20N:
    case HEXAHEDRON_3:
    case HEXAHEDRON_4:
    case PRISM:
    case PRISM_2:
    case PRISM_2_15N:
    case PRISM_3:
    case PRISM_4:
    case PYRAMID:
    case PYRAMID_2:
    case PYRAMID_2_13N:
    case PYRAMID_3: // case PYRAMID_4     :
      *Type_Dimension = DIM_3D;
      return ((void *)JacobianVolSphShell3D);

    default:
      Message::Error("Unknown Jacobian VolSphShell for Element Type (%s)",
                     Get_StringForDefine(Element_Type, Type_Element));
    }

  case JACOBIAN_VOL_CYL_SHELL:

    switch(Type_Element) {
    case TRIANGLE:
    case TRIANGLE_2:
    case TRIANGLE_3:
    case TRIANGLE_4:
    case QUADRANGLE:
    case QUADRANGLE_2:
    case QUADRANGLE_2_8N:
    case QUADRANGLE_3:
    case QUADRANGLE_4:
      *Type_Dimension = DIM_2D;
      return ((void *)JacobianVolSphShell2D);

    case TETRAHEDRON:
    case TETRAHEDRON_2:
    case TETRAHEDRON_3:
    case TETRAHEDRON_4:
    case HEXAHEDRON:
    case HEXAHEDRON_2:
    case HEXAHEDRON_2_20N:
    case HEXAHEDRON_3:
    case HEXAHEDRON_4:
    case PRISM:
    case PRISM_2:
    case PRISM_2_15N:
    case PRISM_3:
    case PRISM_4:
    case PYRAMID:
    case PYRAMID_2:
    case PYRAMID_2_13N:
    case PYRAMID_3: // case PYRAMID_4     :
      *Type_Dimension = DIM_3D;
      return ((void *)JacobianVolCylShell3D);

    default:
      Message::Error("Unknown Jacobian VolCylShell for Element Type (%s)",
                     Get_StringForDefine(Element_Type, Type_Element));
    }

  case JACOBIAN_VOL_RECT_SHELL:

    switch(Type_Element) {
    case TRIANGLE:
    case TRIANGLE_2:
    case TRIANGLE_3:
    case TRIANGLE_4:
    case QUADRANGLE:
    case QUADRANGLE_2:
    case QUADRANGLE_2_8N:
    case QUADRANGLE_3:
    case QUADRANGLE_4:
      *Type_Dimension = DIM_2D;
      return ((void *)JacobianVolRectShell2D);

    case TETRAHEDRON:
    case TETRAHEDRON_2:
    case TETRAHEDRON_3:
    case TETRAHEDRON_4:
    case HEXAHEDRON:
    case HEXAHEDRON_2:
    case HEXAHEDRON_2_20N:
    case HEXAHEDRON_3:
    case HEXAHEDRON_4:
    case PRISM:
    case PRISM_2:
    case PRISM_2_15N:
    case PRISM_3:
    case PRISM_4:
    case PYRAMID:
    case PYRAMID_2:
    case PYRAMID_2_13N:
    case PYRAMID_3: // case PYRAMID_4     :
      *Type_Dimension = DIM_3D;
      return ((void *)JacobianVolRectShell3D);

    default:
      Message::Error("Unknown Jacobian VolRectShell for Element Type (%s)",
                     Get_StringForDefine(Element_Type, Type_Element));
    }

  case JACOBIAN_VOL_UNI_DIR_SHELL:

    switch(Type_Element) {
    case TETRAHEDRON:
    case TETRAHEDRON_2:
    case TETRAHEDRON_3:
    case TETRAHEDRON_4:
    case HEXAHEDRON:
    case HEXAHEDRON_2:
    case HEXAHEDRON_2_20N:
    case HEXAHEDRON_3:
    case HEXAHEDRON_4:
    case PRISM:
    case PRISM_2:
    case PRISM_2_15N:
    case PRISM_3:
    case PRISM_4:
    case PYRAMID:
    case PYRAMID_2:
    case PYRAMID_2_13N:
    case PYRAMID_3: // case PYRAMID_4     :
      *Type_Dimension = DIM_3D;
      return ((void *)JacobianVolUniDirShell3D);

    default:
      Message::Error("Unknown Jacobian VolUniDirShell for Element Type (%s)",
                     Get_StringForDefine(Element_Type, Type_Element));
    }

  case JACOBIAN_VOL_PLPD_X:

    switch(Type_Element) {
    case TRIANGLE:
    case TRIANGLE_2:
    case TRIANGLE_3:
    case TRIANGLE_4:
    case QUADRANGLE:
    case QUADRANGLE_2:
    case QUADRANGLE_2_8N:
    case QUADRANGLE_3:
    case QUADRANGLE_4:
      *Type_Dimension = DIM_2D;
      return ((void *)JacobianVolPlpdX2D);

    default:
      Message::Error("Unknown Jacobian VolPlpdX for Element Type (%s)",
                     Get_StringForDefine(Element_Type, Type_Element));
    }

  case JACOBIAN_VOL_AXI:

    switch(Type_Element) {
    case LINE:
    case LINE_2:
    case LINE_3:
    case LINE_4: *Type_Dimension = DIM_1D; return ((void *)JacobianVolAxi1D);

    case TRIANGLE:
    case TRIANGLE_2:
    case TRIANGLE_3:
    case TRIANGLE_4:
    case QUADRANGLE:
    case QUADRANGLE_2:
    case QUADRANGLE_2_8N:
    case QUADRANGLE_3:
    case QUADRANGLE_4:
      *Type_Dimension = DIM_2D;
      return ((void *)JacobianVolAxi2D);

    default:
      Message::Error("Unknown Jacobian VolAxi for Element Type (%s)",
                     Get_StringForDefine(Element_Type, Type_Element));
    }

  case JACOBIAN_VOL_AXI_SPH_SHELL:

    switch(Type_Element) {
    case TRIANGLE:
    case TRIANGLE_2:
    case TRIANGLE_3:
    case TRIANGLE_4:
    case QUADRANGLE:
    case QUADRANGLE_2:
    case QUADRANGLE_2_8N:
    case QUADRANGLE_3:
    case QUADRANGLE_4:
      *Type_Dimension = DIM_2D;
      return ((void *)JacobianVolAxiSphShell2D);

    default:
      Message::Error("Unknown Jacobian VolAxiSphShell for Element Type (%s)",
                     Get_StringForDefine(Element_Type, Type_Element));
    }

  case JACOBIAN_VOL_AXI_RECT_SHELL:

    switch(Type_Element) {
    case TRIANGLE:
    case TRIANGLE_2:
    case TRIANGLE_3:
    case TRIANGLE_4:
    case QUADRANGLE:
    case QUADRANGLE_2:
    case QUADRANGLE_2_8N:
    case QUADRANGLE_3:
    case QUADRANGLE_4:
      *Type_Dimension = DIM_2D;
      return ((void *)JacobianVolAxiRectShell2D);

    default:
      Message::Error("Unknown Jacobian VolAxiRectShell for Element Type (%s)",
                     Get_StringForDefine(Element_Type, Type_Element));
    }

  case JACOBIAN_VOL_AXI_PLPD_X:

    switch(Type_Element) {
    case TRIANGLE:
    case TRIANGLE_2:
    case TRIANGLE_3:
    case TRIANGLE_4:
    case QUADRANGLE:
    case QUADRANGLE_2:
    case QUADRANGLE_2_8N:
    case QUADRANGLE_3:
    case QUADRANGLE_4:
      *Type_Dimension = DIM_2D;
      return ((void *)JacobianVolAxiPlpdX2D);

    default:
      Message::Error("Unknown Jacobian VolAxiPlpdX for Element Type (%s)",
                     Get_StringForDefine(Element_Type, Type_Element));
    }

  case JACOBIAN_VOL_AXI_SQU:

    switch(Type_Element) {
    case LINE:
    case LINE_2:
    case LINE_3:
    case LINE_4: *Type_Dimension = DIM_1D; return ((void *)JacobianVolAxiSqu1D);

    case TRIANGLE:
    case TRIANGLE_2:
    case TRIANGLE_3:
    case TRIANGLE_4:
    case QUADRANGLE:
    case QUADRANGLE_2:
    case QUADRANGLE_2_8N:
    case QUADRANGLE_3:
    case QUADRANGLE_4:
      *Type_Dimension = DIM_2D;
      return ((void *)JacobianVolAxiSqu2D);

    default:
      Message::Error("Unknown Jacobian VolAxiSqu for Element Type (%s)",
                     Get_StringForDefine(Element_Type, Type_Element));
    }

  case JACOBIAN_VOL_AXI_SQU_SPH_SHELL:

    switch(Type_Element) {
    case TRIANGLE:
    case TRIANGLE_2:
    case TRIANGLE_3:
    case TRIANGLE_4:
    case QUADRANGLE:
    case QUADRANGLE_2:
    case QUADRANGLE_2_8N:
    case QUADRANGLE_3:
    case QUADRANGLE_4:
      *Type_Dimension = DIM_2D;
      return ((void *)JacobianVolAxiSquSphShell2D);

    default:
      Message::Error("Unknown Jacobian VolAxiSquSphShell for Element Type (%s)",
                     Get_StringForDefine(Element_Type, Type_Element));
    }

  case JACOBIAN_VOL_AXI_SQU_RECT_SHELL:

    switch(Type_Element) {
    case TRIANGLE:
    case TRIANGLE_2:
    case TRIANGLE_3:
    case TRIANGLE_4:
    case QUADRANGLE:
    case QUADRANGLE_2:
    case QUADRANGLE_2_8N:
    case QUADRANGLE_3:
    case QUADRANGLE_4:
      *Type_Dimension = DIM_2D;
      return ((void *)JacobianVolAxiSquRectShell2D);

    default:
      Message::Error(
        "Unknown Jacobian VolAxiSquRectShell for Element Type (%s)",
        Get_StringForDefine(Element_Type, Type_Element));
    }

  case JACOBIAN_SUR:

    switch(Type_Element) {
    case POINT_ELEMENT:
      *Type_Dimension = DIM_1D;
      return ((void *)JacobianVol0D);

    case LINE:
    case LINE_2:
    case LINE_3:
    case LINE_4: *Type_Dimension = DIM_2D; return ((void *)JacobianSur2D);

    case TRIANGLE:
    case TRIANGLE_2:
    case TRIANGLE_3:
    case TRIANGLE_4:
    case QUADRANGLE:
    case QUADRANGLE_2:
    case QUADRANGLE_2_8N:
    case QUADRANGLE_3:
    case QUADRANGLE_4: *Type_Dimension = DIM_3D; return ((void *)JacobianSur3D);

    default:
      Message::Error("Unknown Jacobian Sur for element Type (%s)",
                     Get_StringForDefine(Element_Type, Type_Element));
    }

  case JACOBIAN_SUR_SPH_SHELL:

    switch(Type_Element) {
    case LINE:
    case LINE_2:
    case LINE_3:
    case LINE_4:
      *Type_Dimension = DIM_2D;
      return ((void *)JacobianSurSphShell2D);

    default:
      Message::Error("Unknown Jacobian SurSphShell for element Type (%s)",
                     Get_StringForDefine(Element_Type, Type_Element));
    }

  case JACOBIAN_SUR_AXI:

    switch(Type_Element) {
    case LINE:
    case LINE_2:
    case LINE_3:
    case LINE_4:
      *Type_Dimension = DIM_2D;
      return ((void *)JacobianSurAxi2D);

      // for integrals on surfaces in the study plane in axisymm. problems
      // e.g. the computation of the area of a region
    case TRIANGLE:
    case TRIANGLE_2:
    case TRIANGLE_3:
    case TRIANGLE_4:
    case QUADRANGLE:
    case QUADRANGLE_2:
    case QUADRANGLE_2_8N:
    case QUADRANGLE_3:
    case QUADRANGLE_4: *Type_Dimension = DIM_2D; return ((void *)JacobianVol2D);

    default:
      Message::Error("Unknown Jacobian SurAxi for Element Type (%s)",
                     Get_StringForDefine(Element_Type, Type_Element));
    }

  case JACOBIAN_LIN:

    switch(Type_Element) {
    case POINT_ELEMENT:
      *Type_Dimension = DIM_2D;
      return ((void *)JacobianVol0D);

    case LINE:
    case LINE_2:
    case LINE_3:
    case LINE_4: *Type_Dimension = DIM_3D; return ((void *)JacobianLin3D);

    default:
      Message::Error("Unknown Jacobian Lin for Element Type (%s)",
                     Get_StringForDefine(Element_Type, Type_Element));
    }

  default: Message::Error("Unknown Jacobian"); return (NULL);
  }
}

/* ------------------------------------------------------------------------ */
/*  G e t _ J a c o b i a n F u n c t i o n A u t o                         */
/* ------------------------------------------------------------------------ */

void *Get_JacobianFunctionAuto(int Type_Element, int Dimension)
{
  switch(Type_Element) {
  case POINT_ELEMENT: return ((void *)JacobianVol0D);
  case LINE:
  case LINE_2:
  case LINE_3:
  case LINE_4:
    switch(Dimension) {
    case DIM_3D: return ((void *)JacobianLin3D);
    case DIM_2D: return ((void *)JacobianSur2D);
    default: return ((void *)JacobianVol1D);
    }
  case TRIANGLE:
  case TRIANGLE_2:
  case TRIANGLE_3:
  case TRIANGLE_4:
  case QUADRANGLE:
  case QUADRANGLE_2:
  case QUADRANGLE_2_8N:
  case QUADRANGLE_3:
  case QUADRANGLE_4:
    switch(Dimension) {
    case DIM_3D: return ((void *)JacobianSur3D);
    default: return ((void *)JacobianVol2D);
    }
  case TETRAHEDRON:
  case TETRAHEDRON_2:
  case TETRAHEDRON_3:
  case TETRAHEDRON_4:
  case HEXAHEDRON:
  case HEXAHEDRON_2:
  case HEXAHEDRON_2_20N:
  case HEXAHEDRON_3:
  case HEXAHEDRON_4:
  case PRISM:
  case PRISM_2:
  case PRISM_2_15N:
  case PRISM_3:
  case PRISM_4:
  case PYRAMID:
  case PYRAMID_2:
  case PYRAMID_2_13N:
  case PYRAMID_3: // case PYRAMID_4     :
  default: return ((void *)JacobianVol3D);
  }
}

/* ------------------------------------------------------------------------ */
/*  G e t _ I n t e g r a t i o n F u n c t i o n A u t o                   */
/* ------------------------------------------------------------------------ */

void *Get_IntegrationFunctionAuto(int Type_Element, int Order, int *NumPoints)
{
  // TODO : compute correct number of points

  switch(Type_Element) {
  case POINT_ELEMENT: *NumPoints = 1; return ((void *)Gauss_Point);
  case LINE:
  case LINE_2: *NumPoints = 3; return ((void *)Gauss_Line);
  case TRIANGLE:
  case TRIANGLE_2: *NumPoints = 6; return ((void *)Gauss_Triangle);
  case QUADRANGLE:
  case QUADRANGLE_2:
  case QUADRANGLE_2_8N: *NumPoints = 7; return ((void *)Gauss_Quadrangle);
  case TETRAHEDRON:
  case TETRAHEDRON_2: *NumPoints = 15; return ((void *)Gauss_Tetrahedron);
  case HEXAHEDRON:
  case HEXAHEDRON_2:
  case HEXAHEDRON_2_20N: *NumPoints = 34; return ((void *)Gauss_Hexahedron);
  case PRISM:
  case PRISM_2:
  case PRISM_2_15N: *NumPoints = 21; return ((void *)Gauss_Prism);
  case PYRAMID:
  case PYRAMID_2:
  case PYRAMID_2_13N: *NumPoints = 8; return ((void *)Gauss_Pyramid);
  default:
    Message::Error("Unknown type of element for integration function");
    return 0;
  }
}

/* ------------------------------------------------------------------------ */
/*  G e o m e t r i c a l   T r a n s f o r m a t i o n s                   */
/* ------------------------------------------------------------------------ */

double power(double x, double y)
{
  if(y == 1.0)
    return (x);
  else if(y == 2.0)
    return (x * x);
  else if(y == 0.5)
    return (sqrt(x));
  else
    return (pow(x, y));
}

double Transformation(int Dim, int Type, struct Element *Element,
                      MATRIX3x3 *Jac)
{
  int i, Axis = 0;
  double X = 0., Y = 0., Z = 0.;
  double p = 1., L = 0.;
  double Ca = 0., Cx = 0., Cy = 0., Cz = 0., A = 0., B = 0., R = 0.;
  double theta = 0., XR = 0., YR = 0., ZR = 0., f = 0., dRdx = 0., dRdy = 0.,
         dRdz = 0.;
  double DetJac = 0.;

  /*
    A          = interior radius
    B          = exterior radius
    Ca         = position of axis
    Cx, Cy, Cz = coord of centre
    Axis       = direction of the transformation
    p          = exponant
    1/L        = f(B)
  */

  for(i = 0; i < Element->GeoElement->NbrNodes; i++) {
    X += Element->x[i] * Element->n[i];
    Y += Element->y[i] * Element->n[i];
    Z += Element->z[i] * Element->n[i];
  }

  if(Element->JacobianCase->NbrParameters >= 2) {
    A = Element->JacobianCase->Para[0];
    B = Element->JacobianCase->Para[1];
  }
  else
    Message::Error(
      "Missing interior and/or exterior radius for transformation Jacobian");

  if(Type == JACOBIAN_RECT) {
    if(Element->JacobianCase->NbrParameters >= 3)
      Axis = (int)Element->JacobianCase->Para[2];
    if(Element->JacobianCase->NbrParameters >= 4)
      Cx = Element->JacobianCase->Para[3];
    if(Element->JacobianCase->NbrParameters >= 5)
      Cy = Element->JacobianCase->Para[4];
    if(Element->JacobianCase->NbrParameters >= 6)
      Cz = Element->JacobianCase->Para[5];
    if(Element->JacobianCase->NbrParameters >= 7)
      p = Element->JacobianCase->Para[6];
    if(Element->JacobianCase->NbrParameters >= 8)
      L = Element->JacobianCase->Para[7];
    if(Element->JacobianCase->NbrParameters >= 9) {
      Message::Error(
        "Too many parameters for rectangular transformation Jacobian. "
        "Valid parameters: Dist1 Dist2 Axis CenterX CenterY CenterZ Power "
        "1/Infinity");
    }
  }
  else if(Type == JACOBIAN_SPH) {
    if(Element->JacobianCase->NbrParameters >= 3)
      Cx = Element->JacobianCase->Para[2];
    if(Element->JacobianCase->NbrParameters >= 4)
      Cy = Element->JacobianCase->Para[3];
    if(Element->JacobianCase->NbrParameters >= 5)
      Cz = Element->JacobianCase->Para[4];
    if(Element->JacobianCase->NbrParameters >= 6)
      p = Element->JacobianCase->Para[5];
    if(Element->JacobianCase->NbrParameters >= 7)
      L = Element->JacobianCase->Para[6];
    if(Element->JacobianCase->NbrParameters >= 8) {
      Message::Error(
        "Too many parameters for spherical transformation Jacobian. "
        "Valid parameters: Radius1 Radius2 CenterX CenterY CenterZ Power "
        "1/Infinity");
    }
  }
  else if(Type == JACOBIAN_VOL_CYL_SHELL) {
    if(Element->JacobianCase->NbrParameters >= 3)
      Axis = (int)Element->JacobianCase->Para[2];
    if(Element->JacobianCase->NbrParameters >= 4)
      Cx = Element->JacobianCase->Para[3];
    if(Element->JacobianCase->NbrParameters >= 5)
      Cy = Element->JacobianCase->Para[4];
    if(Element->JacobianCase->NbrParameters >= 6)
      Cz = Element->JacobianCase->Para[5];
    if(Element->JacobianCase->NbrParameters >= 7)
      p = Element->JacobianCase->Para[6];
    if(Element->JacobianCase->NbrParameters >= 8)
      L = Element->JacobianCase->Para[7];
    if(Element->JacobianCase->NbrParameters >= 9) {
      Message::Error(
        "Too many parameters for cylindrical transformation Jacobian. "
        "Valid parameters: Radius1 Radius2 Axis CenterX CenterY CenterZ "
        "Power 1/Infinity");
    }
  }
  else if(Type == JACOBIAN_VOL_UNI_DIR_SHELL) {
    if(Element->JacobianCase->NbrParameters >= 3)
      Axis = (int)Element->JacobianCase->Para[2];
    if(Element->JacobianCase->NbrParameters >= 4)
      Ca = Element->JacobianCase->Para[3];
    if(Element->JacobianCase->NbrParameters >= 5)
      p = Element->JacobianCase->Para[4];
    if(Element->JacobianCase->NbrParameters >= 6)
      L = Element->JacobianCase->Para[5];
    if(Element->JacobianCase->NbrParameters >= 7) {
      Message::Error(
        "Too many parameters for uni-directional transformation Jacobian. "
        "Valid parameters: Dist1 Dist2 Axis PositionAxis Power 1/Infinity");
    }
  }
  else
    Message::Error("Unknown type of transformation Jacobian");

  if(L) B = (B * B - A * A * L) / (B - A * L);

  if(Type == JACOBIAN_VOL_UNI_DIR_SHELL) {
    /* R is the distance from the plane whose normal vector is parallel to the
       axis and which contains the point (Ca,0,0),(0,Ca,0) or (0,0,Ca), for Axis
       equal to 1, 2, 3 respectively*/

    switch(Axis) {
    case 1: R = fabs(X - Ca); break;
    case 2: R = fabs(Y - Ca); break;
    case 3: R = fabs(Z - Ca); break;
    default:
      Message::Error("Bad axis specification: 1 for X, 2 for Y and 3 for Z");
    }

    if((fabs(R) > fabs(B) + 1.e-2 * fabs(B)) ||
       (fabs(R) < fabs(A) - 1.e-2 * fabs(A))) {
      Message::Error(
        "Bad parameters for unidirectional transformation Jacobian:"
        "Rint=%g, Rext=%g, R=%g",
        A, B, R);
    }

    if(B == R) {
      Jac->c11 = 1.;
      Jac->c12 = 0.;
      Jac->c13 = 0.;
      Jac->c21 = 0.;
      Jac->c22 = 1.;
      Jac->c23 = 0.;
      Jac->c31 = 0.;
      Jac->c32 = 0.;
      Jac->c33 = 1.;
      return (1.);
    }

    f = power((A * (B - A)) / (R * (B - R)), p);
    theta = p * (B - 2. * R) / (B - R);

    switch(Axis) {
    case 1:
      Jac->c11 = f * (1.0 - theta);
      Jac->c12 = 0.0;
      Jac->c13 = 0.0;
      Jac->c21 = 0.0;
      Jac->c22 = 1.0;
      Jac->c23 = 0.0;
      Jac->c31 = 0.0;
      Jac->c32 = 0.0;
      Jac->c33 = 1.0;

      DetJac = f * (1.0 - theta);
      break;

    case 2:
      Jac->c11 = 1.0;
      Jac->c12 = 0.0;
      Jac->c13 = 0.0;
      Jac->c21 = 0.0;
      Jac->c22 = f * (1.0 - theta);
      Jac->c23 = 0.0;
      Jac->c31 = 0.0;
      Jac->c32 = 0.0;
      Jac->c33 = 1.0;

      DetJac = f * (1.0 - theta);
      break;

    case 3:
      Jac->c11 = 1.0;
      Jac->c12 = 0.0;
      Jac->c13 = 0.0;
      Jac->c21 = 0.0;
      Jac->c22 = 1.0;
      Jac->c23 = 0.0;
      Jac->c31 = 0.0;
      Jac->c32 = 0.0;
      Jac->c33 = f * (1.0 - theta);

      DetJac = f * (1.0 - theta);
      break;
    }
  }
  else if(Type == JACOBIAN_VOL_CYL_SHELL) {
    if(!Axis) Axis = 3; // usual 2D case
    switch(Axis) {
    case 1:
      R = sqrt(SQU(Y - Cy) + SQU(Z - Cz));
      YR = (Y - Cy) / R;
      ZR = (Z - Cz) / R;
      dRdy = (Y - Cy) / R;
      dRdz = (Z - Cz) / R;
      break;
    case 2:
      R = sqrt(SQU(X - Cx) + SQU(Z - Cz));
      XR = (X - Cx) / R;
      ZR = (Z - Cz) / R;
      dRdx = (X - Cx) / R;
      dRdz = (Z - Cz) / R;
      break;
    case 3:
      R = sqrt(SQU(X - Cx) + SQU(Y - Cy));
      XR = (X - Cx) / R;
      YR = (Y - Cy) / R;
      dRdx = (X - Cx) / R;
      dRdy = (Y - Cy) / R;
      break;
    default:
      Message::Error("Bad axis specification : 1 for X, 2 for Y, 3 for Z");
    }
    if((fabs(R) > fabs(B) + 1.e-2 * fabs(B)) ||
       (fabs(R) < fabs(A) - 1.e-2 * fabs(A))) {
      Message::Error("Bad parameters for cylindrical transformation Jacobian:"
                     "Rint=%g, Rext=%g, R=%g",
                     A, B, R);
    }

    if(B == R) {
      Jac->c11 = 1.;
      Jac->c12 = 0.;
      Jac->c13 = 0.;
      Jac->c21 = 0.;
      Jac->c22 = 1.;
      Jac->c23 = 0.;
      Jac->c31 = 0.;
      Jac->c32 = 0.;
      Jac->c33 = 1.;
      return (1.);
    }

    f = power((A * (B - A)) / (R * (B - R)), p);
    theta = p * (B - 2. * R) / (B - R);

    switch(Axis) {
    case 1:
      Jac->c11 = 1.0;
      Jac->c12 = 0.0;
      Jac->c13 = 0.0;
      Jac->c21 = 0.0;
      Jac->c22 = f * (1.0 - theta * YR * dRdy);
      Jac->c23 = f * (-theta * YR * dRdz);
      Jac->c31 = 0.0;
      Jac->c32 = f * (-theta * ZR * dRdy);
      Jac->c33 = f * (1.0 - theta * ZR * dRdz);

      DetJac = f * f * (1.0 - theta);
      break;

    case 2:
      Jac->c11 = f * (1.0 - theta * XR * dRdx);
      Jac->c12 = 0.0;
      Jac->c13 = f * (-theta * XR * dRdz);
      Jac->c21 = 0.0;
      Jac->c22 = 1.0;
      Jac->c23 = 0.0;
      Jac->c31 = f * (-theta * ZR * dRdx);
      Jac->c32 = 0.0;
      Jac->c33 = f * (1.0 - theta * ZR * dRdz);

      DetJac = f * f * (1.0 - theta);
      break;

    case 3:
      Jac->c11 = f * (1.0 - theta * XR * dRdx);
      Jac->c12 = f * (-theta * XR * dRdy);
      Jac->c13 = 0.0;
      Jac->c21 = f * (-theta * YR * dRdx);
      Jac->c22 = f * (1.0 - theta * YR * dRdy);
      Jac->c23 = 0.0;
      Jac->c31 = 0.0;
      Jac->c32 = 0.0;
      Jac->c33 = 1.0;

      DetJac = f * f * (1.0 - theta);
      break;
    }
  }
  else {
    if(Type == JACOBIAN_SPH) {
      if(Dim == DIM_2D) {
        R = sqrt(SQU(X - Cx) + SQU(Y - Cy));
        dRdx = (X - Cx) / R;
        dRdy = (Y - Cy) / R;
      }
      else {
        R = sqrt(SQU(X - Cx) + SQU(Y - Cy) + SQU(Z - Cz));
        dRdx = (X - Cx) / R;
        dRdy = (Y - Cy) / R;
        dRdz = (Z - Cz) / R;
      }
    }
    else {
      switch(Axis) {
      case 1:
        R = fabs(X - Cx);
        dRdx = THESIGN(X - Cx);
        dRdy = 0.0;
        dRdz = 0.0;
        break;
      case 2:
        R = fabs(Y - Cy);
        dRdx = 0.0;
        dRdy = THESIGN(Y - Cy);
        dRdz = 0.0;
        break;
      case 3:
        R = fabs(Z - Cz);
        dRdx = 0.0;
        dRdy = 0.0;
        dRdz = THESIGN(Z - Cz);
        break;
      default:
        Message::Error("Bad axis specification: 1 for X, 2 for Y and 3 for Z");
      }
    }

    if((fabs(R) > fabs(B) + 1.e-2 * fabs(B)) ||
       (fabs(R) < fabs(A) - 1.e-2 * fabs(A))) {
      Message::Error(
        "Bad parameters for transformation Jacobian: %g not in [%g,%g]", R, A,
        B);
    }

    if(B == R) {
      Jac->c11 = 1.;
      Jac->c12 = 0.;
      Jac->c13 = 0.;
      Jac->c21 = 0.;
      Jac->c22 = 1.;
      Jac->c23 = 0.;
      Jac->c31 = 0.;
      Jac->c32 = 0.;
      Jac->c33 = 1.;
      return (1.);
    }

    f = power((A * (B - A)) / (R * (B - R)), p);
    theta = p * (B - 2. * R) / (B - R);
    XR = (X - Cx) / R;
    YR = (Y - Cy) / R;
    ZR = (Z - Cz) / R;

    Jac->c11 = f * (1.0 - theta * XR * dRdx);
    Jac->c12 = f * (-theta * XR * dRdy);
    Jac->c13 = f * (-theta * XR * dRdz);
    Jac->c21 = f * (-theta * YR * dRdx);
    Jac->c22 = f * (1.0 - theta * YR * dRdy);
    Jac->c23 = f * (-theta * YR * dRdz);
    Jac->c31 = f * (-theta * ZR * dRdx);
    Jac->c32 = f * (-theta * ZR * dRdy);
    Jac->c33 = f * (1.0 - theta * ZR * dRdz);

    switch(Dim) {
    case DIM_2D:
      Jac->c33 = 1.;
      DetJac = f * f * (1.0 - theta);
      // DetJac =  Jac->c11 * Jac->c22 - Jac->c12 * Jac->c21;
      break;
    case DIM_AXI: DetJac = f * f * f * (1.0 - theta); break;
    default:
      DetJac = f * f * f * (1.0 - theta);
      /*
        DetJac =  Jac->c11 * (Jac->c22 * Jac->c33 - Jac->c23*Jac->c32)
                - Jac->c12 * (Jac->c21 * Jac->c33 - Jac->c23*Jac->c31)
                + Jac->c13 * (Jac->c21 * Jac->c32 - Jac->c22*Jac->c31);
      */
      break;
    }
  }

  return (DetJac);
}

double PlpdX2D(struct Element *Element, MATRIX3x3 *Jac)
{
  int i;
  double CoorX, CoorY, A, B, R, theta, f;
  double DetJac;

  CoorX = CoorY = 0.;
  for(i = 0; i < Element->GeoElement->NbrNodes; i++) {
    CoorX += Element->x[i] * Element->n[i];
    CoorY += Element->y[i] * Element->n[i];
  }

  A = Element->JacobianCase->Para[0];
  B = Element->JacobianCase->Para[1];

  R = CoorX;

  if((R > B + 1.e-12 * B) || (R < A - 1.e-12 * A))
    Message::Error("Bad parameters for unidirectional transformation Jacobian: "
                   "Rint=%g, Rext=%g, R=%g",
                   A, B, R);

  if(B == R) {
    Jac->c11 = 1.;
    Jac->c12 = 0.;
    Jac->c21 = 0.;
    Jac->c22 = 1.;
    return (1.);
  }

  f = (A * (B - A)) / (R * (B - R));
  theta = (B - 2. * R) / (B - R);

  Jac->c11 = f * (1. - theta);
  Jac->c12 = 0.;
  Jac->c21 = 0.;
  Jac->c22 = 1.;

  DetJac = f * (1. - theta);

  return (DetJac);
}

/* ------------------------------------------------------------------------ */
/*  J a c o b i a n V o l                                                   */
/* ------------------------------------------------------------------------ */

/* 0D */

double JacobianVol0D(struct Element *Element, MATRIX3x3 *Jac)
{
  Jac->c11 = 1.;
  Jac->c12 = 0.;
  Jac->c13 = 0.;
  Jac->c21 = 0.;
  Jac->c22 = 1.;
  Jac->c23 = 0.;
  Jac->c31 = 0.;
  Jac->c32 = 0.;
  Jac->c33 = 1.;

  return (1.);
}

/* 1D */

double JacobianVol1D(struct Element *Element, MATRIX3x3 *Jac)
{
  int i;
  double DetJac;

  Jac->c11 = 0.;
  Jac->c12 = 0.;
  Jac->c13 = 0.;
  Jac->c21 = 0.;
  Jac->c22 = 1.;
  Jac->c23 = 0.;
  Jac->c31 = 0.;
  Jac->c32 = 0.;
  Jac->c33 = 1.;

  for(i = 0; i < Element->GeoElement->NbrNodes; i++) {
    Jac->c11 += Element->x[i] * Element->dndu[i][0];
  }

  DetJac = Jac->c11;

  return (DetJac);
}

/* 2D */

double JacobianVol2D(struct Element *Element, MATRIX3x3 *Jac)
{
  int i;
  double DetJac;

  Jac->c11 = 0.;
  Jac->c12 = 0.;
  Jac->c13 = 0.;
  Jac->c21 = 0.;
  Jac->c22 = 0.;
  Jac->c23 = 0.;
  Jac->c31 = 0.;
  Jac->c32 = 0.;
  Jac->c33 = 1.;

  for(i = 0; i < Element->GeoElement->NbrNodes; i++) {
    Jac->c11 += Element->x[i] * Element->dndu[i][0];
    Jac->c21 += Element->x[i] * Element->dndu[i][1];
    Jac->c12 += Element->y[i] * Element->dndu[i][0];
    Jac->c22 += Element->y[i] * Element->dndu[i][1];
  }

  DetJac = Jac->c11 * Jac->c22 - Jac->c12 * Jac->c21;

  return (DetJac);
}

double JacobianVolSphShell2D(struct Element *Element, MATRIX3x3 *Jac)
{
  MATRIX3x3 Jac1, Jac2;
  double DetJac1, DetJac2;

  DetJac1 = JacobianVol2D(Element, &Jac1);
  DetJac2 = Transformation(DIM_2D, JACOBIAN_SPH, Element, &Jac2);

  Get_ProductMatrix(DIM_2D, &Jac1, &Jac2, Jac);

  Jac->c13 = 0.;
  Jac->c23 = 0.;
  Jac->c31 = 0.;
  Jac->c32 = 0.;
  Jac->c33 = 1.;

  return (DetJac1 * DetJac2);
}

double JacobianVolRectShell2D(struct Element *Element, MATRIX3x3 *Jac)
{
  MATRIX3x3 Jac1, Jac2;
  double DetJac1, DetJac2;

  DetJac1 = JacobianVol2D(Element, &Jac1);
  DetJac2 = Transformation(DIM_2D, JACOBIAN_RECT, Element, &Jac2);

  Get_ProductMatrix(DIM_2D, &Jac1, &Jac2, Jac);

  Jac->c13 = 0.;
  Jac->c23 = 0.;
  Jac->c31 = 0.;
  Jac->c32 = 0.;
  Jac->c33 = 1.;

  return (DetJac1 * DetJac2);
}

double JacobianVolPlpdX2D(struct Element *Element, MATRIX3x3 *Jac)
{
  MATRIX3x3 Jac1, Jac2;
  double DetJac1, DetJac2;

  DetJac1 = JacobianVol2D(Element, &Jac1);
  DetJac2 = PlpdX2D(Element, &Jac2);

  Get_ProductMatrix(DIM_2D, &Jac1, &Jac2, Jac);

  Jac->c13 = 0.;
  Jac->c23 = 0.;
  Jac->c31 = 0.;
  Jac->c32 = 0.;
  Jac->c33 = 1.;

  return (DetJac1 * DetJac2);
}

/* 1D & 2D Axi (Attention, l'axe doit etre x=z=0) */

double JacobianVolAxi1D(struct Element *Element, MATRIX3x3 *Jac)
{
  int i;
  double s = 0., DetJac;

  DetJac = JacobianVol1D(Element, Jac);

  for(i = 0; i < Element->GeoElement->NbrNodes; i++)
    s += Element->x[i] * Element->n[i];

  /* Warning! For evaluations on the symmetry axis */
  if(s == 0.0) {
    for(i = 0; i < Element->GeoElement->NbrNodes; i++) s += Element->x[i];
    s /= (double)Element->GeoElement->NbrNodes;
  }

  Jac->c33 = s;

  return (DetJac * Jac->c33);
}

double JacobianVolAxi2D(struct Element *Element, MATRIX3x3 *Jac)
{
  int i;
  double s = 0., DetJac;

  DetJac = JacobianVol2D(Element, Jac);

  for(i = 0; i < Element->GeoElement->NbrNodes; i++)
    s += Element->x[i] * Element->n[i];

  /* Warning! For evaluations on the symmetry axis */
  if(s == 0.0) {
    for(i = 0; i < Element->GeoElement->NbrNodes; i++) s += Element->x[i];
    s /= (double)Element->GeoElement->NbrNodes;
  }

  Jac->c33 = s;

  return (DetJac * Jac->c33);
}

double JacobianVolAxiSphShell2D(struct Element *Element, MATRIX3x3 *Jac)
{
  MATRIX3x3 Jac1, Jac2;
  double DetJac1, DetJac2;

  DetJac1 = JacobianVolAxi2D(Element, &Jac1);
  DetJac2 = Transformation(DIM_AXI, JACOBIAN_SPH, Element, &Jac2);

  Get_ProductMatrix(DIM_2D, &Jac1, &Jac2, Jac);

  Jac->c13 = 0.;
  Jac->c23 = 0.;
  Jac->c31 = 0.;
  Jac->c32 = 0.;
  Jac->c33 = Jac1.c33 * Jac2.c33;

  return (DetJac1 * DetJac2);
}

double JacobianVolAxiRectShell2D(struct Element *Element, MATRIX3x3 *Jac)
{
  MATRIX3x3 Jac1, Jac2;
  double DetJac1, DetJac2;

  DetJac1 = JacobianVolAxi2D(Element, &Jac1);
  DetJac2 = Transformation(DIM_AXI, JACOBIAN_RECT, Element, &Jac2);

  Get_ProductMatrix(DIM_2D, &Jac1, &Jac2, Jac);

  Jac->c13 = 0.;
  Jac->c23 = 0.;
  Jac->c31 = 0.;
  Jac->c32 = 0.;
  Jac->c33 = Jac1.c33 * Jac2.c33;

  return (DetJac1 * DetJac2);
}

double JacobianVolAxiPlpdX2D(struct Element *Element, MATRIX3x3 *Jac)
{
  MATRIX3x3 Jac1, Jac2;
  double DetJac1, DetJac2;

  DetJac1 = JacobianVolAxi2D(Element, &Jac1);
  DetJac2 = PlpdX2D(Element, &Jac2);

  Get_ProductMatrix(DIM_2D, &Jac1, &Jac2, Jac);

  Jac->c13 = 0.;
  Jac->c23 = 0.;
  Jac->c31 = 0.;
  Jac->c32 = 0.;
  Jac->c33 = Jac1.c33;

  return (DetJac1 * DetJac2);
}

/* 1D & 2D Axi avec transformation quadratique (Attention, l'axe doit etre
 * x=z=0) */
double JacobianVolAxiSqu1D(struct Element *Element, MATRIX3x3 *Jac)
{
  int i;
  double s = 0., r, DetJac;

  Jac->c11 = 0.;
  Jac->c12 = 0.;
  Jac->c13 = 0.;
  Jac->c21 = 0.;
  Jac->c22 = 1.;
  Jac->c23 = 0.;
  Jac->c31 = 0.;
  Jac->c32 = 0.;
  Jac->c33 = 1.;

  for(i = 0; i < Element->GeoElement->NbrNodes; i++)
    s += SQU(Element->x[i]) * Element->n[i];

  /* Warning! For evaluations on the symmetry axis */
  if(s == 0.0) {
    for(i = 0; i < Element->GeoElement->NbrNodes; i++)
      s += Element->x[i] * Element->x[i];
    s /= (double)Element->GeoElement->NbrNodes;
  }

  r = sqrt(s);

  for(i = 0; i < Element->GeoElement->NbrNodes; i++) {
    Jac->c11 += 0.5 / r * SQU(Element->x[i]) * Element->dndu[i][0];
  }
  Jac->c33 = r;

  DetJac = Jac->c11 * Jac->c33;

  return (DetJac);
}

double JacobianVolAxiSqu2D(struct Element *Element, MATRIX3x3 *Jac)
{
  int i;
  double s = 0., r, DetJac;

  Jac->c11 = 0.;
  Jac->c12 = 0.;
  Jac->c13 = 0.;
  Jac->c21 = 0.;
  Jac->c22 = 0.;
  Jac->c23 = 0.;
  Jac->c31 = 0.;
  Jac->c32 = 0.;
  Jac->c33 = 1.;

  for(i = 0; i < Element->GeoElement->NbrNodes; i++)
    s += SQU(Element->x[i]) * Element->n[i];

  /* Warning! For evaluations on the symmetry axis */
  if(s == 0.0) {
    for(i = 0; i < Element->GeoElement->NbrNodes; i++)
      s += Element->x[i] * Element->x[i];
    s /= (double)Element->GeoElement->NbrNodes;
  }

  r = sqrt(s);

  for(i = 0; i < Element->GeoElement->NbrNodes; i++) {
    Jac->c11 += 0.5 / r * SQU(Element->x[i]) * Element->dndu[i][0];
    Jac->c21 += 0.5 / r * SQU(Element->x[i]) * Element->dndu[i][1];
    Jac->c12 += Element->y[i] * Element->dndu[i][0];
    Jac->c22 += Element->y[i] * Element->dndu[i][1];
  }
  Jac->c33 = r;

  DetJac = (Jac->c11 * Jac->c22 - Jac->c12 * Jac->c21) * Jac->c33;

  return (DetJac);
}

double JacobianVolAxiSquSphShell2D(struct Element *Element, MATRIX3x3 *Jac)
{
  MATRIX3x3 Jac1, Jac2;
  double DetJac1, DetJac2;

  DetJac1 = JacobianVolAxiSqu2D(Element, &Jac1);
  DetJac2 = Transformation(DIM_AXI, JACOBIAN_SPH, Element, &Jac2);

  Get_ProductMatrix(DIM_2D, &Jac1, &Jac2, Jac);

  Jac->c13 = 0.;
  Jac->c23 = 0.;
  Jac->c31 = 0.;
  Jac->c32 = 0.;
  Jac->c33 = Jac1.c33 * Jac2.c33;

  return (DetJac1 * DetJac2);
}

double JacobianVolAxiSquRectShell2D(struct Element *Element, MATRIX3x3 *Jac)
{
  MATRIX3x3 Jac1, Jac2;
  double DetJac1, DetJac2;

  DetJac1 = JacobianVolAxiSqu2D(Element, &Jac1);
  DetJac2 = Transformation(DIM_AXI, JACOBIAN_RECT, Element, &Jac2);

  Get_ProductMatrix(DIM_2D, &Jac1, &Jac2, Jac);

  Jac->c13 = 0.;
  Jac->c23 = 0.;
  Jac->c31 = 0.;
  Jac->c32 = 0.;
  Jac->c33 = Jac1.c33 * Jac2.c33;

  return (DetJac1 * DetJac2);
}

/* 3D */

double JacobianVol3D(struct Element *Element, MATRIX3x3 *Jac)
{
  int i;
  double DetJac;

  Jac->c11 = 0.;
  Jac->c12 = 0.;
  Jac->c13 = 0.;
  Jac->c21 = 0.;
  Jac->c22 = 0.;
  Jac->c23 = 0.;
  Jac->c31 = 0.;
  Jac->c32 = 0.;
  Jac->c33 = 0.;

  for(i = 0; i < Element->GeoElement->NbrNodes; i++) {
    Jac->c11 += Element->x[i] * Element->dndu[i][0];
    Jac->c21 += Element->x[i] * Element->dndu[i][1];
    Jac->c31 += Element->x[i] * Element->dndu[i][2];

    Jac->c12 += Element->y[i] * Element->dndu[i][0];
    Jac->c22 += Element->y[i] * Element->dndu[i][1];
    Jac->c32 += Element->y[i] * Element->dndu[i][2];

    Jac->c13 += Element->z[i] * Element->dndu[i][0];
    Jac->c23 += Element->z[i] * Element->dndu[i][1];
    Jac->c33 += Element->z[i] * Element->dndu[i][2];
  }

  DetJac = Jac->c11 * Jac->c22 * Jac->c33 + Jac->c13 * Jac->c21 * Jac->c32 +
           Jac->c12 * Jac->c23 * Jac->c31 - Jac->c13 * Jac->c22 * Jac->c31 -
           Jac->c11 * Jac->c23 * Jac->c32 - Jac->c12 * Jac->c21 * Jac->c33;

  return (DetJac);
}

double JacobianVolSphShell3D(struct Element *Element, MATRIX3x3 *Jac)
{
  MATRIX3x3 Jac1, Jac2;
  double DetJac1, DetJac2;

  DetJac1 = JacobianVol3D(Element, &Jac1);
  DetJac2 = Transformation(DIM_3D, JACOBIAN_SPH, Element, &Jac2);

  Get_ProductMatrix(DIM_3D, &Jac1, &Jac2, Jac);

  return (DetJac1 * DetJac2);
}

double JacobianVolCylShell3D(struct Element *Element, MATRIX3x3 *Jac)
{
  MATRIX3x3 Jac1, Jac2;
  double DetJac1, DetJac2;

  DetJac1 = JacobianVol3D(Element, &Jac1);
  DetJac2 = Transformation(DIM_3D, JACOBIAN_VOL_CYL_SHELL, Element, &Jac2);

  Get_ProductMatrix(DIM_3D, &Jac1, &Jac2, Jac);

  return (DetJac1 * DetJac2);
}

double JacobianVolRectShell3D(struct Element *Element, MATRIX3x3 *Jac)
{
  MATRIX3x3 Jac1, Jac2;
  double DetJac1, DetJac2;

  DetJac1 = JacobianVol3D(Element, &Jac1);
  DetJac2 = Transformation(DIM_3D, JACOBIAN_RECT, Element, &Jac2);

  Get_ProductMatrix(DIM_3D, &Jac1, &Jac2, Jac);

  return (DetJac1 * DetJac2);
}

double JacobianVolUniDirShell3D(struct Element *Element, MATRIX3x3 *Jac)
{
  MATRIX3x3 Jac1, Jac2;
  double DetJac1, DetJac2;

  DetJac1 = JacobianVol3D(Element, &Jac1);
  DetJac2 = Transformation(DIM_3D, JACOBIAN_VOL_UNI_DIR_SHELL, Element, &Jac2);

  Get_ProductMatrix(DIM_3D, &Jac1, &Jac2, Jac);

  return (DetJac1 * DetJac2);
}

/* ------------------------------------------------------------------------ */
/*  J a c o b i a n S u r                                                   */
/* ------------------------------------------------------------------------ */

static void prodve(double a[3], double b[3], double c[3])
{
  c[2] = a[0] * b[1] - a[1] * b[0];
  c[1] = -a[0] * b[2] + a[2] * b[0];
  c[0] = a[1] * b[2] - a[2] * b[1];
}

static double norm3(double a[3])
{
  return sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]);
}

static double norme(double a[3])
{
  const double mod = norm3(a);
  if(mod != 0.0) {
    const double one_over_mod = 1. / mod;
    a[0] *= one_over_mod;
    a[1] *= one_over_mod;
    a[2] *= one_over_mod;
  }
  return mod;
}

double JacobianSur2D(struct Element *Element, MATRIX3x3 *Jac)
{
  int i;
  double DetJac;

  Jac->c11 = 0.;
  Jac->c12 = 0.;
  Jac->c13 = 0.;
  Jac->c21 = 0.;
  Jac->c22 = 0.;
  Jac->c23 = 0.;
  Jac->c31 = 0.;
  Jac->c32 = 0.;
  Jac->c33 = 1.;

  for(i = 0; i < Element->GeoElement->NbrNodes; i++) {
    Jac->c11 += Element->x[i] * Element->dndu[i][0];
    Jac->c12 += Element->y[i] * Element->dndu[i][0];
  }

  DetJac = HYPOT(Jac->c11, Jac->c12);

  // regularize matrix
  double b[3] = {Jac->c12, -Jac->c11, 0.};
  norme(b);
  Jac->c21 = b[0];
  Jac->c22 = b[1];

  // make sure DetJac > 0: this is not necessary in theory, but it is
  // required here because we use DetJac when we invert the matrix
  double realDetJac = Jac->c11 * Jac->c22 - Jac->c12 * Jac->c21;
  if(realDetJac < 0.) {
    Jac->c21 = -Jac->c21;
    Jac->c22 = -Jac->c22;
  }

  return (DetJac);
}

double JacobianSurSphShell2D(struct Element *Element, MATRIX3x3 *Jac)
{
  MATRIX3x3 Jac1, Jac2;
  double DetJac1, DetJac2;

  DetJac1 = JacobianSur2D(Element, &Jac1);
  DetJac2 = Transformation(DIM_2D, JACOBIAN_SPH, Element, &Jac2);

  Get_ProductMatrix(DIM_3D, &Jac1, &Jac2, Jac);

  return (DetJac1 * DetJac2);
}

double JacobianSurRectShell2D(struct Element *Element, MATRIX3x3 *Jac)
{
  MATRIX3x3 Jac1, Jac2;
  double DetJac1, DetJac2;

  DetJac1 = JacobianSur2D(Element, &Jac1);
  DetJac2 = Transformation(DIM_2D, JACOBIAN_RECT, Element, &Jac2);

  Get_ProductMatrix(DIM_3D, &Jac1, &Jac2, Jac);

  return (DetJac1 * DetJac2);
}

double JacobianSurAxi2D(struct Element *Element, MATRIX3x3 *Jac)
{
  int i;
  double DetJac;

  DetJac = JacobianSur2D(Element, Jac);

  Jac->c33 = 0.;
  for(i = 0; i < Element->GeoElement->NbrNodes; i++)
    Jac->c33 += Element->x[i] * Element->n[i];
  return (DetJac * Jac->c33);
}

double JacobianSur3D(struct Element *Element, MATRIX3x3 *Jac)
{
  int i;
  double DetJac;

  Jac->c11 = 0.;
  Jac->c12 = 0.;
  Jac->c13 = 0.;
  Jac->c21 = 0.;
  Jac->c22 = 0.;
  Jac->c23 = 0.;
  Jac->c31 = 0.;
  Jac->c32 = 0.;
  Jac->c33 = 0.;

  for(i = 0; i < Element->GeoElement->NbrNodes; i++) {
    Jac->c11 += Element->x[i] * Element->dndu[i][0];
    Jac->c21 += Element->x[i] * Element->dndu[i][1];

    Jac->c12 += Element->y[i] * Element->dndu[i][0];
    Jac->c22 += Element->y[i] * Element->dndu[i][1];

    Jac->c13 += Element->z[i] * Element->dndu[i][0];
    Jac->c23 += Element->z[i] * Element->dndu[i][1];
  }

  DetJac = sqrt(SQU(Jac->c11 * Jac->c22 - Jac->c12 * Jac->c21) +
                SQU(Jac->c13 * Jac->c21 - Jac->c11 * Jac->c23) +
                SQU(Jac->c12 * Jac->c23 - Jac->c13 * Jac->c22));

  // regularize matrix
  double a[3] = {Jac->c11, Jac->c12, Jac->c13};
  double b[3] = {Jac->c21, Jac->c22, Jac->c23};
  double c[3];
  prodve(a, b, c);
  norme(c);
  Jac->c31 = c[0];
  Jac->c32 = c[1];
  Jac->c33 = c[2];

  // make sure DetJac > 0: this is not necessary in theory, but it is
  // required here because we use DetJac when we invert the matrix
  double realDetJac =
    Jac->c11 * Jac->c22 * Jac->c33 + Jac->c13 * Jac->c21 * Jac->c32 +
    Jac->c12 * Jac->c23 * Jac->c31 - Jac->c13 * Jac->c22 * Jac->c31 -
    Jac->c11 * Jac->c23 * Jac->c32 - Jac->c12 * Jac->c21 * Jac->c33;
  if(realDetJac < 0.) {
    Jac->c31 = -Jac->c31;
    Jac->c32 = -Jac->c32;
    Jac->c33 = -Jac->c33;
  }

  return (DetJac);
}

/* ------------------------------------------------------------------------ */
/*  J a c o b i a n L i n                                                   */
/* ------------------------------------------------------------------------ */

double JacobianLin3D(struct Element *Element, MATRIX3x3 *Jac)
{
  int i;
  double DetJac;

  Jac->c11 = 0.;
  Jac->c12 = 0.;
  Jac->c13 = 0.;
  Jac->c21 = 0.;
  Jac->c22 = 0.;
  Jac->c23 = 0.;
  Jac->c31 = 0.;
  Jac->c32 = 0.;
  Jac->c33 = 0.;

  for(i = 0; i < Element->GeoElement->NbrNodes; i++) {
    Jac->c11 += Element->x[i] * Element->dndu[i][0];
    Jac->c12 += Element->y[i] * Element->dndu[i][0];
    Jac->c13 += Element->z[i] * Element->dndu[i][0];
  }

  DetJac = sqrt(SQU(Jac->c11) + SQU(Jac->c12) + SQU(Jac->c13));

  // regularize matrix
  double a[3] = {Jac->c11, Jac->c12, Jac->c13};
  double b[3];
  if((fabs(a[0]) >= fabs(a[1]) && fabs(a[0]) >= fabs(a[2])) ||
     (fabs(a[1]) >= fabs(a[0]) && fabs(a[1]) >= fabs(a[2]))) {
    b[0] = a[1];
    b[1] = -a[0];
    b[2] = 0.;
  }
  else {
    b[0] = 0.;
    b[1] = a[2];
    b[2] = -a[1];
  }
  norme(b);
  double c[3];
  prodve(a, b, c);
  norme(c);
  Jac->c21 = b[0];
  Jac->c22 = b[1];
  Jac->c23 = b[2];
  Jac->c31 = c[0];
  Jac->c32 = c[1];
  Jac->c33 = c[2];

  // make sure DetJac > 0: this is not necessary in theory, but it is
  // required here because we use DetJac when we invert the matrix
  double realDetJac =
    Jac->c11 * Jac->c22 * Jac->c33 + Jac->c13 * Jac->c21 * Jac->c32 +
    Jac->c12 * Jac->c23 * Jac->c31 - Jac->c13 * Jac->c22 * Jac->c31 -
    Jac->c11 * Jac->c23 * Jac->c32 - Jac->c12 * Jac->c21 * Jac->c33;
  if(realDetJac < 0.) {
    Jac->c31 = -Jac->c31;
    Jac->c32 = -Jac->c32;
    Jac->c33 = -Jac->c33;
  }

  return (DetJac);
}

/* ------------------------------------------------------------------------ */
/*  G e t _ I n v e r s e M a t r i x                                       */
/* ------------------------------------------------------------------------ */

void Get_InverseMatrix(int Type_Dimension, int Type_Element, double DetMat,
                       MATRIX3x3 *Mat, MATRIX3x3 *InvMat)
{
  switch(Type_Dimension) {
  case DIM_0D:
    InvMat->c11 = InvMat->c22 = InvMat->c33 = 1.;
    InvMat->c12 = InvMat->c21 = 0.;
    InvMat->c13 = InvMat->c31 = 0.;
    InvMat->c23 = InvMat->c32 = 0.;
    break;

  case DIM_1D:
    InvMat->c11 = 1. / Mat->c11;
    InvMat->c22 = 1. / Mat->c22;
    InvMat->c33 = 1. / Mat->c33;
    InvMat->c12 = InvMat->c21 = 0.;
    InvMat->c13 = InvMat->c31 = 0.;
    InvMat->c23 = InvMat->c32 = 0.;
    break;

  case DIM_2D:
    if(!DetMat)
      Message::Error("Null determinant in 'Get_InverseMatrix' (%d)",
                     Type_Dimension);
    InvMat->c11 = Mat->c22 * Mat->c33 / DetMat;
    InvMat->c21 = -Mat->c21 * Mat->c33 / DetMat;
    InvMat->c12 = -Mat->c12 * Mat->c33 / DetMat;
    InvMat->c22 = Mat->c11 * Mat->c33 / DetMat;
    InvMat->c13 = InvMat->c23 = InvMat->c31 = InvMat->c32 = 0.;
    InvMat->c33 = 1. / Mat->c33;
    break;

  case DIM_3D:
    if(!DetMat)
      Message::Error("Null determinant in 'Get_InverseMatrix' (%d)",
                     Type_Dimension);
    InvMat->c11 = (Mat->c22 * Mat->c33 - Mat->c23 * Mat->c32) / DetMat;
    InvMat->c21 = -(Mat->c21 * Mat->c33 - Mat->c23 * Mat->c31) / DetMat;
    InvMat->c31 = (Mat->c21 * Mat->c32 - Mat->c22 * Mat->c31) / DetMat;
    InvMat->c12 = -(Mat->c12 * Mat->c33 - Mat->c13 * Mat->c32) / DetMat;
    InvMat->c22 = (Mat->c11 * Mat->c33 - Mat->c13 * Mat->c31) / DetMat;
    InvMat->c32 = -(Mat->c11 * Mat->c32 - Mat->c12 * Mat->c31) / DetMat;
    InvMat->c13 = (Mat->c12 * Mat->c23 - Mat->c13 * Mat->c22) / DetMat;
    InvMat->c23 = -(Mat->c11 * Mat->c23 - Mat->c13 * Mat->c21) / DetMat;
    InvMat->c33 = (Mat->c11 * Mat->c22 - Mat->c12 * Mat->c21) / DetMat;
    break;

  default: Message::Error("Wrong dimension in 'Get_InverseMatrix'"); break;
  }
}

/* ------------------------------------------------------------------------ */
/*  G e t _ P r o d u c t M a t r i x                                       */
/* ------------------------------------------------------------------------ */

void Get_ProductMatrix(int Type_Dimension, MATRIX3x3 *A, MATRIX3x3 *B,
                       MATRIX3x3 *AB)
{
  switch(Type_Dimension) {
  case DIM_2D:
    AB->c11 = A->c11 * B->c11 + A->c12 * B->c21;
    AB->c12 = A->c11 * B->c12 + A->c12 * B->c22;
    AB->c21 = A->c21 * B->c11 + A->c22 * B->c21;
    AB->c22 = A->c21 * B->c12 + A->c22 * B->c22;
    break;

  case DIM_3D:
    AB->c11 = A->c11 * B->c11 + A->c12 * B->c21 + A->c13 * B->c31;
    AB->c12 = A->c11 * B->c12 + A->c12 * B->c22 + A->c13 * B->c32;
    AB->c13 = A->c11 * B->c13 + A->c12 * B->c23 + A->c13 * B->c33;
    AB->c21 = A->c21 * B->c11 + A->c22 * B->c21 + A->c23 * B->c31;
    AB->c22 = A->c21 * B->c12 + A->c22 * B->c22 + A->c23 * B->c32;
    AB->c23 = A->c21 * B->c13 + A->c22 * B->c23 + A->c23 * B->c33;
    AB->c31 = A->c31 * B->c11 + A->c32 * B->c21 + A->c33 * B->c31;
    AB->c32 = A->c31 * B->c12 + A->c32 * B->c22 + A->c33 * B->c32;
    AB->c33 = A->c31 * B->c13 + A->c32 * B->c23 + A->c33 * B->c33;
    break;
  }
}

/* ------------------------------------------------------------------------ */
/*   G e t _ C h a n g e O f C o o r d i n a t e s                          */
/* ------------------------------------------------------------------------ */

void *Get_ChangeOfCoordinates(int Flag_ChangeCoord, int Type_Form)
{
  switch(Type_Form) {
  case SCALAR:
  case FORM0: return ((void *)ChangeOfCoord_No1);

  case FORM1:
    return ((Flag_ChangeCoord) ? (void *)ChangeOfCoord_Form1 :
                                 (void *)ChangeOfCoord_No123);

  case FORM2:
    return ((Flag_ChangeCoord) ? (void *)ChangeOfCoord_Form2 :
                                 (void *)ChangeOfCoord_No123);

  case FORM3:
  case FORM3P:
    return ((Flag_ChangeCoord) ? (void *)ChangeOfCoord_Form3 :
                                 (void *)ChangeOfCoord_No1);

  case FORM1P:
    return ((Flag_ChangeCoord) ? (void *)ChangeOfCoord_Form1P :
                                 (void *)ChangeOfCoord_No123);

  case FORM2P:
    return ((Flag_ChangeCoord) ? (void *)ChangeOfCoord_Form2P :
                                 (void *)ChangeOfCoord_No123);

  case VECTOR: return ((void *)ChangeOfCoord_No123);

  case FORM1S:
    return ((Flag_ChangeCoord) ? (void *)ChangeOfCoord_Form1S :
                                 (void *)ChangeOfCoord_No123);

  default:
    Message::Error("Unknown type of field (%s)",
                   Get_StringForDefine(Field_Type, Type_Form));
    return (NULL);
  }
}

/* ------------------------------------------------------------------------ */
/*   C h a n g e O f C o o r d _ X X X                                      */
/* ------------------------------------------------------------------------ */

void ChangeOfCoord_No1(struct Element *Element, double vBFu[], double vBFx[])
{
  vBFx[0] = vBFu[0];
}

void ChangeOfCoord_No123(struct Element *Element, double vBFu[], double vBFx[])
{
  vBFx[0] = vBFu[0];
  vBFx[1] = vBFu[1];
  vBFx[2] = vBFu[2];
}

void ChangeOfCoord_Form1(struct Element *Element, double vBFu[], double vBFx[])
{
  vBFx[0] = vBFu[0] * Element->InvJac.c11 + vBFu[1] * Element->InvJac.c12 +
            vBFu[2] * Element->InvJac.c13;
  vBFx[1] = vBFu[0] * Element->InvJac.c21 + vBFu[1] * Element->InvJac.c22 +
            vBFu[2] * Element->InvJac.c23;
  vBFx[2] = vBFu[0] * Element->InvJac.c31 + vBFu[1] * Element->InvJac.c32 +
            vBFu[2] * Element->InvJac.c33;
}

void ChangeOfCoord_Form2(struct Element *Element, double vBFu[], double vBFx[])
{
  if(!Element->DetJac)
    Message::Error("Null determinant in 'ChangeOfCoord_Form2'");

  vBFx[0] = (vBFu[0] * Element->Jac.c11 + vBFu[1] * Element->Jac.c21 +
             vBFu[2] * Element->Jac.c31) /
            Element->DetJac;
  vBFx[1] = (vBFu[0] * Element->Jac.c12 + vBFu[1] * Element->Jac.c22 +
             vBFu[2] * Element->Jac.c32) /
            Element->DetJac;
  vBFx[2] = (vBFu[0] * Element->Jac.c13 + vBFu[1] * Element->Jac.c23 +
             vBFu[2] * Element->Jac.c33) /
            Element->DetJac;
}

void ChangeOfCoord_Form3(struct Element *Element, double vBFu[], double vBFx[])
{
  if(!Element->DetJac)
    Message::Error("Null determinant in 'ChangeOfCoord_Form3'");

  vBFx[0] = vBFu[0] / Element->DetJac;
}

/* Form1P, 2P, 1S : valid in 2D only ! */

void ChangeOfCoord_Form1P(struct Element *Element, double vBFu[], double vBFx[])
{
  vBFx[0] = 0.;
  vBFx[1] = 0.;
  vBFx[2] = vBFu[2] / Element->Jac.c33; /* ... * Element->InvJac.c33 */
}

void ChangeOfCoord_Form2P(struct Element *Element, double vBFu[], double vBFx[])
{
  if(!Element->DetJac)
    Message::Error("Null determinant in 'ChangeOfCoord_Form2P' %d %d %d",
                   Element->Num, Element->Type, Element->Region);

  vBFx[0] =
    (vBFu[0] * Element->Jac.c11 + vBFu[1] * Element->Jac.c21) / Element->DetJac;
  vBFx[1] =
    (vBFu[0] * Element->Jac.c12 + vBFu[1] * Element->Jac.c22) / Element->DetJac;
  vBFx[2] = 0.;
}

void ChangeOfCoord_Form1S(struct Element *Element, double vBFu[], double vBFx[])
{
  if(!Element->DetJac)
    Message::Error("Null determinant in 'ChangeOfCoord_Form1S'");

  vBFx[0] = 0.;
  vBFx[1] = 0.;
  vBFx[2] = vBFu[0] / Element->DetJac;
}

/* ------------------------------------------------------------------------ */
/*   C a l _ P r o d u c t X X X                                            */
/* ------------------------------------------------------------------------ */

double Cal_Product123(double v1[], double v2[])
{
  return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
}

double Cal_Product12(double v1[], double v2[])
{
  return v1[0] * v2[0] + v1[1] * v2[1];
}

double Cal_Product3(double v1[], double v2[]) { return v1[2] * v2[2]; }

double Cal_Product1(double v1[], double v2[]) { return v1[0] * v2[0]; }
