/*     CalculiX - A 3-dimensional finite element program                 */
/*              Copyright (C) 1998-2005 Guido Dhondt                          */

/*     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(version 2);    */
/*                    */

/*     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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.         */

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include "CalculiX.h"
#ifdef SPOOLES
   #include "spooles.h"
#endif
#ifdef SGI
   #include "sgi.h"
#endif
#ifdef TAUCS
   #include "tau.h"
#endif


void checkconvergence(double *co, int *nk, int *kon, int *ipkon, char *lakon,
	  int *ne, double *v, double *stn, int *inum, int *nmethod, 
	  int *kode, char *filab, double *een, double *t1act,
          double *fn, double *time, double *epn,int *ielmat,char *matname,
          double *enern, double *xstaten, int *nstate_, int *istep,
          int *iinc, int *iperturb, double *ener, int *mint_, char *output,
          int *ithermal, double *qfn, int *mode, int *noddiam, double *trab,
          int *inotr, int *ntrans, double *orab, int *ielorien, int *norien,
          char *description,double *sti,
	  int *icutb, int *iit, double *dtime, double *qa, double *vold,
          double *qam, double *ram1, double *ram2, double *ram,
          double *cam, double *uam, int *ntg, double *ttime,
          int *icntrl, double *theta, double *dtheta, double *veold,
          double *vini, int *idrct, double *tper,int *istab, double *tmax, 
          int *nactdof, double *b, double *tmin, double *ctrl){

    int k,*ipneigh=NULL,*neigh=NULL;
    double c1,c2;

    int i0,ir,ip,ic,il,ig,ia,iest;
    double df,dc,db,dd,ran,can,rap,ea,cae,ral;
    i0=ctrl[0];ir=ctrl[1];ip=ctrl[2];ic=ctrl[3];il=ctrl[4];ig=ctrl[5];ia=ctrl[7];
    df=ctrl[10];dc=ctrl[11];db=ctrl[12];dd=ctrl[16];
    ran=ctrl[18];can=ctrl[19];rap=ctrl[22];
    ea=ctrl[23];cae=ctrl[24];ral=ctrl[25];

    if(*qa>ea**qam){
	if(*iit<=ip){c1=ran;}
	else{c1=rap;}
	c2=can;
    }
    else{
	c1=ea;
	c2=cae;
    }
    
    if(*ram1<*ram2){*ram2=*ram1;}
/*	printf("*ram<=c1**qam %f %f\n",*ram,c1**qam);
	printf("*cam<=c2**uam %f %f\n",*cam,c2**uam);
	printf("*ram**cam/(*ram2)<c2**uam %f %f\n",*ram**cam/(*ram2),c2**uam);
	printf("*ram<=ral**qam %f %f\n",*ram,ral**qam);
	printf("*qa<=ea**qam %f %f\n",*qa,ea**qam);
	printf("ntg %d \n",*ntg);*/
    if((*ram<=c1**qam)&&
       ((*cam<=c2**uam)||
	(((*ram**cam/(*ram2)<c2**uam)||(*ram<=ral**qam)||(*qa<=ea**qam))&&(*ntg==0))||
	(*cam<1.e-8))){
	
	/* increment convergence reached */
	
	*ttime=*ttime+*dtime;
	FORTRAN(writesummary,(istep,iinc,icutb,iit,ttime,time,dtime));
	
	*icntrl=1;
	*icutb=0;
	*theta=*theta+*dtheta;
	
	/* defining a mean "velocity" for static calculations: is used to
	   extrapolate the present results for next increment */
	
	if(*nmethod != 4){
	    for(k=0;k<4**nk;++k){
		veold[k]=(vold[k]-vini[k])/(*dtime);
	    }
	}
	
	/* check whether next increment size must be decreased */
	
	if((*iit>il)&&(*idrct==0)){
	    if(*idrct==0){
		*dtheta=*dtheta*db;
		printf(" convergence; the increment size is decreased to %e\n\n",*dtheta**tper);
		if(*dtheta<*tmin){
		    printf("\n *ERROR: increment size smaller than minimum\n");
		    printf(" best solution and residuals are in the frd file\n\n");
		    fn=NNEW(double,4**nk);
		    FORTRAN(storeresidual,(nactdof,b,fn,filab,ithermal,nk));
		    ++*kode;
		    inum=NNEW(int,*nk);for(k=0;k<*nk;k++) inum[k]=1;
		    ipneigh=NNEW(int,*nk);neigh=NNEW(int,40**ne);
		    FORTRAN(out,(co,nk,kon,ipkon,lakon,ne,v,stn,inum,nmethod,kode,
			 filab,een,t1act,fn,ttime,epn,ielmat,matname,enern,
			 xstaten,nstate_,istep,iinc,iperturb,ener,mint_,output,
			 ithermal,qfn,mode,noddiam,
			 trab,inotr,ntrans,orab,ielorien,norien,description,
			 ipneigh,neigh,sti));free(ipneigh);free(neigh);
			 FORTRAN(stop,());
		}
	    }
	    else{
		printf("convergence\n\n");}
	}
	
	/* check whether next increment size can be increased */
	
	else if(*iit<=ig){
	    if((*istab==1)&&(*idrct==0)){
		*dtheta=*dtheta*dd;
		printf(" convergence; the increment size is increased to %e\n\n",*dtheta**tper);
	    }
	    else{
		*istab=1;
		printf(" convergence\n\n");
	    }
	}
	else{
	    *istab=0;
	    printf(" convergence\n\n");
	}
	
	if((*dtheta>*tmax)&&(*idrct==0)){
	    *dtheta=*tmax;
	    printf(" the increment size exceeds thetamax and is decreased to %e\n\n",*dtheta**tper);
	}
	if(*dtheta>1.-*theta){
	    *dtheta=1.-*theta;
	    printf(" the increment size exceeds the remainder of the step and is decreased to %e\n\n",*dtheta**tper);
	}
    }
    else{
	
	/* check for the amount of iterations */
	
	if(*iit>ic){
	    printf("\n *ERROR: too many iterations needed\n");
	    printf(" best solution and residuals are in the frd file\n\n");
	    fn=NNEW(double,4**nk);
	    FORTRAN(storeresidual,(nactdof,b,fn,filab,ithermal,nk));
	    ++*kode;
	    inum=NNEW(int,*nk);for(k=0;k<*nk;k++) inum[k]=1;
	    ipneigh=NNEW(int,*nk);neigh=NNEW(int,40**ne);
	    FORTRAN(out,(co,nk,kon,ipkon,lakon,ne,v,stn,inum,nmethod,kode,
		filab,een,t1act,fn,ttime,epn,ielmat,matname,enern,
		xstaten,nstate_,istep,iinc,iperturb,ener,mint_,output,
		ithermal,qfn,mode,noddiam,
		trab,inotr,ntrans,orab,ielorien,norien,description,
		ipneigh,neigh,sti));free(ipneigh);free(neigh);
		FORTRAN(stop,());
	}	
	
	/* check for diverging residuals */
	
	if((*iit>=i0)||(fabs(*ram)>1.e20)||(fabs(*cam)>1.e20)){
	    if((*ram1>*ram2)&&(*ram>*ram2)&&(*ram>c1**qam)){
		if(*idrct==1) {
		    printf("\n *ERROR: solution seems to diverge; please try \n");
		    printf(" automatic incrementation; program stops\n");
		    printf(" best solution and residuals are in the frd file\n\n");
		    fn=NNEW(double,4**nk);
		    FORTRAN(storeresidual,(nactdof,b,fn,filab,ithermal,nk));
		    ++*kode;
		    inum=NNEW(int,*nk);for(k=0;k<*nk;k++) inum[k]=1;
		    ipneigh=NNEW(int,*nk);neigh=NNEW(int,40**ne);
		    FORTRAN(out,(co,nk,kon,ipkon,lakon,ne,v,stn,inum,nmethod,kode,
		       filab,een,t1act,fn,ttime,epn,ielmat,matname,enern,
		       xstaten,nstate_,istep,iinc,iperturb,ener,mint_,output,
		       ithermal,qfn,mode,noddiam,
		       trab,inotr,ntrans,orab,ielorien,norien,description,
		       ipneigh,neigh,sti));free(ipneigh);free(neigh);
		       FORTRAN(stop,());
		}
		else {
		    *dtheta=*dtheta*df;
		    printf(" divergence; the increment size is decreased to %e\n",*dtheta**tper);
		    printf(" the increment is reattempted\n\n");
		    *istab=0;
		    if(*dtheta<*tmin){
			printf("\n *ERROR: increment size smaller than minimum\n");
			printf(" best solution and residuals are in the frd file\n\n");
			fn=NNEW(double,4**nk);
			FORTRAN(storeresidual,(nactdof,b,fn,filab,ithermal,nk));
			++*kode;
			inum=NNEW(int,*nk);for(k=0;k<*nk;k++) inum[k]=1;
			ipneigh=NNEW(int,*nk);neigh=NNEW(int,40**ne);
			FORTRAN(out,(co,nk,kon,ipkon,lakon,ne,v,stn,inum,nmethod,kode,
			   filab,een,t1act,fn,ttime,epn,ielmat,matname,enern,
			   xstaten,nstate_,istep,iinc,iperturb,ener,mint_,output,
			   ithermal,qfn,mode,noddiam,
			   trab,inotr,ntrans,orab,ielorien,norien,description,
			   ipneigh,neigh,sti));free(ipneigh);free(neigh);
			   FORTRAN(stop,());
		    }
		    *icntrl=1;
		    (*icutb)++;
		    if(*icutb>ia){
			printf("\n *ERROR: too many cutbacks\n");
			printf(" best solution and residuals are in the frd file\n\n");
			fn=NNEW(double,4**nk);
			FORTRAN(storeresidual,(nactdof,b,fn,filab,ithermal,nk));
			++*kode;
			inum=NNEW(int,*nk);for(k=0;k<*nk;k++) inum[k]=1;
			ipneigh=NNEW(int,*nk);neigh=NNEW(int,40**ne);
			FORTRAN(out,(co,nk,kon,ipkon,lakon,ne,v,stn,inum,nmethod,kode,
			   filab,een,t1act,fn,ttime,epn,ielmat,matname,enern,
			   xstaten,nstate_,istep,iinc,iperturb,ener,mint_,output,
			   ithermal,qfn,mode,noddiam,
			   trab,inotr,ntrans,orab,ielorien,norien,description,
			   ipneigh,neigh,sti));free(ipneigh);free(neigh);
			   FORTRAN(stop,());
		    }
		    return;
		}
	    }
	}
	
	/* check for too slow convergence */
	
	if(*iit>=ir){
	    iest=(int)ceil(*iit+log(ran**qam/(*ram))/log(*ram/(*ram1)));
	    if(iest>0){
	    printf(" estimated number of iterations till convergence = %d\n",
		   iest);
	    }
	    if((iest>ic)||(*iit==ic)){
		
		if(*idrct!=1){
		    *dtheta=*dtheta*dc;
		    printf(" too slow convergence; the increment size is decreased to %e\n",*dtheta**tper);
		    printf(" the increment is reattempted\n\n");
		    *istab=0;
		    if(*dtheta<*tmin){
			printf("\n *ERROR: increment size smaller than minimum\n");
			printf(" best solution and residuals are in the frd file\n\n");
			fn=NNEW(double,4**nk);
			FORTRAN(storeresidual,(nactdof,b,fn,filab,ithermal,nk));
			++*kode;
			inum=NNEW(int,*nk);for(k=0;k<*nk;k++) inum[k]=1;
			ipneigh=NNEW(int,*nk);neigh=NNEW(int,40**ne);
			FORTRAN(out,(co,nk,kon,ipkon,lakon,ne,v,stn,inum,nmethod,kode,
			   filab,een,t1act,fn,ttime,epn,ielmat,matname,enern,
			   xstaten,nstate_,istep,iinc,iperturb,ener,mint_,output,
			   ithermal,qfn,mode,noddiam,
			   trab,inotr,ntrans,orab,ielorien,norien,description,
			   ipneigh,neigh,sti));free(ipneigh);free(neigh);
			   FORTRAN(stop,());
		    }
		    *icntrl=1;
		    (*icutb)++;
		    if(*icutb>ia){
			printf("\n *ERROR: too many cutbacks\n");
			printf(" best solution and residuals are in the frd file\n\n");
			fn=NNEW(double,4**nk);
			FORTRAN(storeresidual,(nactdof,b,fn,filab,ithermal,nk));
			++*kode;
			inum=NNEW(int,*nk);for(k=0;k<*nk;k++) inum[k]=1;
			ipneigh=NNEW(int,*nk);neigh=NNEW(int,40**ne);
			FORTRAN(out,(co,nk,kon,ipkon,lakon,ne,v,stn,inum,nmethod,kode,
			   filab,een,t1act,fn,ttime,epn,ielmat,matname,enern,
			   xstaten,nstate_,istep,iinc,iperturb,ener,mint_,output,
			   ithermal,qfn,mode,noddiam,
			   trab,inotr,ntrans,orab,ielorien,norien,description,
			   ipneigh,neigh,sti));free(ipneigh);free(neigh);
			   FORTRAN(stop,());
		    }
		    return;
		}
	    }
	}
	
	printf(" no convergence\n\n");
	
	(*iit)++;
	
    }
    
    return;
}
