/*
    Copyright (C) 1998  Dennis Roddeman
    email: dennis.roddeman@uibk.ac.at

    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 
    59 Temple Place, Suite 330, Boston, MA, 02111-1307, USA
*/

#include "tochnog.h"

#define EPS_D 1.e-8
#define EPS_PECLET 1.e-10

void general( long int element, long int name, long int nnol, long int element_group, 
  long int type, long int nodes[], double old_dof[], double new_dof[], 
  double new_unknowns[], double new_grad_new_unknowns[], double h[], 
  double new_d[], double volume, double element_rhside[], 
  double element_rhside_internal[], double element_rhside_static[], 
  double element_residue[], double element_lhside[], double element_matrix[],
  double cons_var_vel[], double grad_cons_var_flow[] )

{
  long int inol=0, inod=0, jnol=0, jdim=0, ipuknwn=0, iuknwn=0,
    itmp=0, swit=0, stokes=-NO, icontrol=0, unknown_belongs_to_type=0,
    length=0, indx=0, indxi=0, indxj=0,
    ldum=0, idum[1], node_locate[MDIM], 
    skip_spatial_stabilization[MPUKNWN], skip_inertia[MPUKNWN],
    skip_convective_part[MPUKNWN], dof_type[MUKNWN], dof_principal[MUKNWN];
  double condif_conductivity=0., pe=0., dens=0., condif_capacity=0., visc=0., vel=0.,
    co=0., dtime=0., diffusion=0., inertia=0.,
    conv_part=0., diff_part=0., peclet=0., peclet_factor=0., 
    artificial_diffusion=0., tmp=0., D=0., fac=0., ddum[1], node_remesh_velocity[MDIM];

  swit = set_swit(element,-1,"general");
  if ( swit ) pri( "In GENERAL" );

  db( DOF_TYPE, 0, dof_type, ddum, ldum, VERSION_NORMAL, GET_IF_EXISTS );
  db( DOF_PRINCIPAL, 0, dof_principal, ddum, ldum, VERSION_NORMAL, GET_IF_EXISTS );
  db( DTIME, 0, idum, &dtime, ldum, VERSION_NEW, GET );
  db( ICONTROL, 0, &icontrol, ddum, ldum, VERSION_NORMAL, GET );

  array_set( skip_inertia, -NO, npuknwn );
  if ( db( CONTROL_SKIP_INERTIA_CONDIF_TEMPERATURE, icontrol, &itmp, ddum, ldum, 
           VERSION_NORMAL, GET_IF_EXISTS ) ) {
    ipuknwn = temp_indx/nder;
    skip_inertia[ipuknwn] = itmp;
  }
  if ( db( CONTROL_SKIP_INERTIA_GROUNDFLOW_PRESSURE, icontrol, &itmp, ddum, ldum, 
           VERSION_NORMAL, GET_IF_EXISTS ) ) {
    ipuknwn = pres_indx/nder;
    skip_inertia[ipuknwn] = itmp;
  }
  if ( db( CONTROL_SKIP_INERTIA_MATERI_VELOCITY, icontrol, &itmp, ddum, ldum, 
           VERSION_NORMAL, GET_IF_EXISTS ) ) {
    for ( jdim=0; jdim<ndim; jdim++ ) {
      ipuknwn = vel_indx/nder + jdim;
      skip_inertia[ipuknwn] = itmp;
    }
  }
  if ( db( CONTROL_SKIP_INERTIA_WAVE_FSCALAR, icontrol, &itmp, ddum, ldum, 
           VERSION_NORMAL, GET_IF_EXISTS ) ) {
    ipuknwn = fscal_indx/nder;
    skip_inertia[ipuknwn] = itmp;
  }

  if ( db_active_index( CONTROL_SKIP_CONVECTIVE_PART, icontrol, VERSION_NORMAL ) ) {
    if ( db_len( CONTROL_SKIP_CONVECTIVE_PART, icontrol, VERSION_NORMAL ) == npuknwn )
      db( CONTROL_SKIP_CONVECTIVE_PART, icontrol, skip_convective_part, 
        ddum, ldum, VERSION_NORMAL, GET );
    else {
      length = 1;
      db( CONTROL_SKIP_CONVECTIVE_PART, icontrol, skip_convective_part, ddum, 
        length, VERSION_NORMAL, GET_AND_CHECK );
    }
    if ( skip_convective_part[0]==-ALL )
      array_set( skip_convective_part, -YES, npuknwn );
  }
  else array_set( skip_convective_part, -NO, npuknwn );

  if ( db_active_index( CONTROL_SKIP_SPATIAL_STABILIZATION, icontrol, VERSION_NORMAL ) ) {
    if ( db_len( CONTROL_SKIP_SPATIAL_STABILIZATION, icontrol, VERSION_NORMAL ) == npuknwn )
      db( CONTROL_SKIP_SPATIAL_STABILIZATION, icontrol, skip_spatial_stabilization,
        ddum, ldum, VERSION_NORMAL, GET );
    else {
      length = 1;
      db( CONTROL_SKIP_SPATIAL_STABILIZATION, icontrol, skip_spatial_stabilization, 
        ddum, length, VERSION_NORMAL, GET_AND_CHECK );
    }
    if ( skip_spatial_stabilization[0]==-ALL )
      array_set( skip_spatial_stabilization, -YES, npuknwn );
  }
  else array_set( skip_spatial_stabilization, -NO, npuknwn );

  get_group_data( GROUP_CONDIF_CAPACITY, element_group, new_unknowns, 
    &condif_capacity, ldum, GET_IF_EXISTS );
  get_group_data( GROUP_CONDIF_CONDUCTIVITY, element_group, new_unknowns, 
    &condif_conductivity, ldum, GET_IF_EXISTS );
  get_group_data( GROUP_GROUNDFLOW_COMPRESSIBILITY, element_group, new_unknowns, 
    &co, ldum, GET_IF_EXISTS );
  get_group_data( GROUP_GROUNDFLOW_DENSITY, element_group, new_unknowns, 
    &dens, ldum, GET_IF_EXISTS );
  get_group_data( GROUP_GROUNDFLOW_PERMEABILITY, element_group, new_unknowns, 
    &pe, ldum, GET_IF_EXISTS );
  get_group_data( GROUP_MATERI_VISCOSITY, element_group, new_unknowns, 
    &visc, ldum, GET_IF_EXISTS );
  db( GROUP_MATERI_STOKES, element_group, &stokes, ddum, ldum, 
    VERSION_NORMAL, GET_IF_EXISTS );
  if ( materi_density ) {
    dens = new_unknowns[dens_indx];
    if ( dens<0. ) dens = 0.;
  }
  else {
    if ( type==-CONDIF )
      get_group_data( GROUP_CONDIF_DENSITY, element_group, new_unknowns, 
        &dens, ldum, GET_IF_EXISTS );
    else if ( type==-MATERI )
      get_group_data( GROUP_MATERI_DENSITY, element_group, new_unknowns, 
        &dens, ldum, GET_IF_EXISTS );
  }

  for ( ipuknwn=0; ipuknwn<npuknwn; ipuknwn++ ) {
    iuknwn = ipuknwn * nder;

    inertia = 0.;
    unknown_belongs_to_type = 0;
    if      ( type==-CONDIF ) {
      if      ( dof_type[iuknwn]==-CONDIF_TEMPERATURE ) {
        unknown_belongs_to_type = 1;
        inertia = dens * condif_capacity;
      }
    }
    else if ( type==-GROUNDFLOW ) {
      if      ( dof_type[iuknwn]==-GROUNDFLOW_VELOCITY ) {
        unknown_belongs_to_type = 1;
        inertia = 1.;
      }
      else if ( dof_type[iuknwn]==-GROUNDFLOW_PRESSURE ) {
        unknown_belongs_to_type = 1;
        inertia = dens * co;
      }
    }
    else if ( type==-MATERI ) {
      if      ( dof_type[iuknwn]==-MATERI_DENSITY ) {
        unknown_belongs_to_type = 1;
        inertia = 1.;
      }
      else if ( dof_type[iuknwn]==-MATERI_HISTORY_VARIABLES ) {
        unknown_belongs_to_type = 1;
        inertia = 1.;
      }
      else if ( dof_type[iuknwn]==-MATERI_PLASTI_KAPPA ) {
        unknown_belongs_to_type = 1;
        inertia = 1.;
      }
      else if ( dof_type[iuknwn]==-MATERI_PLASTI_RHO ) {
        unknown_belongs_to_type = 1;
        inertia = 1.;
      }
      else if ( dof_type[iuknwn]==-MATERI_MAXWELL_STRESS ) {
        unknown_belongs_to_type = 1;
        inertia = 1.;
      }
      else if ( dof_type[iuknwn]==-MATERI_STRAIN_ELASTI ) {
        unknown_belongs_to_type = 1;
        inertia = 1.;
      }
      else if ( dof_type[iuknwn]==-MATERI_STRAIN_PLASTI ) {
        unknown_belongs_to_type = 1;
        inertia = 1.;
      }
      else if ( dof_type[iuknwn]==-MATERI_STRAIN_TOTAL ) {
        unknown_belongs_to_type = 1;
        inertia = 1.;
      }
      else if ( dof_type[iuknwn]==-MATERI_STRESS ) {
        unknown_belongs_to_type = 1;
        inertia = 1.;
      }
      else if ( dof_type[iuknwn]==-MATERI_DAMAGE ) {
        unknown_belongs_to_type = 1;
        inertia = 1.;
      }
      else if ( dof_type[iuknwn]==-MATERI_VELOCITY ) {
        unknown_belongs_to_type = 1;
        inertia = dens;
      }
      else if ( dof_type[iuknwn]==-MATERI_VOID_FRACTION ) {
        unknown_belongs_to_type = 1;
        inertia = 1.;
      }
    }
    else if ( type==-WAVE ) {
      if ( dof_type[iuknwn]==-WAVE_FSCALAR ) {
        unknown_belongs_to_type = 1;
        inertia = 1.;
      }
    }

    if ( unknown_belongs_to_type ) {

      for ( inol=0; inol<nnol; inol++ ) {

        indx = inol*npuknwn + ipuknwn;
        inod = nodes[inol];
        if ( db_active_index( NODE_LOCATE, inod, VERSION_NORMAL ) )
          db( NODE_LOCATE, inod, node_locate, ddum, ndim, 
            VERSION_NORMAL, GET_AND_CHECK );
        else {
          if ( materi_velocity ) array_set( node_locate, -YES, ndim );
          else array_set( node_locate, -NO, ndim );
        }
        if ( db_active_index( NODE_REMESH_VELOCITY, inod, VERSION_NORMAL ) )
          db( NODE_REMESH_VELOCITY, inod, idum, node_remesh_velocity, ldum, 
            VERSION_NORMAL, GET );
        else
          array_set( node_remesh_velocity, 0., ndim );

          // inertia (lumped)
        if ( skip_inertia[ipuknwn]!=-YES && skip_inertia[ipuknwn]!=-ALL ) {
          tmp = h[inol] * inertia *
            ( new_dof[inol*nuknwn+ipuknwn*nder] -
              old_dof[inol*nuknwn+ipuknwn*nder] ) / dtime;
          element_rhside[indx] -= volume * tmp;
          element_rhside_internal[indx] += volume * tmp;
          if ( dof_principal[iuknwn]<0 )
            element_rhside_static[indx] += volume * tmp;
          if ( residue && dof_principal[iuknwn]>=0 ) element_residue[indx] -= tmp;
          element_lhside[indx] += 
            volume * h[inol] * inertia / dtime;
          element_matrix[indx*nnol*npuknwn+indx] +=
            volume * h[inol] * inertia / dtime;
        }
          
        for ( jdim=0; jdim<ndim; jdim++ ) {

          diffusion = conv_part = diff_part = 0.;
          if      ( dof_type[iuknwn]==-CONDIF_TEMPERATURE && type==-CONDIF ) {
            conv_part = dens*condif_capacity;
            diff_part = condif_conductivity;
            diffusion = condif_conductivity;
          }
          else if ( dof_type[iuknwn]==-GROUNDFLOW_PRESSURE && type==-GROUNDFLOW ) {
            conv_part = co;
            diff_part = pe;
            diffusion = pe;
          }
          else if ( dof_type[iuknwn]==-MATERI_DENSITY && type==-MATERI )
            conv_part = 1.;
          else if ( dof_type[iuknwn]==-MATERI_HISTORY_VARIABLES && type==-MATERI )
            conv_part = 1.;
          else if ( dof_type[iuknwn]==-MATERI_MAXWELL_STRESS && type==-MATERI )
            conv_part = 1.;
          else if ( dof_type[iuknwn]==-MATERI_PLASTI_KAPPA && type==-MATERI )
            conv_part = 1.;
          else if ( dof_type[iuknwn]==-MATERI_PLASTI_RHO && type==-MATERI )
            conv_part = 1.;
          else if ( dof_type[iuknwn]==-MATERI_STRAIN_ELASTI && type==-MATERI )
            conv_part = 1.;
          else if ( dof_type[iuknwn]==-MATERI_STRAIN_PLASTI && type==-MATERI )
            conv_part = 1.;
          else if ( dof_type[iuknwn]==-MATERI_STRAIN_TOTAL && type==-MATERI )
            conv_part = 1.;
          else if ( dof_type[iuknwn]==-MATERI_STRESS && type==-MATERI )
            conv_part = 1.;
          else if ( dof_type[iuknwn]==-MATERI_DAMAGE && type==-MATERI )
            conv_part = 1.;
          else if ( dof_type[iuknwn]==-MATERI_VELOCITY && type==-MATERI ) {
            conv_part = dens;
            diff_part = visc;
          }
          else if ( dof_type[iuknwn]==-MATERI_VOID_FRACTION && type==-MATERI )
            conv_part = 1.;

            // convection with respect to mesh
          if ( skip_convective_part[ipuknwn]==-YES ||
               ( dof_type[iuknwn]==-MATERI_VELOCITY && stokes==-YES ) ||
               materi_displacement || node_locate[jdim]==-YES )
            vel = 0.;
          else
            vel = cons_var_vel[ipuknwn*nnol*ndim+inol*ndim+jdim] + node_remesh_velocity[jdim];
          if ( scalar_dabs(vel)>0. ) {
            if ( (name!=-TRIA3 && name!=-TET4) || 
                 scalar_dabs(new_d[jdim*nnol+inol])>EPS_D ) {
              tmp = h[inol] * grad_cons_var_flow[ipuknwn*ndim*ndim+jdim*ndim+jdim];
              element_rhside[indx] -= volume * tmp;
              element_rhside_internal[indx] += volume * tmp;
              element_rhside_static[indx] += volume * tmp;
              if ( residue && dof_principal[iuknwn]>=0 ) element_residue[indx] -= tmp;
              indxi = inol*npuknwn+ipuknwn;
              for ( jnol=0; jnol<nnol; jnol++ ) {
                indxj = jnol*npuknwn+ipuknwn;
                element_matrix[indxi*nnol*npuknwn+indxj] += 
                  volume * h[inol] * vel * conv_part * new_d[jdim*nnol+jnol];
              }
            }
          }

            // artificial diffusion and physical diffusion
          if ( scalar_dabs(new_d[jdim*nnol+inol])>EPS_D ) {
            if ( skip_spatial_stabilization[ipuknwn]==-YES )
              artificial_diffusion = 0.;
            else {
              if ( scalar_dabs(conv_part*h[inol]*vel)<EPS_PECLET )
                peclet_factor = 0.;
              else if ( 10.*scalar_dabs(diff_part*new_d[jdim*nnol+inol])<=
                scalar_dabs(conv_part*h[inol]*vel) ) peclet_factor = 1.;
              else {
                peclet= scalar_dabs(conv_part*h[inol]*vel/
                  (diff_part*new_d[jdim*nnol+inol]));
                peclet_factor = 
                  ((cosh(scalar_dabs(peclet)))/(sinh(scalar_dabs(peclet)))-
                  1/(scalar_dabs(peclet)));
              }
              artificial_diffusion = peclet_factor *
                scalar_dabs(h[inol]*vel*conv_part/new_d[jdim*nnol+inol]+diff_part);
            }

              // diffusive terms (with green partial integration)
            D = diffusion + artificial_diffusion;
            tmp = new_d[jdim*nnol+inol] * D *
              new_grad_new_unknowns[jdim*nuknwn+iuknwn];
            element_rhside[indx] -= volume * tmp;
            element_rhside_internal[indx] += volume * tmp;
            element_rhside_static[indx] += volume * tmp;
            if ( residue && dof_principal[iuknwn]>=0 ) element_residue[indx] += h[inol] *
              diffusion * new_grad_new_unknowns[jdim*nuknwn+iuknwn+jdim+1];
              // the next line is because of the explicit scheme
            fac = ((double)nnol)/2.;
            element_lhside[indx] += 
              fac * volume * new_d[jdim*nnol+inol] * D * new_d[jdim*nnol+inol];
            indxi = inol*npuknwn+ipuknwn;
            for ( jnol=0; jnol<nnol; jnol++ ) {
              indxj = jnol*npuknwn+ipuknwn;
              element_matrix[indxi*nnol*npuknwn+indxj] += 
                volume * new_d[jdim*nnol+inol] * D * new_d[jdim*nnol+jnol];
            }           
          }
        }
      }
    }
  }

  if ( swit ) {
    pri( "element_rhside", element_rhside, nnol, npuknwn );
    pri( "element_rhside_internal", element_rhside_internal, nnol, npuknwn );
    pri( "element_rhside_static", element_rhside_static, nnol, npuknwn );
    pri( "element_residue", element_residue, nnol, npuknwn );
    pri( "element_lhside", element_lhside, nnol, npuknwn );
  }

  if ( swit ) pri( "Out function GENERAL" );

}
