/*
 $Id: linsystemB.cc,v 1.3 1996/11/20 10:00:08 roitzsch Exp $
 (C)opyright 1996 by Konrad-Zuse-Center, Berlin
 All rights reserved.
 Part of the Kaskade distribution
*/

#include "linsystem.h"

#include "sysmat.h"
#include "precond.h"

#include "cmdpars.h"
extern CmdPars Cmd;

extern ostream* infoFile;

//-------------------------------------------------------------------------
//-------------------------------------------------------------------------


Bool LinSystem:: CROmin(Vector<Num>& x, int maxIter) 
{
    int iter, i;
    Num alpha, beta, qCq;
    Real bNorm, deltaE=0.0;
    int  dim = A->Dim();
    Vector<Num> p(dim), q(dim), Cq(dim), r(dim), Cr(dim), ACr(dim);

    preCond->initialize(A,x,b);
    bNorm = normRhs();
    
    preCond->AMult(r,A,x);
    FORALL(r,i) r[i] = b[i]-r[i];		// initial residual

    preCond->invert(Cr,A,r);	   		// initial search direction

    FORALL(p,i) p[i] = Cr[i];
    preCond->AMult(q,A,p);

    if (convergenceTest(0, x, r, bNorm, 0.0))
    { 
	preCond->close(A,x,b); 
 	return True; 
    }


    for (iter=1; iter<=maxIter; ++iter)
    {
	preCond->invert(Cq,A,q); 
	qCq = dot(q,Cq);				// qCq = (p,ACA*p)
	alpha = dot(Cr,q)/qCq;

	FORALL(x,i)   
	{
	    x[i]  += alpha * p[i];
	    r[i]  -= alpha * q[i];
	    Cr[i] -= alpha * Cq[i];
	}

	deltaE = Abs(alpha*alpha*qCq);  		// alt: dot(q,p);

	if (convergenceTest(iter, x, r, bNorm, deltaE)) break;

	preCond->AMult(ACr,A,Cr);

	beta = -dot(ACr,Cq)/qCq;

	FORALL(p,i) p[i] = Cr[i]  + beta*p[i];
	FORALL(q,i) q[i] = ACr[i] + beta*q[i];
    }

    preCond->close(A,x,b);
    return iter <= maxIter;
}
//-------------------------------------------------------------------------


Bool LinSystem:: CROdir(Vector<Num>& x, int maxIter) 
{
    int  iter, i;
    Num  alpha, sigma, gamma, qCq, qM1qM1=0.0, temp;
    Real bNorm, deltaE=0.0;
    int  dim = A->Dim();
    Vector<Num> p(dim), q(dim), r(dim), Cq(dim), ACq(dim),
    		pM1(dim), CqM1(dim), qM1(dim);

    preCond->initialize(A,x,b);
    bNorm = normRhs();
    
    preCond->AMult(r,A,x);
    FORALL(r,i) r[i] = b[i]-r[i];		// initial residual

    preCond->invert(p,A,r);	   		// initial search direction
    preCond->AMult (q,A,p);

    if (convergenceTest(0, x, r, bNorm, 0.0))
    { 
	preCond->close(A,x,b); 
	return True; 
    }


    for (iter=1; iter<=maxIter; ++iter)
    {
	preCond->invert(Cq,A,q); 
	qCq = dot(q,Cq);
	alpha = dot(r,Cq)/qCq;

	FORALL(x,i) x[i] += alpha * p[i];
	FORALL(r,i) r[i] -= alpha * q[i];

	deltaE = Abs(alpha*alpha*qCq);			// qCq = (p,ACA*p)

	if (convergenceTest(iter, x, r, bNorm, deltaE)) break;

	preCond->AMult(ACq,A,Cq);

	gamma = dot(ACq,Cq)/qCq;
	if (iter > 1) sigma = dot(ACq,CqM1)/qM1qM1;
	else          sigma = 0.0;

	FORALL(p,i) 
	{
	    temp = p[i];
	    p[i] = Cq[i] - gamma*p[i] - sigma*pM1[i];
	    pM1[i] = temp;

	    temp = q[i];
	    q[i] = ACq[i] - gamma*q[i] - sigma*qM1[i];
	    qM1[i] = temp;

	}
	// preCond->AMult(q,A,p);

	FORALL(Cq,i) CqM1[i] = Cq[i];
	qM1qM1 = qCq;
    }

    preCond->close(A,x,b);
    return iter <= maxIter;
}
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------

	//  solve normal equation system AT*A*x = AT * b

		// right preconditioning version


