/*
    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 element_edge 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 MTYPES 3
#define MSIDE 12

  // things with area integral, e.g. convection, radiation, element_edge force

void area( long int name, long int nnol, long int nodes[], 
  double new_coord[], double new_dof[], double element_lhside[], 
  double element_matrix[], double element_rhside[],
  double element_rhside_internal[], double element_rhside_static[] )

{
  long int inol=0, jnol=0, knol=0, inol_side=0, max=0, ind=0, nnod=0, ipuknwn=0,
    iside=0, nside=0, ok=0, itype=0, use_geom=0, swit=0, inod=0,
    axisymmetric=-NO, any_area_integral=0,
    length=0, indx=0, nnol_side=0, ldum=0, idum[1], 
    sides[MSIDE][5], type[MTYPES], type_area[MTYPES], 
    geometry_entity[DATA_ITEM_SIZE], *area=NULL;
  double alpha=0., temp=0., env_temp=0., ar=0., heat_flux=0.,
    a=0., b=0., c=0., radius=0., heat_flux_stiffness=0., load=0., rdum=0., 
    ddum[MDIM], iso[MNOL], weight[MNOL], values[DATA_ITEM_SIZE], vec[MDIM], 
    *force_element_edge_time=NULL;

  type[0] = CONDIF_RADIATION;
  type[1] = CONDIF_CONVECTION;
  type[2] = FORCE_ELEMENT_EDGE;
  type_area[0] = CONDIF_RADIATION_GEOMETRY;
  type_area[1] = CONDIF_CONVECTION_GEOMETRY;
  type_area[2] = FORCE_ELEMENT_EDGE_GEOMETRY;

  for ( itype=0; itype<MTYPES; itype++ ) {
    if ( db_max_index( type[itype], max, VERSION_NORMAL, GET ) >=0 ) 
      any_area_integral = 1;
  }

  if ( any_area_integral ) {

    swit = set_swit(-1,-1,"area");
    if ( swit ) pri( "In routine AREA" );

    if      ( name==-TRIA3 ) {
      nside = 3;
      nnol_side = 2;
      sides[0][0] = 0;
      sides[0][1] = 1;
      sides[1][0] = 1;
      sides[1][1] = 2;
      sides[2][0] = 2;
      sides[2][1] = 0;
    }
    else if ( name==-QUAD4 ) {
      nside = 4;
      nnol_side = 2;
      sides[0][0] = 0;
      sides[0][1] = 1;
      sides[1][0] = 1;
      sides[1][1] = 3;
      sides[2][0] = 3;
      sides[2][1] = 2;
      sides[3][0] = 2;
      sides[3][1] = 0;
    }
    else if ( name==-QUAD9 ) {
      nside = 4;
      nnol_side = 3;
      sides[0][0] = 0;
      sides[0][1] = 1;
      sides[0][2] = 2;
      sides[1][0] = 2;
      sides[1][1] = 5;
      sides[1][2] = 8;
      sides[2][0] = 8;
      sides[2][1] = 7;
      sides[2][2] = 6;
      sides[3][0] = 6;
      sides[3][1] = 3;
      sides[3][2] = 0;
    }
    else if ( name==-QUAD16 ) {
      nside = 4;
      nnol_side = 4;
      sides[0][0] = 0;
      sides[0][1] = 1;
      sides[0][2] = 2;
      sides[0][3] = 3;
      sides[1][0] = 3;
      sides[1][1] = 7;
      sides[1][2] = 11;
      sides[1][3] = 15;
      sides[2][0] = 15;
      sides[2][1] = 14;
      sides[2][2] = 13;
      sides[2][3] = 12;
      sides[3][0] = 12;
      sides[3][1] = 8;
      sides[3][2] = 4;
      sides[3][3] = 0;
    }
    else if ( name==-QUAD25 ) {
      nside = 4;
      nnol_side = 5;
      sides[0][0] = 0;
      sides[0][1] = 1;
      sides[0][2] = 2;
      sides[0][3] = 3;
      sides[0][4] = 4;
      sides[1][0] = 4;
      sides[1][1] = 9;
      sides[1][2] = 14;
      sides[1][3] = 19;
      sides[1][4] = 24;
      sides[2][0] = 24;
      sides[2][1] = 23;
      sides[2][2] = 22;
      sides[2][3] = 21;
      sides[2][4] = 20;
      sides[3][0] = 20;
      sides[3][1] = 15;
      sides[3][2] = 10;
      sides[3][3] = 5;
      sides[3][4] = 0;
    }
    else if ( name==-TET4 ) {
      nside = 4;
      nnol_side = 3;
      sides[0][0] = 0;
      sides[0][1] = 1;
      sides[0][2] = 2;
      sides[1][0] = 0;
      sides[1][1] = 1;
      sides[1][2] = 3;
      sides[2][0] = 0;
      sides[2][1] = 2;
      sides[2][2] = 3;
      sides[3][0] = 1;
      sides[3][1] = 2;
      sides[3][2] = 3;
    }
    else if ( name==-HEX8 ) {
      nside = 12;
      nnol_side = 3;
      sides[0][0] = 0;
      sides[0][1] = 1;
      sides[0][2] = 3;
      sides[1][0] = 0;
      sides[1][1] = 3;
      sides[1][2] = 2;
      sides[2][0] = 4;
      sides[2][1] = 5;
      sides[2][2] = 7;
      sides[3][0] = 4;
      sides[3][1] = 7;
      sides[3][2] = 6;
      sides[4][0] = 0;
      sides[4][1] = 1;
      sides[4][2] = 4;
      sides[5][0] = 1;
      sides[5][1] = 4;
      sides[5][2] = 5;
      sides[6][0] = 3;
      sides[6][1] = 2;
      sides[6][2] = 7;
      sides[7][0] = 2;
      sides[7][1] = 7;
      sides[7][2] = 6;
      sides[8][0] = 1;
      sides[8][1] = 3;
      sides[8][2] = 7;
      sides[9][0] = 1;
      sides[9][1] = 7;
      sides[9][2] = 5;
      sides[10][0] = 0;
      sides[10][1] = 2;
      sides[10][2] = 6;
      sides[11][0] = 0;
      sides[11][1] = 6;
      sides[11][2] = 4;
    }
    else 
      nside = 0;

    db( OPTIONS_AXISYMMETRIC, 0, &axisymmetric, ddum, ldum, 
      VERSION_NORMAL, GET_IF_EXISTS );

    for ( itype=0; itype<MTYPES; itype++ ) {
      if ( swit ) pri( "itype", itype );

      db_max_index( type[itype], max, VERSION_NORMAL, GET );
      for ( ind=0; ind<=max; ind++ ) {
        if ( db_active_index( type[itype], ind, VERSION_NORMAL ) ) {
          if ( nside==0 ) {
            cout << "\nError: " << db_name( name ) << " not available for "; 
            cout << db_name( type[itype] ) << ".\n";
            exit(TN_EXIT_STATUS);
          }

          use_geom = nnod = 0;
          area = db_int( type_area[itype], ind, VERSION_NORMAL );
          if ( area[0]>0 ) {
            nnod = db_len( type_area[itype], ind, VERSION_NORMAL );
            renumbering_check( type_area[itype] );
          }
          else {
            db( type_area[itype], ind, geometry_entity, ddum, 
              ldum, VERSION_NORMAL, GET );
            use_geom = db_data_class(geometry_entity[0])==GEOMETRY;
            if ( !use_geom ) db_error( type[itype], ind );
          }
          if ( type[itype]==FORCE_ELEMENT_EDGE ) {
            if ( db_active_index( FORCE_ELEMENT_EDGE_TIME, ind, VERSION_NORMAL ) ) {
              force_element_edge_time = db_dbl( FORCE_ELEMENT_EDGE_TIME, 
                ind, VERSION_NORMAL );
              length = db_len( FORCE_ELEMENT_EDGE_TIME, ind, VERSION_NORMAL );
              if ( length<4 ) db_error( FORCE_ELEMENT_EDGE_TIME, ind );
              force_time( force_element_edge_time, length, load );
            }
            else
              load = 1.;
          }

          for ( iside=0; iside<nside; iside++ ) {
            ok = 1;
            for ( inol_side=0; inol_side<nnol_side && ok; inol_side++ ) {
              inol = sides[iside][inol_side]; inod = nodes[inol];
              if      ( use_geom )
                geometry( inod, ddum, geometry_entity, ok, rdum, ddum, rdum,
                  ddum, NODE_START_REFINED, PROJECT_EXACT, VERSION_NORMAL );
              else {
                assert( nnod>0 );
                ok = array_member( area, inod, nnod, ldum );
              }
            }
            if ( ok ) {
              if ( ndim==2 ) {
                array_subtract( &new_coord[sides[iside][0]*ndim], 
                    &new_coord[sides[iside][nnol_side-1]*ndim], vec, ndim );
                ar = array_size( vec, ndim );
                if ( axisymmetric==-YES ) {
                  radius = new_coord[inol*ndim];
                  ar *= 2. * PIRAD * radius;
                }
                integration_lobatto( nnol_side, iso, weight );
              }
              else {
                assert( ndim==3 );
                inol = sides[iside][0];
                jnol = sides[iside][1];
                knol = sides[iside][2];
                array_subtract( &new_coord[inol*ndim], &new_coord[jnol*ndim], vec, ndim );
                a = array_size( vec, ndim );
                array_subtract( &new_coord[inol*ndim], &new_coord[knol*ndim], vec, ndim );
                b = array_size( vec, ndim );
                array_subtract( &new_coord[jnol*ndim], &new_coord[knol*ndim], vec, ndim );
                c = array_size( vec, ndim );
                ar = sqrt( (a+b+c)*(a+b-c)*(a-b+c)*(-a+b+c)/16 );
                weight[0] = c*c/(a*a+b*b+c*c);
                weight[1] = b*b/(a*a+b*b+c*c);
                weight[2] = a*a/(a*a+b*b+c*c);
              }
              if ( swit ) {
                pri( "ar", ar );
                pri( "weight", weight, nnol_side );
              }
              for ( inol_side=0; inol_side<nnol_side; inol_side++ ) {
                inol = sides[iside][inol_side]; 
                if ( type[itype]==CONDIF_RADIATION || 
                     type[itype]==CONDIF_CONVECTION ) {
                  if ( type[itype]==CONDIF_RADIATION )
                    db( CONDIF_RADIATION, ind, idum, values, 
                      ldum, VERSION_NORMAL, GET );
                  else {
                    assert( type[itype]==CONDIF_CONVECTION );
                    db( CONDIF_CONVECTION, ind, idum, values, 
                      ldum, VERSION_NORMAL, GET );
                  }
                  alpha = values[0]; env_temp = values[1];
                  if ( swit ) {
                    pri( "alpha", alpha );
                    pri( "env_temp", env_temp );
                  }
                  temp = new_dof[inol*nuknwn+temp_indx];
                  if ( type[itype]==CONDIF_RADIATION ) {
                    heat_flux = alpha * weight[inol_side] * ar *
                      (pow(env_temp,4)-pow(temp,4));
                    heat_flux_stiffness = alpha * weight[inol_side] * ar * 4.*pow(temp,3);
                  }
                  else {
                    assert( type[itype]==CONDIF_CONVECTION );
                    heat_flux = alpha * weight[inol_side] * ar * (env_temp-temp);
                    heat_flux_stiffness = alpha * weight[inol_side] * ar * scalar_dabs(temp);
                  }
                  if ( swit ) {
                    pri( "inol", inol );
                    pri( "temp", temp );
                    pri( "heat_flux_stiffness", heat_flux_stiffness );
                    pri( "heat_flux", heat_flux );
                  }
                  indx = inol*npuknwn + temp_indx/nder;
                  element_lhside[indx] += heat_flux_stiffness;
                  element_matrix[indx*nnol*npuknwn+indx] += heat_flux_stiffness;
                  element_rhside[inol*npuknwn+temp_indx/nder] += 
                    heat_flux;
                  element_rhside_internal[inol*npuknwn+temp_indx/nder] -= 
                    heat_flux;
                  element_rhside_static[inol*npuknwn+temp_indx/nder] -= 
                    heat_flux;
                }
                else {
                  assert( type[itype]==FORCE_ELEMENT_EDGE );
                  db( FORCE_ELEMENT_EDGE, ind, idum, values, 
                    ldum, VERSION_NORMAL, GET );
                  for ( ipuknwn=0; ipuknwn<npuknwn; ipuknwn++ ) {
                    element_rhside[inol*npuknwn+ipuknwn] += 
                      load * weight[inol_side] * ar * values[ipuknwn];
                    element_rhside_internal[inol*npuknwn+ipuknwn] -= 
                      load * weight[inol_side] * ar * values[ipuknwn];
                    element_rhside_static[inol*npuknwn+ipuknwn] -= 
                      load * weight[inol_side] * ar * values[ipuknwn];
                  }
                }
              }
            }
          }
        }
      }
    }

    if ( swit ) pri( "Out routine AREA" );
  }

}

void area_node_boundary( )

{
  long int inod=0, max_node=0, found=0, iarea=0, max_area=0,
    node_boundary=-YES, length=0, ldum=0, area_node_boundary[2];
  double rdum=0., ddum[MDIM];

  db_max_index( AREA_NODE_BOUNDARY, max_area, VERSION_NORMAL, GET );
  for ( iarea=0; iarea<=max_area; iarea++ ) {
    if ( db_active_index( AREA_NODE_BOUNDARY, iarea, VERSION_NORMAL ) ) {
      db( AREA_NODE_BOUNDARY, iarea, area_node_boundary, ddum, ldum, 
        VERSION_NORMAL, GET );
      db_max_index( NODE, max_node, VERSION_NORMAL, GET );
      for ( inod=0; inod<=max_node; inod++ ) {
        if ( db_active_index( NODE, inod, VERSION_NORMAL ) ) {
          geometry( inod, ddum, area_node_boundary, found, rdum, ddum, rdum,
            ddum, NODE_START_REFINED, PROJECT_EXACT, VERSION_NORMAL );
          if ( found ) {
            length = 1;
            db( NODE_BOUNDARY, inod, &node_boundary, ddum, length, VERSION_NORMAL, PUT );
          }
        }
      }
    }
  }

}
