/*
    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_SIG 1.e-5
#define EPS_DIR 1.e-10

void plasti_rule( long int element, long int gr, double user_data[],
  double new_unknowns[], double new_grad_new_unknowns[],
  double old_hisv[], double new_hisv[], long int task, 
  long int &plasti_type, double sig[], double &f, double dir[] )

{

  long int i=0, swit=0, idim=0, tmp_plasti_type=-1, in_apex=0, ldum=0;
  double sig_yield=0., sigm=0., K=0., alpha=0.,
    f_flow_right=0., f_flow_left=0., phi=0., c=0., dir_length=0.,
    phi_flow=0., alpha_flow=0., f_yield=-1.e10, f_flow=-1.e10, 
    sig_eq=0., tmp_sig_eq=0., void_fraction=0.,
    q1=0., q2=0., q3=0., sig_princ_apex[MDIM], 
    sig_dev[MDIM*MDIM], sig_tmp[MDIM*MDIM], 
    plasti_data[DATA_ITEM_SIZE], sig_princ[MDIM], ddum[MDIM*MDIM],
    e_vec[MDIM], f_vec[MDIM];

  swit = set_swit(element,-1,"plasti_rule");
  if ( swit ) pri( "In PLASTI_RULE" );

  if ( swit ) {
    if      ( task==GET_YIELD_RULE ) 
      pri( "GET_YIELD_RULE" );
    else if ( task==GET_FLOW_RULE ) {
      pri( "GET_FLOW_RULE" );
      pri( "plasti_type", plasti_type );
    }
    else if ( task==GET_FLOW_RULE_GRAD ) {
      pri( "GET_FLOW_RULE_GRAD" );
      pri( "plasti_type", plasti_type );
    }
    pri( "sig", sig, MDIM, MDIM );
  }

    // mean stress
  sigm = ( sig[0] + sig[4] + sig[8] ) / 3.;
  if ( swit ) pri( "sigm", sigm );

    // deviatoric strains and stresses
  array_move( sig, sig_dev, MDIM*MDIM );
  for ( i=0; i<MDIM; i++ ) sig_dev[i*MDIM+i] -= sigm;
  if ( swit ) pri( "sig_dev", sig_dev, MDIM, MDIM );

    // equivalent stress
  sig_eq = sqrt(scalar_dabs(array_inproduct(sig_dev,sig_dev,MDIM*MDIM))/2.);
  if ( swit ) pri( "sig_eq", sig_eq );

  f = -1.e10;
  if ( get_group_data( GROUP_MATERI_PLASTI_COMPRESSION, gr, new_unknowns, 
    plasti_data, ldum, GET_IF_EXISTS ) ) {
    if ( 
      (task==GET_YIELD_RULE&&plasti_type==-NONE) ||
      (task==GET_YIELD_RULE&&plasti_type==GROUP_MATERI_PLASTI_COMPRESSION) ||
      (task==GET_FLOW_RULE&&plasti_type==GROUP_MATERI_PLASTI_COMPRESSION) ) {
      if ( swit ) pri( "check plasti_compression" );
      sig_yield = plasti_data[0];
      if ( swit ) pri( "sig_yield", sig_yield );
      matrix_eigenvalues( sig, sig_princ );
      if ( swit ) pri( "sig_princ", sig_princ, MDIM );
      tmp_sig_eq = 0.;
      for ( idim=0; idim<MDIM; idim++ ) {
        if ( sig_princ[idim]<0. ) {
          tmp_sig_eq += sig_princ[idim] * sig_princ[idim];
        }
      }
      tmp_sig_eq = sqrt( scalar_dabs(tmp_sig_eq) ); 
      f_yield = tmp_sig_eq - sig_yield;
      f_flow = tmp_sig_eq - sig_yield;
      if      ( task==GET_YIELD_RULE ) {
        if (  f_yield>f ) {
          f = f_yield;
          tmp_plasti_type = GROUP_MATERI_PLASTI_COMPRESSION;
        }
        if ( swit ) pri( "f_yield", f_yield );
      }
      else {
        assert( task==GET_FLOW_RULE );
        f = f_flow;
        if ( swit ) pri( "f_flow", f_flow );
      }
    }
  }
  if ( get_group_data( GROUP_MATERI_PLASTI_DRUCKPRAG, gr, new_unknowns,
      plasti_data, ldum, GET_IF_EXISTS ) ) {
    if ( 
      (task==GET_YIELD_RULE&&plasti_type==-NONE) ||
      (task==GET_YIELD_RULE&&plasti_type==GROUP_MATERI_PLASTI_DRUCKPRAG) ||
      (task==GET_YIELD_RULE&&plasti_type==GROUP_MATERI_PLASTI_DRUCKPRAG_APEX) ||
      (task==GET_FLOW_RULE&&plasti_type==GROUP_MATERI_PLASTI_DRUCKPRAG) ||
      (task==GET_FLOW_RULE&&plasti_type==GROUP_MATERI_PLASTI_DRUCKPRAG_APEX)
      ) {
      if ( swit ) pri( "check plasti_druckprag" );
      phi = plasti_data[0];
      c = plasti_data[1];
      alpha = 2.*sin(phi)/((3.-sin(phi))*sqrt(3.));
      K = 6.*c*cos(phi)/((3.-sin(phi))*sqrt(3.));
      phi_flow = plasti_data[2];
      alpha_flow = 2.*sin(phi_flow)/((3.-sin(phi_flow))*sqrt(3.));
      if ( (task==GET_YIELD_RULE&&plasti_type==-NONE && phi>0. ) ||
           (task==GET_YIELD_RULE&&plasti_type==GROUP_MATERI_PLASTI_DRUCKPRAG_APEX) ||
           (task==GET_FLOW_RULE&&plasti_type==GROUP_MATERI_PLASTI_DRUCKPRAG_APEX) ) {
          // separate yield and flow rule for apex
        matrix_eigenvalues( sig, sig_princ );
        array_set( sig_princ_apex, c*cos(phi)/sin(phi), MDIM );
        array_set( e_vec, 1./sqrt(3.), MDIM );
        array_subtract( sig_princ, sig_princ_apex, f_vec, MDIM );
        in_apex = array_inproduct(f_vec,e_vec,MDIM)>0.;
        if ( in_apex || plasti_type==GROUP_MATERI_PLASTI_DRUCKPRAG_APEX ) {
          f_yield = array_size( f_vec, MDIM );
          f_flow = array_size( f_vec, MDIM );
        }
        else {
          f_yield = -1.e10;
          f_flow = -1.e10;
        }
        if      ( task==GET_YIELD_RULE ) {
          if (  f_yield>f ) {
            f = f_yield;
            tmp_plasti_type = GROUP_MATERI_PLASTI_DRUCKPRAG_APEX;
          }
          if ( swit ) pri( "f_yield", f_yield );
        }
        else {
          assert( task==GET_FLOW_RULE );
          f = f_flow;
          if ( swit ) pri( "f_flow", f_flow );
        }
      }
      if ( !in_apex ) {
        if ( (task==GET_YIELD_RULE&&plasti_type==-NONE ) ||
             (task==GET_YIELD_RULE&&plasti_type==GROUP_MATERI_PLASTI_DRUCKPRAG) ||
             (task==GET_FLOW_RULE&&plasti_type==GROUP_MATERI_PLASTI_DRUCKPRAG) ) {
          f_yield = 3.*alpha*sigm + sig_eq - K;
          f_flow = 3.*alpha_flow*sigm + sig_eq - K;
          if      ( task==GET_YIELD_RULE ) {
            if (  f_yield>f ) {
              f = f_yield;
              tmp_plasti_type = GROUP_MATERI_PLASTI_DRUCKPRAG;
            }
            if ( swit ) pri( "f_yield", f_yield );
          }
          else {
            assert( task==GET_FLOW_RULE );
            f = f_flow;
            if ( swit ) pri( "f_flow", f_flow );
          }
        }
      }
    }
  }
  if ( get_group_data( GROUP_MATERI_PLASTI_GURSON, gr, new_unknowns,
      plasti_data, ldum, GET_IF_EXISTS ) ) {
    if ( 
      (task==GET_YIELD_RULE&&plasti_type==-NONE) ||
      (task==GET_YIELD_RULE&&plasti_type==GROUP_MATERI_PLASTI_GURSON) ||
      (task==GET_FLOW_RULE&&plasti_type==GROUP_MATERI_PLASTI_GURSON) ) {
      if ( swit ) pri( "plasti_gurson" );
      void_fraction = new_unknowns[void_indx];
      sig_yield = plasti_data[0];
      q1 = plasti_data[1];
      q2 = plasti_data[2];
      q3 = plasti_data[3];
      f_yield = 3.*sqrt(3.)*sig_eq/(sig_yield*sig_yield) +
        2.*q1*void_fraction*cosh(q2*3.*sigm/(2.*sig_yield)) -
        (1.+(q3*void_fraction)*(q3*void_fraction) );
      f_flow = f_yield;
      if      ( task==GET_YIELD_RULE ) {
        if ( f_yield>f ) {
          f = f_yield;
          tmp_plasti_type = GROUP_MATERI_PLASTI_GURSON;
        }
        if ( swit ) pri( "f_yield", f_yield );
      }
      else {
        assert( task==GET_FLOW_RULE );
        f = f_flow;
        if ( swit ) pri( "f_flow", f_flow );
      }
    }
  }
  if ( get_group_data( GROUP_MATERI_PLASTI_MOHRCOUL, gr, new_unknowns,
      plasti_data, ldum, GET_IF_EXISTS ) ) {
    if ( 
      (task==GET_YIELD_RULE&&plasti_type==-NONE) ||
      (task==GET_YIELD_RULE&&plasti_type==GROUP_MATERI_PLASTI_MOHRCOUL_01) ||
      (task==GET_YIELD_RULE&&plasti_type==GROUP_MATERI_PLASTI_MOHRCOUL_12) ||
      (task==GET_YIELD_RULE&&plasti_type==GROUP_MATERI_PLASTI_MOHRCOUL_20) ||
      (task==GET_YIELD_RULE&&plasti_type==GROUP_MATERI_PLASTI_MOHRCOUL_APEX) ||
      (task==GET_FLOW_RULE&&plasti_type==GROUP_MATERI_PLASTI_MOHRCOUL_01) ||
      (task==GET_FLOW_RULE&&plasti_type==GROUP_MATERI_PLASTI_MOHRCOUL_12) ||
      (task==GET_FLOW_RULE&&plasti_type==GROUP_MATERI_PLASTI_MOHRCOUL_20) ||
      (task==GET_FLOW_RULE&&plasti_type==GROUP_MATERI_PLASTI_MOHRCOUL_APEX)
       ) {
      if ( swit ) pri( "check plasti_mohrcoul" );
      phi = plasti_data[0];
      c = plasti_data[1];
      phi_flow = plasti_data[2];
      matrix_eigenvalues( sig, sig_princ );
      if ( (task==GET_YIELD_RULE&&plasti_type==-NONE && phi>0. ) ||
           (task==GET_YIELD_RULE&&plasti_type==GROUP_MATERI_PLASTI_MOHRCOUL_APEX) ||
           (task==GET_FLOW_RULE&&plasti_type==GROUP_MATERI_PLASTI_MOHRCOUL_APEX) ) {
          // seperate yield and flow rule for apex
        array_set( sig_princ_apex, c*cos(phi)/sin(phi), MDIM );
        array_set( e_vec, 1./sqrt(3.), MDIM );
        array_subtract( sig_princ, sig_princ_apex, f_vec, MDIM );
        in_apex = array_inproduct(f_vec,e_vec,MDIM)>0.;
        if ( in_apex || plasti_type==GROUP_MATERI_PLASTI_MOHRCOUL_APEX ) {
          f_yield = array_size( f_vec, MDIM );
          f_flow = array_size( f_vec, MDIM );
        }
        else {
          f_yield = -1.e10;
          f_flow = -1.e10;
        }
        if      ( task==GET_YIELD_RULE ) {
          if (  f_yield>f ) {
            f = f_yield;
            tmp_plasti_type = GROUP_MATERI_PLASTI_MOHRCOUL_APEX;
          }
          if ( swit ) pri( "f_yield", f_yield );
        }
        else {
          assert( task==GET_FLOW_RULE );
          f = f_flow;
          if ( swit ) pri( "f_flow", f_flow );
        }
      }
      if ( !in_apex ) {
        if ( (task==GET_YIELD_RULE&&plasti_type==-NONE) ||
             (task==GET_YIELD_RULE&&plasti_type==GROUP_MATERI_PLASTI_MOHRCOUL_01) ||
             (task==GET_FLOW_RULE&&plasti_type==GROUP_MATERI_PLASTI_MOHRCOUL_01) ) {
          f_yield = 0.5*scalar_dabs(sig_princ[0]-sig_princ[1]) +
           0.5*(sig_princ[0]+sig_princ[1])*sin(phi) - c*cos(phi);
          f_flow = 0.5*scalar_dabs(sig_princ[0]-sig_princ[1]) + 
            0.5*(sig_princ[0]+sig_princ[1])*sin(phi_flow) - c*cos(phi_flow);
          if      ( task==GET_YIELD_RULE ) {
            if ( f_yield>f ) {
              f = f_yield;
              tmp_plasti_type = GROUP_MATERI_PLASTI_MOHRCOUL_01;
              if ( swit ) pri( "f_yield", f_yield );
            }
          }
          else {
            assert( task==GET_FLOW_RULE );
            f = f_flow;
            if ( swit ) pri( "f_flow", f_flow );
          }
        }
        if ( (task==GET_YIELD_RULE&&plasti_type==-NONE) ||
             (task==GET_YIELD_RULE&&plasti_type==GROUP_MATERI_PLASTI_MOHRCOUL_12) ||
             (task==GET_FLOW_RULE&&plasti_type==GROUP_MATERI_PLASTI_MOHRCOUL_12) ) {
          f_yield = 0.5*scalar_dabs(sig_princ[1]-sig_princ[2]) +
           0.5*(sig_princ[1]+sig_princ[2])*sin(phi) - c*cos(phi);
          f_flow = 0.5*scalar_dabs(sig_princ[1]-sig_princ[2]) + 
            0.5*(sig_princ[1]+sig_princ[2])*sin(phi_flow) - c*cos(phi_flow);
          if      ( task==GET_YIELD_RULE ) {
            if ( f_yield>f ) {
              f = f_yield;
              tmp_plasti_type = GROUP_MATERI_PLASTI_MOHRCOUL_12;
              if ( swit ) pri( "f_yield", f_yield );
            }
          }
          else {
            assert( task==GET_FLOW_RULE );
            f = f_flow;
            if ( swit ) pri( "f_flow", f_flow );
          }
        }
        if ( (task==GET_YIELD_RULE&&plasti_type==-NONE) ||
             (task==GET_YIELD_RULE&&plasti_type==GROUP_MATERI_PLASTI_MOHRCOUL_20) ||
             (task==GET_FLOW_RULE&&plasti_type==GROUP_MATERI_PLASTI_MOHRCOUL_20) ) {
          f_yield = 0.5*scalar_dabs(sig_princ[2]-sig_princ[0]) +
           0.5*(sig_princ[2]+sig_princ[0])*sin(phi) - c*cos(phi);
          f_flow = 0.5*scalar_dabs(sig_princ[2]-sig_princ[0]) + 
            0.5*(sig_princ[2]+sig_princ[0])*sin(phi_flow) - c*cos(phi_flow);
          if      ( task==GET_YIELD_RULE ) {
            if ( f_yield>f ) {
              f = f_yield;
              tmp_plasti_type = GROUP_MATERI_PLASTI_MOHRCOUL_20;
              if ( swit ) pri( "f_yield", f_yield );
            }
          }
          else {
            assert( task==GET_FLOW_RULE );
            f = f_flow;
            if ( swit ) pri( "f_flow", f_flow );
          }
        }
      }
    }
  }
  if ( get_group_data( GROUP_MATERI_PLASTI_TENSION, gr, new_unknowns, 
      plasti_data, ldum, GET_IF_EXISTS ) ) {
    if ( 
      (task==GET_YIELD_RULE&&plasti_type==-NONE) ||
      (task==GET_YIELD_RULE&&plasti_type==GROUP_MATERI_PLASTI_TENSION) ||
      (task==GET_FLOW_RULE&&plasti_type==GROUP_MATERI_PLASTI_TENSION) ) {
      if ( swit ) pri( "check plasti_tension" );
      sig_yield = plasti_data[0];
      if ( swit ) pri( "sig_yield", sig_yield );
      matrix_eigenvalues( sig, sig_princ );
      if ( swit ) pri( "sig_princ", sig_princ, MDIM );
      tmp_sig_eq = 0.;
      for ( idim=0; idim<MDIM; idim++ ) {
        if ( sig_princ[idim]>0. ) {
          tmp_sig_eq += sig_princ[idim] * sig_princ[idim];
        }
      }
      tmp_sig_eq = sqrt( scalar_dabs(tmp_sig_eq) ); 
      f_yield = tmp_sig_eq - sig_yield;
      f_flow = tmp_sig_eq - sig_yield;
      if      ( task==GET_YIELD_RULE ) {
        if (  f_yield>f ) {
          f = f_yield;
          tmp_plasti_type = GROUP_MATERI_PLASTI_TENSION;
        }
        if ( swit ) pri( "f_yield", f_yield );
      }
      else {
        assert( task==GET_FLOW_RULE );
        f = f_flow;
        if ( swit ) pri( "f_flow", f_flow );
      }
    }
  }
  if ( get_group_data( GROUP_MATERI_PLASTI_VONMISES, gr, new_unknowns,
      plasti_data, ldum, GET_IF_EXISTS ) ) {
    if ( 
      (task==GET_YIELD_RULE&&plasti_type==-NONE) ||
      (task==GET_YIELD_RULE&&plasti_type==GROUP_MATERI_PLASTI_VONMISES) ||
      (task==GET_FLOW_RULE&&plasti_type==GROUP_MATERI_PLASTI_VONMISES) ) {
      if ( swit ) pri( "plasti_vonmises" );
      sig_yield = plasti_data[0];
      f_yield = sig_eq*sqrt(3.) - sig_yield;
      f_flow = f_yield;
      if      ( task==GET_YIELD_RULE ) {
        if ( f_yield>f ) {
          f = f_yield;
          tmp_plasti_type = GROUP_MATERI_PLASTI_VONMISES;
        }
        if ( swit ) pri( "f_yield", f_yield );
      }
      else {
        assert( task==GET_FLOW_RULE );
        f = f_flow;
        if ( swit ) pri( "f_flow", f_flow );
      }
    }
  }
  if ( 
    (task==GET_YIELD_RULE&&plasti_type==-NONE) ||
    (task==GET_YIELD_RULE&&plasti_type==GROUP_MATERI_PLASTI_USER) ||
    (task==GET_FLOW_RULE&&plasti_type==GROUP_MATERI_PLASTI_USER) ) {
    if ( swit ) pri( "check plasti_user" );
    if      ( task==GET_YIELD_RULE ) {
      f_yield = -1.e10;
      user_plasti( 1, user_data, new_unknowns, old_hisv, new_hisv, sig, f_yield );
      if ( f_yield>f ) {
        f = f_yield;
        tmp_plasti_type = GROUP_MATERI_PLASTI_USER;
      }
      if ( swit ) pri( "f_yield", f_yield );
    }
    else {
      assert( task==GET_FLOW_RULE );
      user_plasti( 2, user_data, new_unknowns, old_hisv, new_hisv, sig, f_flow );
      f = f_flow;
      if ( swit ) pri( "f_flow", f_flow );
    }
  }
  if ( task==GET_YIELD_RULE && plasti_type==-NONE ) plasti_type = tmp_plasti_type;
  if ( task==GET_YIELD_RULE || task==GET_FLOW_RULE ) {
    if ( swit ) {
      pri( "f", f );
      pri( "plasti_type", plasti_type );
    }
  }

      // use central differences to determine flow direction
  if ( task==GET_FLOW_RULE_GRAD ) {
    array_move( sig, sig_tmp, MDIM*MDIM ); dir_length = 0.;
    for ( i=0; i<MDIM*MDIM; i++ ) {
      sig_tmp[i] += EPS_SIG * sig_eq;
      plasti_rule( element, gr, user_data, new_unknowns, new_grad_new_unknowns,
        old_hisv, new_hisv, GET_FLOW_RULE, plasti_type, sig_tmp, f_flow_right, ddum );
      sig_tmp[i] -= 2. * EPS_SIG * sig_eq;
      plasti_rule( element, gr, user_data, new_unknowns, new_grad_new_unknowns,
        old_hisv, new_hisv, GET_FLOW_RULE, plasti_type, sig_tmp, f_flow_left, ddum );
      dir[i] = (f_flow_right-f_flow_left)/(2.*EPS_SIG);
      dir_length += dir[i]*dir[i];
      sig_tmp[i] += EPS_SIG * sig_eq;
    }
    dir_length = sqrt( scalar_dabs(dir_length) );
    if ( dir_length>EPS_DIR ) 
      array_multiply( dir, dir, 1./dir_length, MDIM*MDIM );
    if ( swit ) pri( "dir", dir, MDIM*MDIM );
  }

  if ( swit ) pri( "Out PLASTI_RULE" );
}
