/*
    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"

void parallel_remesh( void )

{
  long int inod=0, ino, idim=0, ipuknwn=0, max_node=0,
    swit=0, swit_node=0, in=0, icontrol=0, nnod=0, max_node_boundary=0,
    on_boundary=0, iloop=0, nloop=0, ithread=0, ldum=0, idum[1], 
    node_node[DATA_ITEM_SIZE], node_bounded[MPUKNWN], *next_of_loop=NULL;
  double dx=0., dtime=0., tmp=0., smallest_distance=0., ddum[1], dx_geometry[MDIM], 
    dx_residue[MDIM], average_coord[MDIM], residue_gradient[MDIM], 
    node_dof[MUKNWN], coord[MDIM], neighbour_coord[MDIM], work[MDIM],
    node_remesh_velocity[MDIM], control_remesh_factor[2];

  swit = set_swit(-1,-1,"parallel_remesh");
  if ( swit ) pri( "In routine PARALLEL_REMESH" );

  area_node_boundary();

  if ( db_max_index( NODE_BOUNDARY, max_node_boundary, VERSION_NORMAL, GET ) < 0 ) {
    pri( "Error: node_boundary should be specified if you use control_new_mesh." );
    exit(TN_EXIT_STATUS);
  }

  db( DTIME, 0, idum, &dtime, ldum, VERSION_NEW, GET_IF_EXISTS );
  db( ICONTROL, 0, &icontrol, ddum, ldum, VERSION_NORMAL, GET );
  if ( db_active_index( CONTROL_MESH_REMESH_FACTOR, icontrol, VERSION_NORMAL ) ) {
    db( CONTROL_MESH_REMESH_FACTOR, icontrol, idum, 
      control_remesh_factor, ldum, VERSION_NORMAL, GET );
    if ( control_remesh_factor[0]<0. || control_remesh_factor[1]<0. ) 
      db_error( CONTROL_MESH_REMESH_FACTOR, icontrol );
    if ( control_remesh_factor[1]>0. && !residue ) 
      db_error( CONTROL_MESH_REMESH_FACTOR, icontrol );
  }
  else {
    control_remesh_factor[0] = 1.;
    control_remesh_factor[1] = 0.;
  }

  array_set( dx_geometry, 0., ndim );
  array_set( dx_residue, 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 ) ) {
        swit_node = swit; swit = swit && set_swit(-1,inod,"");
        db( NODE, inod, idum, coord, ldum, VERSION_NORMAL, GET );
        db( NODE_NODE, inod, node_node, ddum, nnod, VERSION_NORMAL, GET );
        if ( materi_displacement ) {
          db( NODE_DOF, inod, idum, node_dof, nuknwn, VERSION_NORMAL, GET );
          for ( idim=0; idim<ndim; idim++ )
            coord[idim] += node_dof[dis_indx+idim*nder];
        }
        if ( swit ) {
          pri( "inod", inod );
          pri( "old coord", coord, ndim );
          pri( "node_node", node_node, nnod );
        }
           // relocate
        if ( nnod>0 ) {
            // geometric remeshing displacement
          if      ( control_remesh_factor[0]>0. ) {
            array_set( average_coord, 0., ndim );
            for ( in=0; in<nnod; in++ ) {
              ino = node_node[in];
              db( NODE, ino, idum, neighbour_coord, ldum, VERSION_NORMAL, GET );
              if ( materi_displacement ) {
                db( NODE_DOF, ino, idum, node_dof, nuknwn, VERSION_NORMAL, GET );
                for ( idim=0; idim<ndim; idim++ )
                  neighbour_coord[idim] += node_dof[dis_indx+idim*nder];
              }
              if ( swit ) pri( "neighbour_coord-b", neighbour_coord, ndim );
              array_add( average_coord, neighbour_coord, average_coord, 
                ndim );
            }
            array_multiply( average_coord, average_coord, 1./nnod, ndim );
            if ( swit ) pri( "average_coord", average_coord, ndim );
            array_subtract( average_coord, coord, dx_geometry, ndim );
            array_multiply( dx_geometry, dx_geometry, control_remesh_factor[0], ndim );
            if ( swit ) pri( "dx_geometry 1", dx_geometry, ndim );
          }
            // residue remeshing displacement
          if ( control_remesh_factor[1]>0. ) {
            db( NODE_DOF, inod, idum, node_dof, nuknwn, VERSION_NORMAL, GET );
            array_move( &node_dof[res_indx+1], residue_gradient, ndim );
            array_normalize( residue_gradient, ndim );
            smallest_distance = DBL_MAX;
            for ( in=0; in<nnod; in++ ) {
              ino = node_node[in];
              db( NODE, ino, idum, neighbour_coord, ldum, VERSION_NORMAL, GET);
              tmp = array_distance( coord, neighbour_coord, work, ndim );
              if ( tmp<smallest_distance ) smallest_distance = tmp;
            }
            array_multiply( residue_gradient, dx_residue, 0.01*smallest_distance, 
              ndim );
            array_multiply( dx_residue, dx_residue, control_remesh_factor[1], ndim );
          }
          if ( db_active_index( NODE_BOUNDARY, inod, VERSION_NORMAL ) )
             on_boundary = 1;
          else
             on_boundary = 0;
          if ( on_boundary ) {
            array_set( dx_geometry, 0., ndim );
            array_set( dx_residue, 0., ndim );
          }
          if ( db_active_index( NODE_BOUNDED, inod, VERSION_NORMAL ) )
            db( NODE_BOUNDED, inod, node_bounded, ddum, ldum, VERSION_NORMAL, GET );
          else
            array_set( node_bounded, 0, npuknwn );
          for ( idim=0; idim<ndim; idim++ ) {
            ipuknwn = vel_indx/nder + idim;
            if ( !materi_velocity || !node_bounded[ipuknwn] ) {
              dx = dx_geometry[idim] + dx_residue[idim];
              if ( dtime!=0. ) node_remesh_velocity[idim] = dx/dtime;
              else node_remesh_velocity[idim] = 0.;
              coord[idim] += dx_geometry[idim];
              coord[idim] += dx_residue[idim];
              if ( swit ) pri( "new coord becoming", coord, ndim );
            }
            else
              dx = 0.;
            if ( dtime!=0. ) node_remesh_velocity[idim] = -dx/dtime;
            else node_remesh_velocity[idim] = 0.;
          }
          db( NODE, inod, idum, coord, ndim, VERSION_TMP, PUT );
          db( NODE_REMESH_VELOCITY, inod, idum, node_remesh_velocity, ndim, 
            VERSION_TMP, PUT );
          if ( swit ) pri( "new coord", coord, ndim );
        }
        swit = swit_node;
      }
    }
    delete[] next_of_loop;
  }

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