/*
    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_DOF_TYINGS 1.e-6

void parallel_new_dof_before( void )

{
  long int max_node=0, ipuknwn=0, iuknwn=0, 
    idim=0, iloop=0, nloop=0, inod=0, ldum=0, 
    ithread=0, length=0, idum[1], *next_of_loop=NULL;
  double dtime=0., node_mass=0., node_damping=0., tmp=0., 
    factor=1., gravity[MDIM], *gravity_time=0,
    *node_dof=NULL, *node_dof_new=NULL, 
    *node_rhside=NULL, *node_rhside_internal=NULL, *node_lhside=NULL;

  if ( materi_velocity ) {
    db( DTIME, 0, idum, &dtime, ldum, VERSION_NEW, GET );
    if ( db_active_index( FORCE_GRAVITY, 0, VERSION_NORMAL ) ) {
      db( FORCE_GRAVITY, 0, idum, gravity, ldum, VERSION_NORMAL, GET );
      if ( db_active_index( FORCE_GRAVITY_TIME, 0, VERSION_NORMAL ) ) {
        length = db_len( FORCE_GRAVITY_TIME, 0, VERSION_NORMAL );
        if ( length<4 ) db_error( FORCE_GRAVITY_TIME, -1 );
        gravity_time = db_dbl( FORCE_GRAVITY_TIME, 0, VERSION_NORMAL );
        force_time( gravity_time, length, factor );
        for ( idim=0; idim<ndim; idim++ ) gravity[idim] *= factor;
      }
    }
    else
      array_set( gravity, 0., ndim );
    db_max_index( NODE, max_node, VERSION_NORMAL, GET );
    if ( max_node>=0 ) {
      next_of_loop = get_new_int(1+max_node);
      parallel_sys_next_of_loop( next_of_loop, max_node, nloop, ithread );
      for ( iloop=0; iloop<nloop; iloop++ ) {
        inod = next_of_loop[iloop];
        if ( inod>max_node )
          break;
        else if ( db_active_index( NODE, inod, VERSION_NORMAL ) ) {
          node_dof = db_dbl( NODE_DOF, inod, VERSION_NORMAL );
          node_dof_new = db_dbl( NODE_DOF, inod, VERSION_NEW );
          node_lhside = db_dbl( NODE_LHSIDE, inod, VERSION_NORMAL );
          node_rhside = db_dbl( NODE_RHSIDE, inod, VERSION_NORMAL );
          node_rhside_internal = db_dbl( NODE_RHSIDE_INTERNAL, 
            inod, VERSION_NORMAL );
          db( NODE_DAMPING, inod, idum, &node_damping, ldum, 
            VERSION_NORMAL, GET_IF_EXISTS );
          db( NODE_MASS, inod, idum, &node_mass, ldum, 
            VERSION_NORMAL, GET_IF_EXISTS );
          for ( idim=0; idim<ndim; idim++ ) {
            ipuknwn = vel_indx/nder + idim;
            iuknwn = vel_indx + idim * nder;
            node_lhside[ipuknwn] += node_damping + node_mass / dtime;
            tmp = node_damping * node_dof_new[iuknwn] +
              node_mass * ( node_dof_new[iuknwn] - node_dof[iuknwn] ) / 
              dtime + node_mass * gravity[idim];
            node_rhside[ipuknwn] -= tmp;
            node_rhside_internal[ipuknwn] += tmp;
          }
        }
      }
      delete[] next_of_loop;
    }
  }

}

void parallel_new_dof_diagonal( void )

{
  long int inod=0, max_node=0, ipuknwn=0, iuknwn=0, iloop=0, nloop=0, 
    ithread=0, icontrol=0, idim=0, ind=0, control_solver=0, ldum=0, 
    idum[1], dof_principal[MUKNWN], *next_of_loop=NULL, *node_bounded=NULL;
  double dtime=0., ddum[1], *node_dof=NULL, *node_dof_new=NULL, 
    *node_rhside=NULL, *node_lhside=NULL;

  db( ICONTROL, 0, &icontrol, ddum, ldum, VERSION_NORMAL, GET );
  db( DTIME, 0, idum, &dtime, ldum, VERSION_NEW, GET );
  db( CONTROL_SOLVER, icontrol, &control_solver, ddum, ldum, 
    VERSION_NORMAL, GET );
  db( DOF_PRINCIPAL, 0, dof_principal, ddum, ldum, VERSION_NORMAL, GET );
  db_max_index( NODE, max_node, VERSION_NORMAL, GET );
  if ( max_node>=0 ) {
    next_of_loop = get_new_int(1+max_node);
    parallel_sys_next_of_loop( next_of_loop, max_node, nloop, ithread );
    for ( iloop=0; iloop<nloop; iloop++ ) {
      inod = next_of_loop[iloop];
      if ( inod>max_node )
        break;
      else if ( db_active_index( NODE, inod, VERSION_NORMAL ) ) {
        node_dof = db_dbl( NODE_DOF, inod, VERSION_NORMAL );
        node_dof_new = db_dbl( NODE_DOF, inod, VERSION_NEW );
        node_bounded = db_int( NODE_BOUNDED, inod, VERSION_NORMAL );
        node_lhside = db_dbl( NODE_LHSIDE, inod, VERSION_NORMAL );
        node_rhside = db_dbl( NODE_RHSIDE, inod, VERSION_NORMAL );
        for ( ipuknwn=0; ipuknwn<npuknwn; ipuknwn++ ) {
          if ( !node_bounded[ipuknwn] && node_lhside[ipuknwn]!=0. ) {
            iuknwn = ipuknwn*nder;
            if ( control_solver==-DIAGONAL || dof_principal[iuknwn]<0 )
              node_dof_new[iuknwn] += node_rhside[ipuknwn]/node_lhside[ipuknwn];
          }
        }
        if ( materi_damage ) {
          iuknwn = dam_indx;
          if ( node_dof_new[iuknwn]<0. ) node_dof_new[iuknwn] = 0.;
          if ( node_dof_new[iuknwn]>1. ) node_dof_new[iuknwn] = 1.;
        }
        if ( materi_density ) {
          iuknwn = dens_indx;
          if ( node_dof_new[iuknwn]<0. ) node_dof_new[iuknwn] = 0.;
        }
        if ( materi_displacement ) {
          for ( idim=0; idim<ndim; idim++ ) {
            iuknwn = dis_indx + idim * nder;
            ind = vel_indx+idim*nder;   
            node_dof_new[iuknwn] = node_dof[iuknwn] + node_dof_new[ind] * dtime;   
          }
        }
        if ( materi_plasti_kappa ) {
          iuknwn = kap_indx;
          if ( node_dof_new[iuknwn]<0. ) node_dof_new[iuknwn] = 0.;
        }
        if ( materi_void_fraction ) {
          iuknwn = void_indx;
          if ( node_dof_new[iuknwn]<0. ) node_dof_new[iuknwn] = 0.;
          if ( node_dof_new[iuknwn]>1. ) node_dof_new[iuknwn] = 1.;
        }
        if ( wave_scalar ) {
          iuknwn = scal_indx;
          node_dof_new[iuknwn] = node_dof[iuknwn] +
            node_dof_new[fscal_indx] * dtime;   
        }
        if ( derivatives ) {
          for ( ipuknwn=0; ipuknwn<npuknwn; ipuknwn++ ) {
            if ( !node_bounded[ipuknwn] ) {
              iuknwn = ipuknwn*nder;
              node_dof_new[iuknwn+nder-1] = 
                ( node_dof_new[iuknwn] - node_dof[iuknwn] ) / dtime;
            }
          }
        }
      }
    }
    delete[] next_of_loop;
  }

}

void dof_tyings( void )

{
  long int i=0, j=0, n=0, idof_tyings=0, ldof_tyings=0, max_dof_tyings=0, 
    inod=0, jnod=0, max_node=0, in_geometry=0,
    length=0, iuknwn=0, ldum=0, idum[1], node_tyings[1], 
    node_tyings_unknowns[MUKNWN], geometry_entity[2], 
    dof_label[MUKNWN], *dof_tyings=NULL, *in_geometry_list=0;
  double rdum=0., node_tyings_factors[1], ddum[MDIM],
    *coordi=NULL, *coordj=NULL;

  db_max_index( NODE, max_node, VERSION_NORMAL, GET );
  db( DOF_LABEL, 0, dof_label, ddum, ldum, VERSION_NORMAL, GET_IF_EXISTS );
  db_max_index( DOF_TYINGS, max_dof_tyings, VERSION_NORMAL, GET );
  if ( max_dof_tyings>=0 ) {
    in_geometry_list = get_new_int(1+max_node );
    for ( idof_tyings=0; idof_tyings<=max_dof_tyings; idof_tyings++ ) {
      if ( db_active_index( DOF_TYINGS, idof_tyings, VERSION_NORMAL ) ) {
        n = 0;
        ldof_tyings = db_len( DOF_TYINGS, idof_tyings, VERSION_NORMAL );
        dof_tyings = db_int( DOF_TYINGS, idof_tyings, VERSION_NORMAL );
        if ( ldof_tyings<3 || ldof_tyings>2+nuknwn ) 
          db_error( DOF_TYINGS, idof_tyings );
        array_move( &dof_tyings[0], geometry_entity, 2 );
        array_set( node_tyings_unknowns, -NO, nuknwn );
        for ( i=0; i<ldof_tyings-2; i++ ) {
          array_member( dof_label, dof_tyings[2+i], nuknwn, iuknwn );
          if ( iuknwn>=0 )
            node_tyings_unknowns[iuknwn] = -YES;
          else
            db_error( DOF_TYINGS, idof_tyings );
        }
        for ( inod=0; inod<max_node; inod++ ) {
          if ( db_active_index( NODE, inod, VERSION_NORMAL ) ) {
            geometry( inod, ddum, geometry_entity, in_geometry, rdum, ddum, rdum,
              ddum, NODE_START_REFINED, PROJECT_EXACT, VERSION_NORMAL );
            if ( in_geometry ) {
              in_geometry_list[n] = inod;     
              coordi = db_dbl( NODE, inod, VERSION_NORMAL );
              for ( j=0; j<n; j++ ) {             
                jnod = in_geometry_list[j];
                coordj = db_dbl( NODE, jnod, VERSION_NORMAL );
                if ( array_distance( coordi, coordj, ddum, ndim ) < EPS_DOF_TYINGS ) {
                  length = 1;
                  node_tyings[0] = jnod;
                  node_tyings_factors[0] = 1.;
                  db( NODE_TYINGS, inod, node_tyings, ddum, length, 
                  VERSION_NORMAL, PUT );
                  db( NODE_TYINGS_FACTORS, inod, idum, node_tyings_factors, length, 
                    VERSION_NORMAL, PUT );
                  length = ldof_tyings-2;
                  db( NODE_TYINGS_UNKNOWNS, inod, node_tyings_unknowns, 
                    ddum, nuknwn, VERSION_NORMAL, PUT );
                }
              }
              n++;
            }
          }
        }
      }
    }
    delete[] in_geometry_list;
  }

}
