/*!\file PetscMat.cpp
 * \brief: implementation of the PetscMat object
 */

/*Headers:*/
/*{{{*/
#ifdef HAVE_CONFIG_H
	#include <config.h>
#else
#error "Cannot compile with HAVE_CONFIG_H symbol! run configure first!"
#endif

#include <stdio.h>
#include <string.h>
#include "../petscincludes.h"
#include "../../../shared/shared.h"

/*}}}*/

/*PetscMat constructors and destructor*/
/*FUNCTION PetscMat::PetscMat(){{{*/
PetscMat::PetscMat(){
	this->matrix=NULL;
	#ifdef _HAVE_ADOLC_
	this->amatrix=NULL;
	#endif

}
/*}}}*/
/*FUNCTION PetscMat::PetscMat(int M,int N){{{*/
PetscMat::PetscMat(int M,int N){

	this->matrix=NewMat(M,N,IssmComm::GetComm());
}
/*}}}*/
/*FUNCTION PetscMat::PetscMat(int M,int N, IssmDouble sparsity){{{*/
PetscMat::PetscMat(int M,int N, IssmDouble sparsity){

	this->matrix=NewMat(M,N,sparsity,IssmComm::GetComm());
}
/*}}}*/
/*FUNCTION PetscMat::PetscMat(int m,int n,int M,int N,int* d_nnz,int* o_nnz){{{*/
PetscMat::PetscMat(int m,int n,int M,int N,int* d_nnz,int* o_nnz){

	MatCreate(IssmComm::GetComm(),&this->matrix);
	MatSetSizes(this->matrix,m,n,M,N);
	MatSetFromOptions(this->matrix);
	MatMPIAIJSetPreallocation(this->matrix,0,d_nnz,0,o_nnz);
//	MatSetOption(this->matrix,MAT_NEW_NONZERO_ALLOCATION_ERR,PETSC_FALSE);

}
/*}}}*/
/*FUNCTION PetscMat::PetscMat(IssmDouble* serial_mat,int M,int N,IssmDouble sparsity){{{*/
PetscMat::PetscMat(IssmDouble* serial_mat,int M,int N,IssmDouble sparsity){

	int     i;
	int* idxm=NULL;
	int* idxn=NULL;

	if(M)idxm=xNew<int>(M);
	if(N)idxn=xNew<int>(N);

	for(i=0;i<M;i++)idxm[i]=i;
	for(i=0;i<N;i++)idxn[i]=i;

	this->matrix=NewMat(M,N,sparsity,IssmComm::GetComm());
	MatSetValues(this->matrix,M,idxm,N,idxn,serial_mat,INSERT_VALUES);
	MatAssemblyBegin(this->matrix,MAT_FINAL_ASSEMBLY);
	MatAssemblyEnd(this->matrix,MAT_FINAL_ASSEMBLY);

	xDelete<int>(idxm);
	xDelete<int>(idxn);

}
/*}}}*/
/*FUNCTION PetscMat::PetscMat(int M,int N, int connectivity, int numberofdofspernode){{{*/
PetscMat::PetscMat(int M,int N, int connectivity,int numberofdofspernode){

	this->matrix=NewMat(M,N,connectivity,numberofdofspernode,IssmComm::GetComm());

}
/*}}}*/
/*FUNCTION PetscMat::~PetscMat(){{{*/
PetscMat::~PetscMat(){
	MatFree(&this->matrix);
}
/*}}}*/

/*PetscMat specific routines: */
/*FUNCTION PetscMat::AllocationInfo{{{*/
void PetscMat::AllocationInfo(void){

	MatInfo info;
	MatGetInfo(this->matrix,MAT_GLOBAL_SUM,&info);
	_printf0_("=========================== Stiffness matrix allocation info ===========================\n");
	_printf0_("\n");
   _printf0_(" Block size  : "<<info.block_size << "\n");
	_printf0_(" nz_allocated: "<<info.nz_allocated << "\n");
	_printf0_(" nz_used     : "<<info.nz_used << "\n");
	_printf0_(" nz_unneeded : "<<info.nz_unneeded<<" ("<<double(info.nz_unneeded)/double(info.nz_allocated)*100.<<"%)\n");
	_printf0_("\n");
	_printf0_("========================================================================================\n");
}
/*}}}*/
/*FUNCTION PetscMat::Echo{{{*/
void PetscMat::Echo(void){

	MatView(this->matrix,PETSC_VIEWER_STDOUT_WORLD);
}
/*}}}*/
/*FUNCTION PetscMat::Assemble{{{*/
void PetscMat::Assemble(void){

	_assert_(this->matrix);
	MatAssemblyBegin(this->matrix,MAT_FINAL_ASSEMBLY);
	MatAssemblyEnd(this->matrix,MAT_FINAL_ASSEMBLY);

}
/*}}}*/
/*FUNCTION PetscMat::Norm{{{*/
IssmDouble PetscMat::Norm(NormMode mode){

	IssmDouble norm=0;
	_assert_(this->matrix);
	MatNorm(this->matrix,ISSMToPetscNormMode(mode),&norm);

	return norm;

}
/*}}}*/
/*FUNCTION PetscMat::GetSize{{{*/
void PetscMat::GetSize(int* pM,int* pN){

	_assert_(this->matrix);
	MatGetSize(this->matrix,pM,pN);
}
/*}}}*/
/*FUNCTION PetscMat::GetLocalSize{{{*/
void PetscMat::GetLocalSize(int* pM,int* pN){

	_assert_(this->matrix);
	MatGetLocalSize(this->matrix,pM,pN);

}
/*}}}*/
/*FUNCTION PetscMat::MatMult{{{*/
void PetscMat::MatMult(PetscVec* X,PetscVec* AX){

	_assert_(this->matrix);
	_assert_(X->vector);
	MatMultPatch(this->matrix,X->vector,AX->vector,IssmComm::GetComm());

}
/*}}}*/
/*FUNCTION PetscMat::Duplicate{{{*/
PetscMat* PetscMat::Duplicate(void){

	PetscMat* output=new PetscMat();
	_assert_(this->matrix);
	MatDuplicate(this->matrix,MAT_COPY_VALUES,&output->matrix);

	return output;

}
/*}}}*/
/*FUNCTION PetscMat::ToSerial{{{*/
IssmDouble* PetscMat::ToSerial(void){

	 IssmDouble* output=NULL;

	 MatToSerial(&output,this->matrix,IssmComm::GetComm());
	 return output;

}
/*}}}*/
/*FUNCTION PetscMat::SetValues{{{*/
void PetscMat::SetValues(int m,int* idxm,int n,int* idxn,IssmDouble* values,InsMode mode){

	PetscErrorCode ierr;
	ierr = MatSetValues(this->matrix,m,idxm,n,idxn,values,ISSMToPetscInsertMode(mode));
	if(ierr) _error_("PETSC's MatSetValues reported an error");

}
/*}}}*/
/*FUNCTION PetscMat::Convert{{{*/
void PetscMat::Convert(MatrixType type){

	MatConvert(this->matrix,ISSMToPetscMatrixType(type),MAT_REUSE_MATRIX,&this->matrix);

}
/*}}}*/