Bool LinSystem:: LSQCG (Vector<Num>& x, int maxIter)
{
    int  iter, i;
    Num  alpha, beta, rr, rrM1;
    Real bNorm;
    int  dim = A->Dim();
    Vector<Num> p(dim), r(dim), aux(dim), h(dim);
    
    preCond->initialize(A,x,b);
    bNorm = normRhs();
    
    preCond->AMult(r,A,x);
    FORALL(r,i) r[i] = b[i]-r[i];	
    
    preCond->ATMult(aux,A,r);			
    preCond->invert(p,A,aux);			// initial search direction
    
    rr = cdot(p,p);

    if (convergenceTest(0, x, r, bNorm, 0.0))
    { 
	preCond->close(A,x,b); 
	return True; 
    }

    
    for (iter=1; iter<=maxIter; ++iter)
    {
	preCond->invert(aux,A,p); 
	preCond->AMult(h,A,aux);
	
	alpha = rr/cdot(h,h);
	
	FORALL(x,i) x[i] += alpha * aux[i]; 
	FORALL(r,i) r[i] -= alpha * h[i];
	
	preCond->ATMult(aux,A,r);
	preCond->invert(h,A,aux); 
	
	rrM1 = rr;
	rr   = cdot(h,h);
	
	if (convergenceTest(iter, x, r, bNorm, 0.0)) break;
	
	beta = rr / rrM1;
	FORALL(p,i) p[i] = h[i] + beta*p[i];
    }

    preCond->close(A,x,b);
    return iter <= maxIter;
}
//-------------------------------------------------------------------------


/* 		// LSQCG with left preconditioning 

  Bool LinSystem:: LSQCG(Vector<Num>& x, int maxIter)
  {
  
  
  int  iter, i;
  Num  alpha, beta, rTsq, rTsqM1;
  Real bNorm;
  int  dim = A->Dim();
  Vector<Num> p(dim), r(dim), aux(dim), h(dim);
  
  preCond->initialize(A,x,b);
  
  preCond->invert(aux,A,b);
  bNorm = normRhs();
  
  preCond->AMult(r,A,x);
  FORALL(r,i) aux[i] = b[i]-r[i];	
  preCond->invert(r,A,aux);   		// initial residual
  
  preCond->invert(aux,A,r);   
  preCond->ATMult(p,A,aux);			// initial search direction
  
  rTsq = dot(p,p);
  convergenceTest(0, x, r, bNorm, 0.0);
  
  
  for (iter=1; iter<=maxIter; ++iter)
  {
  preCond->AMult(h,A,p);
  preCond->invert(aux,A,h); 
  
  alpha = rTsq/dot(aux,aux);
  
  FORALL(x,i) x[i] += alpha * p[i];
  FORALL(r,i) r[i] -= alpha * aux[i];
  
  preCond->invert(aux,A,r); 
  preCond->ATMult(h,A,aux);
  
  rTsqM1 = rTsq;
  rTsq   = dot(h,h);
  
  if (convergenceTest(iter, x, r, bNorm, 0.0)) break;
  
  beta = rTsq / rTsqM1;
  FORALL(p,i) p[i] = h[i] + beta*p[i];
  }
  
  preCond->close(A,x,b);
  return iter <= maxIter;
  }
  */
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------

// -- 	CG with different preconditioners for preconditioning and 
//	iteration error estimation <r,Cr>

Bool LinSystem:: TestSolver1(Vector<Num>& x, int maxIter)
{
    int iter, i;
    Num alpha, beta, rr, rrM1, eAe;
    Real bNorm;
    int  dim = A->Dim();
    Vector<Num> p(dim), r(dim), aux(dim);

    preCond->initialize(A,x,b);
    bNorm = normRhs();
    
    A->Mult(aux,x);				// aux = A*x
    FORALL(r,i) r[i] = b[i]-aux[i];		// initial residual

    // -- 	the usual preconditioning for the cg:
    
    if      (Cmd.isSet("ccgPrec","jacobi")) A->DiagDiv(aux,r);
    else if (Cmd.isSet("ccgPrec","sgs")) 
    {
	A->Fm1(aux,r);
	A->DiagMult(aux,aux);
	A->FmT(aux,aux);
    }
    else  FORALL(r,i) aux[i] = r[i];		// preconditioned residual s

    FORALL(p,i) p[i] = aux[i];			// initial search direction

    rr = dot(r,aux);
    convergenceTest(0, x, r, bNorm, 0.0, 0.0);

    {
	preCond->invert(aux,A,r);	
	eAe = dot(r,aux);
    }
    

    for (iter=1; iter<=maxIter; ++iter)
    {
	A->Mult(aux,p);

	alpha = rr / dot(p,aux);
	FORALL(x,i) x[i] += alpha * p[i];
	FORALL(r,i) r[i] -= alpha * aux[i];

	if (convergenceTest(iter, x, r, bNorm, Abs(alpha*rr), Abs(eAe))) break;

	{
	    preCond->invert(aux,A,r);	
	    eAe = dot(r,aux);
	}

	// -- 	the usual preconditioning for the cg:

	if      (Cmd.isSet("ccgPrec","jacobi")) A->DiagDiv(aux,r);
	else if (Cmd.isSet("ccgPrec","sgs")) 
	{
	    A->Fm1(aux,r);
	    A->DiagMult(aux,aux);
	    A->FmT(aux,aux);
	}
	else  FORALL(r,i) aux[i] = r[i];


	rrM1 = rr;
	rr   = dot(aux,r);

	beta = rr / rrM1;
	FORALL(p,i) p[i] = aux[i] + beta*p[i];
    }

    preCond->close(A,x,b);
    return iter <= maxIter;
}
//-------------------------------------------------------------------------

