/*
    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 NTYPE 3
#define EPS_PRI 1.e-12

void calculate( void )

{
  long int idim=0, jdim=0, indx=0, length=0, length_result=0,
    icalcul=0, nmat=0, unknown=0, max_type_post=0, ipost=0, itype=0, 
    ncalcul=0, max_node=0, swit=0, idum[1], 
    calcul[DATA_ITEM_SIZE], type_post_dof[NTYPE], 
    type_post_dof_calcul[NTYPE], post_calcul_scal_vec_mat[DATA_ITEM_SIZE],
    post_calcul_unknown_operat[DATA_ITEM_SIZE];
  double ddum[1], unknown_values[MDIM*MDIM], dof_calcul[DATA_ITEM_SIZE], 
    result[DATA_ITEM_SIZE], *post_dof=NULL;
  char str[MCHAR], outname[MCHAR], unknown_name[MCHAR];

  if ( db_active_index( POST_CALCUL, 0, VERSION_NORMAL ) ) {

    swit = set_swit(-1,-1,"calculate");
    if ( swit ) pri( "In routine CALCULATE" );

    db_delete( NODE_DOF_CALCUL, VERSION_NORMAL );
    db_delete( POST_POINT_DOF_CALCUL, VERSION_NORMAL );
    db_delete( POST_LINE_DOF_CALCUL, VERSION_NORMAL );
    db_delete( POST_QUADRILATERAL_DOF_CALCUL, VERSION_NORMAL );

    type_post_dof[0] = POST_LINE_DOF;
    type_post_dof_calcul[0] = POST_LINE_DOF_CALCUL;
    type_post_dof[1] = POST_POINT_DOF;
    type_post_dof_calcul[1] = POST_POINT_DOF_CALCUL;
    type_post_dof[2] = POST_QUADRILATERAL_DOF;
    type_post_dof_calcul[2] = POST_QUADRILATERAL_DOF_CALCUL;

    db( POST_CALCUL, 0, calcul, ddum, length, VERSION_NORMAL, GET );
    if ( (length%2)!=0 ) db_error( POST_CALCUL, 0 );
    nmat = length / 2; 
    for ( icalcul=0; icalcul<nmat; icalcul++ ) {
      unknown = calcul[icalcul*2+0];
      calcul_matrix = 0;
      if       ( unknown==-MATERI_STRESS ) {
        calcul_matrix = 1;
        calcul_mat_indx = stres_indx;
        strcpy( unknown_name, "sig" );
      }
      else if ( unknown==-MATERI_STRAIN_ELASTI ) {
        calcul_matrix = 1;
        calcul_mat_indx = epe_indx;
        strcpy( unknown_name, "epe" );
      }
      else if ( unknown==-MATERI_STRAIN_PLASTI ) {
        calcul_matrix = 1;
        calcul_mat_indx = epp_indx;
        strcpy( unknown_name, "epp" );
      }
      else if ( unknown==-MATERI_STRAIN_TOTAL ) {
        calcul_matrix = 1;
        calcul_mat_indx = ept_indx;
        strcpy( unknown_name, "ept" );
      }
      else if ( unknown==-MATERI_VELOCITY ) {
        calcul_vec_indx = vel_indx;
        strcpy( unknown_name, "vel" );
      }
      else if ( unknown==-MATERI_DISPLACEMENT ) {
        calcul_vec_indx = dis_indx;
        strcpy( unknown_name, "dis" );
      }
      else
        db_error( POST_CALCUL, 0 );
      calcul_operat = calcul[icalcul*2+1];

        // to prevent memory problems in parallel comp.
      db_max_index( NODE, max_node, VERSION_NORMAL, GET );
      db_allocate_class( -NODE, max_node, VERSION_NORMAL );
      parallel_sys_routine( &parallel_calcul_node );

      for ( itype=0; itype<NTYPE; itype++ ) {
        db_max_index( type_post_dof[itype], max_type_post, VERSION_NORMAL, GET );
        for ( ipost=0; ipost<=max_type_post; ipost++ ) {
          if ( db_active_index( type_post_dof[itype], ipost, VERSION_NORMAL ) ) {
            post_dof = db_dbl( type_post_dof[itype], ipost, VERSION_NORMAL );
            if ( calcul_matrix ) {
              for ( idim=0; idim<MDIM; idim++ ) {
                for ( jdim=0; jdim<MDIM; jdim++ ) {
                  indx = idim*MDIM + jdim;
                  unknown_values[indx] = 
                    post_dof[calcul_mat_indx+stress_indx(idim,jdim)*nder];
                }
              }
            }
            else {
              for ( idim=0; idim<ndim; idim++ ) {
                indx = idim;
                unknown_values[indx] = post_dof[calcul_vec_indx+idim*nder];
              }
            }
            calculate_operat( unknown_values, result, length_result );
            db( type_post_dof_calcul[itype], ipost, idum, dof_calcul, 
              length, VERSION_NORMAL, GET_IF_EXISTS );
            if ( length+length_result>DATA_ITEM_SIZE ) db_error( POST_CALCUL, 0 );
            array_move( result, &dof_calcul[length], length_result );
            length += length_result;
            db( type_post_dof_calcul[itype], ipost, idum, dof_calcul, 
              length, VERSION_NORMAL, PUT );
          }
        }
      }

      if      ( calcul_operat==-AVERAGE ) {
        strcpy( outname, "a" );
        strcat( outname, unknown_name );
        ncalcul++;
        strcpy( post_calcul_names[ncalcul-1], outname );
        post_calcul_scal_vec_mat[ncalcul-1] = -SCALAR;
        post_calcul_unknown_operat[(ncalcul-1)*2+0] = unknown;
        post_calcul_unknown_operat[(ncalcul-1)*2+1] = calcul_operat;
      }
      else if ( calcul_operat==-NEGATIVE ) {
        strcpy( outname, "n" );
        strcat( outname, unknown_name );
        ncalcul++;
        strcpy( post_calcul_names[ncalcul-1], outname );
        post_calcul_scal_vec_mat[ncalcul-1] = -SCALAR;
        post_calcul_unknown_operat[(ncalcul-1)*2+0] = unknown;
        post_calcul_unknown_operat[(ncalcul-1)*2+1] = calcul_operat;
      }
      else if ( calcul_operat==-POSITIVE ) {
        strcpy( outname, "p" );
        strcat( outname, unknown_name );
        ncalcul++;
        strcpy( post_calcul_names[ncalcul-1], outname );
        post_calcul_scal_vec_mat[ncalcul-1] = -SCALAR;
        post_calcul_unknown_operat[(ncalcul-1)*2+0] = unknown;
        post_calcul_unknown_operat[(ncalcul-1)*2+1] = calcul_operat;
      }
      else if ( calcul_operat==-PRIVAL ) {
        for ( idim=0; idim<MDIM; idim++ ) {
          strcpy( outname, "va" );
          long_to_a( idim, str );
          strcat( outname, str );
          strcat( outname, unknown_name );
          ncalcul++;
          strcpy( post_calcul_names[ncalcul-1], outname );
          post_calcul_scal_vec_mat[ncalcul-1] = -SCALAR;
          post_calcul_unknown_operat[(ncalcul-1)*2+0] = unknown;
          post_calcul_unknown_operat[(ncalcul-1)*2+1] = calcul_operat;
        }
      }
      else if ( calcul_operat==-PRIVEC ) {
        for ( idim=0; idim<MDIM; idim++ ) {
          strcpy( str, "ve" );
          long_to_a( idim, outname );
          strcat( str, outname );
          for ( jdim=0; jdim<MDIM; jdim++ ) {
            strcpy( outname, str );
            if      ( jdim==0 )
              strcat( outname, "x" );
            else if ( jdim==1 )
              strcat( outname, "y" );
            else {
              assert( jdim==2 );
              strcat( outname, "z" );
            }
            strcat( outname, unknown_name );
            ncalcul++;
            strcpy( post_calcul_names[ncalcul-1], outname );
            post_calcul_scal_vec_mat[ncalcul-1] = -VECTOR;
            post_calcul_unknown_operat[(ncalcul-1)*2+0] = unknown;
            post_calcul_unknown_operat[(ncalcul-1)*2+1] = calcul_operat;
          }
        }
      }
      else if ( calcul_operat==-SIZE ) {
        strcpy( outname, "s" );
        strcat( outname, unknown_name );
        ncalcul++;
        strcpy( post_calcul_names[ncalcul-1], outname );
        post_calcul_scal_vec_mat[ncalcul-1] = -SCALAR;
        post_calcul_unknown_operat[(ncalcul-1)*2+0] = unknown;
        post_calcul_unknown_operat[(ncalcul-1)*2+1] = calcul_operat;
      }
      else if ( calcul_operat==-SIZEDEV ) {
        strcpy( outname, "sd" );
        strcat( outname, unknown_name );
        ncalcul++;
        strcpy( post_calcul_names[ncalcul-1], outname );
        post_calcul_scal_vec_mat[ncalcul-1] = -SCALAR;
        post_calcul_unknown_operat[(ncalcul-1)*2+0] = unknown;
        post_calcul_unknown_operat[(ncalcul-1)*2+1] = calcul_operat;
      }
      else if ( unknown==-MATERI_STRESS && calcul_operat==-PHIMOB ) {
        strcpy( outname, "pm" );
        strcat( outname, unknown_name );
        ncalcul++;
        strcpy( post_calcul_names[ncalcul-1], outname );
        post_calcul_scal_vec_mat[ncalcul-1] = -SCALAR;
        post_calcul_unknown_operat[(ncalcul-1)*2+0] = unknown;
        post_calcul_unknown_operat[(ncalcul-1)*2+1] = calcul_operat;
      }
      else
        db_error( POST_CALCUL, 0 );
      assert( ncalcul<db_data_length(POST_CALCUL_SCAL_VEC_MAT)-1 );

    }
    if ( ncalcul>0 ) {
      length = ncalcul;
      db( POST_CALCUL_SCAL_VEC_MAT, 0, post_calcul_scal_vec_mat, 
        ddum, length, VERSION_NORMAL, PUT );
      length = 2*ncalcul;
      db( POST_CALCUL_UNKNOWN_OPERAT, 0, post_calcul_unknown_operat, 
        ddum, length, VERSION_NORMAL, PUT );
    }

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

}

void calculate_operat( double unknown_values[], 
  double result[], long int &length_result )

{

  long int idim=0, jdim=0, indx=0, n=0, idum[1];
  double average=0., phim=0., tmp=0., tmp1=0., tmp2=0.,
    prival[MDIM], privec[MDIM*MDIM], tmp_mat[MDIM*MDIM], 
    dev_mat[MDIM*MDIM], workval[MDIM], workvec[MDIM*MDIM];

  if ( calcul_matrix )
    average = ( unknown_values[0] + unknown_values[4] + 
      unknown_values[8] ) / 3.;
  else {
    for ( idim=0; idim<ndim; idim++ )
      average += unknown_values[idim] / ( (double) ndim );
  }

  if ( calcul_matrix ) {
    array_move( unknown_values, tmp_mat, MDIM*MDIM );
    matrix_jacobi( tmp_mat, MDIM, workval, workvec, idum );
    sort( workval, workvec );
    array_move( workval, prival, MDIM );
    array_move( workvec, privec, MDIM*MDIM );
  }
  
  if      ( calcul_operat==-AVERAGE ) {
    result[0] = average;
    length_result = 1;
  }
  else if ( calcul_operat==-NEGATIVE ) {
    if ( !calcul_matrix ) db_error( POST_CALCUL, 0 );
    result[0] = 0.;
    for ( idim=0; idim<MDIM; idim++ ) {
      if ( prival[idim]<0. ) {
        n++;
        result[0] += prival[idim];
      }
    }
    if ( n>0 ) result[0] = result[0] / n;
    length_result = 1;
  }
  else if ( calcul_operat==-POSITIVE ) {
    if ( !calcul_matrix ) db_error( POST_CALCUL, 0 );
    result[0] = 0.;
    for ( idim=0; idim<MDIM; idim++ ) {
      if ( prival[idim]>0. ) {
        n++;
        result[0] += prival[idim];
      }
    }
    if ( n>0 ) result[0] = result[0] / n;
    length_result = 1;
  }
  else if ( calcul_operat==-PRIVAL ) {
    if ( !calcul_matrix ) db_error( POST_CALCUL, 0 );
    array_move( prival, result, MDIM );
    length_result = MDIM;
  }
  else if ( calcul_operat==-PRIVEC ) {
    if ( !calcul_matrix ) db_error( POST_CALCUL, 0 );
    for ( idim=0; idim<MDIM; idim++ ) {
      indx = idim * MDIM;
      array_normalize( &privec[indx], MDIM );
      array_multiply( &privec[indx], &result[indx], prival[idim], MDIM );
    }
    length_result = MDIM * MDIM;
  }
  else if ( calcul_operat==-SIZE ) {
    if ( calcul_matrix )
      result[0] = sqrt( 0.5 * array_inproduct( unknown_values, 
        unknown_values, MDIM*MDIM ) );
    else
      result[0] = sqrt( 0.5 * array_inproduct( unknown_values, 
        unknown_values, ndim ) );
    length_result = 1;
  }
  else if ( calcul_operat==-SIZEDEV ) {
    if ( !calcul_matrix ) db_error( POST_CALCUL, 0 );
    for ( idim=0; idim<MDIM; idim++ ) {
      for ( jdim=0; jdim<MDIM; jdim++ ) {
        indx = idim*MDIM + jdim;
        dev_mat[indx] = unknown_values[indx];
        if ( idim==jdim ) dev_mat[indx] -= average;
      }
    }
    result[0] = sqrt( 0.5 * array_inproduct( dev_mat, 
      dev_mat, MDIM*MDIM ) );
    length_result = 1;
  }
  else if ( calcul_operat==-PHIMOB ) {
    matrix_eigenvalues( unknown_values, prival );
    phim = 0.;
    tmp1 = scalar_dabs(prival[0]-prival[1]);
    tmp2 = scalar_dabs(prival[0]+prival[1]);
    if ( tmp2>EPS_PRI ) {
      tmp = tmp1 / tmp2;
      if ( tmp<=1. ) {
        tmp = asin(tmp);
        if ( tmp>phim ) phim = tmp;
      }
    }
    tmp1 = scalar_dabs(prival[1]-prival[2]);
    tmp2 = scalar_dabs(prival[1]+prival[2]);
    if ( tmp2>EPS_PRI ) {
      tmp = tmp1 / tmp2;
      if ( tmp<=1. ) {
        tmp = asin(tmp);
        if ( tmp>phim ) phim = tmp;
      }
    }
    tmp1 = scalar_dabs(prival[2]-prival[0]);
    tmp2 = scalar_dabs(prival[2]+prival[0]);
    if ( tmp2>EPS_PRI ) {
      tmp = tmp1 / tmp2;
      if ( tmp<=1. ) {
        tmp = asin(tmp);
        if ( tmp>phim ) phim = tmp;
      }
    }
    result[0] = phim * 360. / ( 2.*PIRAD );
    length_result = 1;
  }
  else
    db_error( POST_CALCUL, 0 );

}

void parallel_calcul_node( void )

{
  long int length=0, idim=0, jdim=0, inod=0, max_node=0,
    length_result=0, indx=0, iloop=0, nloop=0, ithread=0, idum[1], 
    *next_of_loop=NULL;
  double unknown_values[MDIM*MDIM], result[DATA_ITEM_SIZE],
    dof_calcul[MCALCUL], *node_dof=NULL;

  db_max_index( NODE_DOF, 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_DOF, inod, VERSION_NORMAL ) ) {
        node_dof = db_dbl( NODE_DOF, inod, VERSION_NORMAL );
        if ( calcul_matrix ) {
          for ( idim=0; idim<MDIM; idim++ ) {
            for ( jdim=0; jdim<MDIM; jdim++ ) {
              indx = idim*MDIM + jdim;
              unknown_values[indx] = 
                node_dof[calcul_mat_indx+stress_indx(idim,jdim)*nder];
            }
          }
        }
        else {
          for ( idim=0; idim<ndim; idim++ ) {
            indx = idim;
            unknown_values[indx] = node_dof[calcul_vec_indx+idim*nder];
          }
        }
        calculate_operat( unknown_values, result, length_result );
        db( NODE_DOF_CALCUL, inod, idum, dof_calcul, length, 
          VERSION_NORMAL, GET_IF_EXISTS );
        if ( (length+length_result)>MCALCUL ) db_error( POST_CALCUL, 0 );
        array_move( result, &dof_calcul[length], length_result );
        length += length_result;
        db( NODE_DOF_CALCUL, inod, idum, dof_calcul, length, 
          VERSION_NORMAL, PUT );
      }
    }
    delete[] next_of_loop;
  }

}