// -- 	relaxation method with different preconditioners for relaxation
//	(=preconditioning) and iteration error estimation <r,Cr>


Bool LinSystem:: TestSolver2(Vector<Num>& x, int maxIter)
{
    int  i, iter;
    Real bNorm, dE=0.0, eAe;
    int  dim = A->Dim();
    Vector<Num> r(dim), aux(dim);

    preCond->initialize(A,x,b);
    bNorm = normRhs();

    for (iter=0; iter<=maxIter; ++iter)
    {
	A->Mult(aux,x);
	FORALL(r,i) r[i] = b[i] - aux[i];	// new residual

	Real omega = 1.0;
	Cmd.get("Omega",&omega);

	if      (Cmd.isSet("ccgPrec","jacobi")) A->DiagDiv(aux,r,omega);
	else if (Cmd.isSet("ccgPrec","richar")) FORALL(r,i) aux[i] = omega*r[i];
	else {
	    A->Fm1(aux,r,omega);
	    A->DiagMult(aux,aux);
	    A->FmT(aux,aux,omega);
	}
 
	FORALL(x,i) x[i] += aux[i]; 
	/*
	  A->Mult(h,aux);
	  dE = dot(aux,h);
	  preCond->invert(aux,A,r);		// aux is the precond. residual
	*/
	eAe = Abs(dot(aux,r));

	if (convergenceTest(iter, x, r, bNorm, dE, eAe))  break;

    }

    preCond->close(A,x,b);
    return iter <= maxIter;
}
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------


Bool LinSystem:: NonLinRelax(Vector<Num>& x, int maxIter)
{
    int  i, iter;
    Real ENormX, ENormE;
    Num  dummy;
    Vector<Num> r(A->Dim()), e(A->Dim());
    
    preCond->initialize(A,x,b);
    
    nonLinEnergyConvergence(0, 0.0, 0.0);
    
    for (iter=1; iter<=maxIter; ++iter)
    {
	preCond->AMult(e,A,x);
	FORALL(r,i) r[i] = b[i] - e[i];		// new residual
	
	preCond->invert(e,A,r);		
	FORALL(x,i) x[i] += e[i]; 
	
	preCond->AMult(r,A,e);
	ENormE = 0.5*Abs(cdot(r,e));
	
	compEnergy(x, &ENormX, &dummy); 
	
	if (nonLinEnergyConvergence(iter, ENormX, ENormE))  break;
    }
    
    preCond->close(A,x,b);
    return iter <= maxIter;
}
//-------------------------------------------------------------------------
   
   
Bool LinSystem:: nonLinEnergyConvergence(int iter, Real ENormX, Real ENormE)
{
    const char *format0 = "%20s %7s%8.3g %12s";
    const char *format = "\n%20d %15.3g %15.3g";
    
    static Real tol, ratioM1;
    Real ratio;
    
    if (iter==0) 					// solver-setup
    {  
	tol 	= 0.1 * extPrecFactor * sqrt(globalPrecision);
	ratioM1 = 1.0;
	
	if (infoLinSystem) 
	    cout << Form(format0, "iteration", "rel.Error < ", 
			   tol, "conv.rate") << "\t abs.Error";
	return False;
    }

    ratio = sqrt(quot(ENormE,ENormX));
   
    if (ratio < tol) 					// converged
    {
	if (infoLinSystem)
	    cout << Form(format, abs(iter), ratio, ratio/ratioM1) 
	         << "\t " << sqrt(ENormE) << "\n"; 
	
	if (Cmd.isTrue("writeIterations")) 
	  *infoFile << A->Dim() << "  " << iter << "\n";

	return True;
    }
    
    if (infoLinSystem)
	if (iter%infoLinSystem==0 || iter==1 || iter<0)
	  cout << Form(format, abs(iter), ratio, ratio/ratioM1)
	       << "\t " << sqrt(ENormE);
    
    ratioM1 = ratio;
    return False;
}


