!////////////////////////////////////////////////////// ! ! Class for solving linear systems using MKL version of Pardiso ! -> It is possible to construct the matrix using PETSc. This option is currently deactivated... deprecate?? ! !/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #include "Includes.h" #ifdef HAS_PETSC #include "petsc/finclude/petsc.h" #endif MODULE MKLPardisoSolverClass USE GenericLinSolverClass USE CSRMatrixClass , only: csrMat_t use PETScMatrixClass USE SMConstants use DGSEMClass use TimeIntegratorDefinitions use Utilities , only: AlmostEqual use StopwatchClass , only: Stopwatch use MPI_Process_Info , only: MPI_Process #ifdef HAS_PETSC use petsc #endif implicit none TYPE, EXTENDS(GenericLinSolver_t) :: MKLPardisoSolver_t type(csrMat_t) :: A ! Jacobian matrix type(csrMat_t), pointer :: ALU ! LU-Factorized Jacobian matrix type(PETSCMatrix_t) :: PETScA real(kind=RP), DIMENSION(:), ALLOCATABLE :: x ! Solution vector real(kind=RP), DIMENSION(:), ALLOCATABLE :: b ! Right hand side real(kind=RP) :: Ashift LOGICAL :: AIsPrealloc logical :: Variable_dt ! Is the time-step variable? !Variables for creating Jacobian in PETSc context: LOGICAL :: AIsPetsc = .false. !Variables directly related with mkl pardiso solver INTEGER :: mtype ! Matrix type. See construct INTEGER, ALLOCATABLE :: perm(:) INTEGER, POINTER :: Pardiso_iparm(:) => NULL() ! Parameters for mkl version of pardiso INTEGER(KIND=AddrInt), POINTER :: Pardiso_pt(:) => NULL() CONTAINS !Subroutines: PROCEDURE :: construct => ConstructMKLContext procedure :: ComputeAndFactorizeJacobian => MKL_ComputeAndFactorizeJacobian procedure :: ReFactorizeJacobian => MKL_ReFactorizeJacobian PROCEDURE :: solve procedure :: SolveLUDirect => MKL_SolveLUDirect procedure :: SetRHSValue => MKL_SetRHSValue procedure :: SetRHS => MKL_SetRHS PROCEDURE :: GetXValue => MKL_GetXValue PROCEDURE :: GetX => MKL_GetX PROCEDURE :: destroy => MKL_destroy PROCEDURE :: SetOperatorDt PROCEDURE :: ReSetOperatorDt procedure :: ComputeJacobianMKL procedure :: FactorizeJacobian => MKL_FactorizeJacobian procedure :: SetJacobian => MKL_SetJacobian !Functions: PROCEDURE :: Getxnorm => MKL_GetXnorm !Get solution norm PROCEDURE :: Getrnorm !Get residual norm END TYPE MKLPardisoSolver_t private public MKLPardisoSolver_t, GenericLinSolver_t ! ! Useful interfaces ! ----------------- interface subroutine pardisoinit(pt, mtype, iparm) use SMConstants implicit none integer(kind=AddrInt) :: pt(*) integer :: mtype integer :: iparm(*) end subroutine pardisoinit subroutine pardiso(pt, maxfct, mnum, mtype, phase, n, & values, rows, cols, perm, nrhs, iparm, msglvl, b, x, ierror) use SMConstants real(kind=RP) :: values(*), b(*), x(*) integer(kind=AddrInt) :: pt(*) integer :: perm(*), nrhs, iparm(*), msglvl, ierror integer :: maxfct, mnum, mtype, phase, n, rows(*), cols(*) end subroutine pardiso end interface !======== CONTAINS !======== ! !/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ! ! ----------------------- ! MKL pardiso constructor ! ----------------------- subroutine ConstructMKLContext(this,DimPrb, globalDimPrb, nEqn,controlVariables,sem,MatrixShiftFunc) implicit none !----------------------------------------------------------- class(MKLPardisoSolver_t), intent(inout), TARGET :: this integer , intent(in) :: DimPrb integer , intent(in) :: globalDimPrb integer , intent(in) :: nEqn TYPE(FTValueDictionary) , intent(in), OPTIONAL :: controlVariables TYPE(DGSem), TARGET , OPTIONAL :: sem procedure(MatrixShift_FCN) :: MatrixShiftFunc !----------------------------------------------------------- #ifdef HAS_PETSC PetscErrorCode :: ierr #endif !----------------------------------------------------------- call this % GenericLinSolver_t % construct(DimPrb, globalDimPrb, nEqn,controlVariables,sem,MatrixShiftFunc) if (MPI_Process % doMPIRootAction) then error stop 'MKLPardisoSolver_t cannot be used as a distributed solver' !TODO: Implement cluster_sparse_solver (MKL) or use the actual pardiso solver (http://www.pardiso-project.org/) end if if ( present(sem) ) then this % p_sem => sem end if MatrixShift => MatrixShiftFunc this % DimPrb = DimPrb allocate(this % x(DimPrb)) allocate(this % b(DimPrb)) this % mtype = 11 !Set matrix type to real unsymmetric (change?) ALLOCATE(this % Pardiso_pt(64)) ALLOCATE(this % Pardiso_iparm(64)) ALLOCATE(this % perm(DimPrb)) this % perm = 0 if(this % AIsPetsc) then #ifdef HAS_PETSC call PetscInitialize(PETSC_NULL_CHARACTER,ierr) #else error stop "MKL-Pardiso needs PETSc for constructung the Jacobian Matrix" #endif call this % PETScA % construct (num_of_Rows = DimPrb, withMPI = .FALSE.) else call this % A % construct(num_of_Rows = DimPrb, withMPI = .false.) end if ! ! Configure the Jacobian (this includes matrix preallocation -if needed) ! ---------------------------------------------------------------------- if ( present(sem) ) then call this % Jacobian % Configure (sem % mesh, nEqn, this % A) end if #ifdef HAS_MKL call pardisoinit(this % Pardiso_pt, this % mtype, this % Pardiso_iparm) #else error stop 'MKL not linked correctly' #endif ! ! Set variables from input file ! ----------------------------- ! if ( present(controlVariables) ) then ! ! See if the time-step is constant ! If it's not, the factorized matrix cannot be stored in the matrix structure ! ------------------------------------------------------------------------------ if ( controlVariables % containsKey("dt") ) then this % Variable_dt = .FALSE. this % ALU => this % A else this % Variable_dt = .TRUE. allocate (this % ALU) end if else this % Variable_dt = .TRUE. allocate (this % ALU) end if call Stopwatch % CreateNewEvent("Sparse LU-Factorization") end subroutine ConstructMKLContext ! !/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ! subroutine MKL_SetRHSValue(this, irow, value) implicit none !----------------------------------------------------------- class(MKLPardisoSolver_t), intent(inout) :: this INTEGER , intent(in) :: irow real(kind=RP) , intent(in) :: value !----------------------------------------------------------- this % b (irow) = value end subroutine MKL_SetRHSValue ! !/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ! subroutine MKL_SetRHSValues(this, nvalues, irow, values) class(MKLPardisoSolver_t) , intent(inout) :: this INTEGER , intent(in) :: nvalues INTEGER , DIMENSION(1:), intent(in) :: irow real(kind=RP), DIMENSION(1:), intent(in) :: values !------------------------------------------------------ integer :: i do i=1, nvalues if (irow(i)<0) cycle this % b(irow(i)) = values(i) end do end subroutine MKL_SetRHSValues ! !/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ! subroutine MKL_SetRHS(this, RHS) implicit none !-arguments----------------------------------------------------------- class(MKLPardisoSolver_t), intent(inout) :: this real(kind=RP) , intent(in) :: RHS(this % DimPrb) !--------------------------------------------------------------------- this % b = RHS end subroutine MKL_SetRHS ! !/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ! subroutine solve(this, nEqn, nGradEqn, ComputeTimeDerivative,tol,maxiter,time,dt,ComputeA) implicit none ! ! ---------------------------------------------------- ! Main subroutine for solving system using mkl pardiso ! ---------------------------------------------------- ! !----------------------------------------------------------- class(MKLPardisoSolver_t), target, intent(inout) :: this integer, intent(in) :: nEqn integer, intent(in) :: nGradEqn procedure(ComputeTimeDerivative_f) :: ComputeTimeDerivative real(kind=RP), OPTIONAL :: tol INTEGER , OPTIONAL :: maxiter real(kind=RP), OPTIONAL :: time real(kind=RP), OPTIONAL :: dt logical , optional , intent(inout) :: ComputeA !----------------------------------------------------------- #ifdef HAS_MKL integer :: error !----------------------------------------------------------- ! ! Compute Jacobian matrix if needed ! (done in petsc format and then transformed to CSR since the CSR cannot be filled by the Jacobian calculators) ! ----------------------------------------------------- if ( present(ComputeA)) then if (ComputeA) then call this % ComputeJacobianMKL(dt,time,nEqn,nGradEqn,ComputeTimeDerivative) ComputeA = .FALSE. end if else call this % ComputeJacobianMKL(dt,time,nEqn,nGradEqn,ComputeTimeDerivative) end if Write(*,*), "Visualizing matrix in MKL paradisoclass.f90. Remember to delete it" call this % A % Visualize('Jacobian.txt') Write(*,*), "Is symmetric?" WRITE(*,*) "A si symmetric ?", this % A % IsSymmetric() call this % SolveLUDirect(error) if (error .NE. 0) THEN WRITE(*,*) 'MKL Pardiso ERROR:', error error stop else this % converged = .TRUE. end if #else error stop 'MKL is not linked properly' #endif end subroutine solve ! !/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ! subroutine ComputeJacobianMKL(this,dt,time,nEqn,nGradEqn,ComputeTimeDerivative) use DenseBlockDiagonalMatrixClass implicit none !----------------------------------------------------------- class(MKLPardisoSolver_t), intent(inout) :: this real(kind=RP), intent(in) :: dt real(kind=RP), intent(in) :: time integer, intent(in) :: nEqn integer, intent(in) :: nGradEqn procedure(ComputeTimeDerivative_f) :: ComputeTimeDerivative !----------------------------------------------------------- type(csrMat_t) :: B, Cmat !debug if (this % AIsPetsc) then call this % Jacobian % Compute (this % p_sem, nEqn, time, this % PETScA, ComputeTimeDerivative, ComputeTimeDerivative) call this % PETScA % GetCSRMatrix(this % A) call this % SetOperatorDt(dt) this % AIsPetsc = .FALSE. call this % PETScA % destruct else call this % Jacobian % Compute (this % p_sem, nEqn, time, this % A, ComputeTimeDerivative, ComputeTimeDerivative) !~ !<debug !~ call this % A % Visualize('AnJac_visu.dat') !~ !------------ !~ this % JacobianComputation = NUMERICAL_JACOBIAN !~ call B % construct(num_of_Rows = this % DimPrb, withMPI = .false.) !~ call this % ComputeJacobian(B,time,nEqn,nGradEqn,ComputeTimeDerivative) !~ call B % Visualize('NumJac_visu.dat') !~ !------------ !~ call this % A % MatAdd(B, Cmat,-1._RP) !~ print*, 'Error(L2) = ', norm2( (Cmat % Values) ) !~ print*, 'Error(inf) = ', maxval( abs(Cmat % Values) ) !~ print*, ' pos = ', maxloc( abs(Cmat % Values) ), Cmat % Cols(maxloc( abs(Cmat % Values) )) !~ call Cmat % Visualize('C_visu.dat') !~ stop !~ !debug> call this % SetOperatorDt(dt) end if call this % FactorizeJacobian end subroutine ComputeJacobianMKL ! !/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ! subroutine MKL_GetXValue(this,irow,x_i) implicit none !----------------------------------------------------------- class(MKLPardisoSolver_t), intent(inout) :: this INTEGER , intent(in) :: irow real(kind=RP) , INTENT(OUT) :: x_i !----------------------------------------------------------- x_i = this % x(irow) end subroutine MKL_GetXValue ! !/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ! function MKL_GetX(this) result(x) IMPLICIT NONE !----------------------------------------------------------- CLASS(MKLPardisoSolver_t), INTENT(INOUT) :: this REAL(KIND=RP) :: x(this % DimPrb) !----------------------------------------------------------- x = this % x end function MKL_GetX ! !/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ! subroutine MKL_destroy(this) implicit none !----------------------------------------------------------- class(MKLPardisoSolver_t), intent(inout) :: this !----------------------------------------------------------- call this % A % destruct DEALLOCATE(this % b) DEALLOCATE(this % x) DEALLOCATE(this % Pardiso_pt) DEALLOCATE(this % Pardiso_iparm) DEALLOCATE(this % perm) if (this % Variable_dt) then call this % ALU % destruct deallocate (this % ALU) else nullify (this % ALU) end if this % AIsPrealloc = .FALSE. end subroutine MKL_destroy ! !/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ! subroutine SetOperatorDt(this,dt) implicit none !----------------------------------------------------------- class(MKLPardisoSolver_t), intent(inout) :: this real(kind=RP) , intent(in) :: dt !----------------------------------------------------------- this % Ashift = MatrixShift(dt) if (this % AIsPetsc) THEN call this % PETScA % shift(this % Ashift) else call this % A % Shift(this % Ashift) end if end subroutine SetOperatorDt ! !/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ! subroutine ReSetOperatorDt(this,dt) implicit none !----------------------------------------------------------- class(MKLPardisoSolver_t), intent(inout) :: this real(kind=RP) , intent(in) :: dt !----------------------------------------------------------- real(kind=RP) :: shift !----------------------------------------------------------- shift = MatrixShift(dt) if ( AlmostEqual(shift,this % Ashift) ) return if (this % AIsPetsc) THEN call this % PETScA % shift(shift) else call this % A % Shift(-this % Ashift) call this % A % Shift(shift) end if this % Ashift = shift call this % FactorizeJacobian end subroutine ReSetOperatorDt ! !/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ! function MKL_GetXnorm(this,TypeOfNorm) result(xnorm) implicit none !----------------------------------------------------------- class(MKLPardisoSolver_t), intent(inout) :: this CHARACTER(len=*) :: TypeOfNorm real(kind=RP) :: xnorm !----------------------------------------------------------- select case (TypeOfNorm) CASE ('infinity') xnorm = MAXVAL(ABS(this % x)) CASE ('l2') xnorm = NORM2(this % x) CASE DEFAULT stop 'MKLPardisoSolverClass ERROR: Norm not implemented yet' end select end function MKL_GetXnorm ! !/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ! function Getrnorm(this) result(rnorm) implicit none ! ! ---------------------------------------- ! Currently implemented with infinity norm ! ---------------------------------------- ! !----------------------------------------------------------- class(MKLPardisoSolver_t), intent(inout) :: this real(kind=RP) :: rnorm !----------------------------------------------------------- real(kind=RP) :: residual(this % DimPrb) !----------------------------------------------------------- residual = this % b - this % A % MatVecMul (this % x) rnorm = MAXVAL(ABS(residual)) !rnorm = NORM2(this % x) end function Getrnorm ! !/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ! subroutine MKL_ComputeAndFactorizeJacobian(self,nEqn, nGradEqn, F_J, dt, eps, mode_in) ! ! ************************************************************************************* ! This subroutine performs the following: ! -> Construct the numerical Jacobian of the function F_J, with perturbation eps. ! -> Shifts the Jacobian -dt * I. ! -> Converts the Jacobian to CSR. ! -> Factorizes the Jacobian (jobs 12 in Pardiso). ! ! ************************************************************************************* ! implicit none class(MKLPardisoSolver_t), intent(inout) :: self integer, intent(in) :: nEqn, nGradEqn procedure(ComputeTimeDerivative_f) :: F_J real(kind=RP), intent(in) :: dt real(kind=RP), intent(in) :: eps integer, intent(in) :: mode_in ! ! Compute numerical Jacobian in the PETSc matrix ! ---------------------------------------------- if ( self % AIsPetsc) then call self % Jacobian % Compute (self % p_sem, nEqn, 0._RP, self % PETScA, F_J, eps_in = eps) ! ! Transform the Jacobian to CSRMatrix ! ----------------------------------- call self % PETScA % GetCSRMatrix(self % A) call self % SetOperatorDt(dt) ! ! Correct the shifted Jacobian values ! ----------------------------------- self % A % values = -dt * self % A % values else call self % Jacobian % Compute (self % p_sem, nEqn, 0._RP, self % A, F_J, F_J, eps, .false., mode_in) call self % SetOperatorDt(dt) self % A % values = -dt * self % A % values end if call self % FactorizeJacobian end subroutine MKL_ComputeAndFactorizeJacobian subroutine MKL_ReFactorizeJacobian(self) ! ! ************************************************************************************* ! This subroutine changes the gamma0 coefficient from 1.0 to 1.5 by ! adding 0.5 to the diagonal. ! ************************************************************************************* ! implicit none class(MKLPardisoSolver_t), intent(inout) :: self ! ! --------------- ! Local variables ! --------------- ! integer :: error ! ! Shift the Jacobian ! ------------------ self % A % values(self % A % diag) = self % A % values(self % A % diag) + 0.5_RP ! ! Perform the factorization ! ------------------------- #ifdef HAS_MKL call pardiso(self % Pardiso_pt, 1, 1, self % mtype, 12, self % ALU % num_of_Rows, self % ALU % values, & self % ALU % rows, self % ALU % cols, self % perm, 1, self % Pardiso_iparm, 0, & self % b, self % x, error) #else error stop 'MKL not linked correctly' #endif end subroutine MKL_ReFactorizeJacobian ! !/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ! subroutine MKL_FactorizeJacobian(self) implicit none !-arguments--------------------------------------------------- class(MKLPardisoSolver_t), intent(inout) :: self !-local-variables--------------------------------------------- integer :: error !------------------------------------------------------------- call Stopwatch % Start("Sparse LU-Factorization") if (self % Variable_dt) then call self % ALU % destruct call self % ALU % constructWithCSRArrays (self % A % Rows, & self % A % Cols, & self % A % Values) end if ! ! Perform the factorization ! ------------------------- #ifdef HAS_MKL call pardiso(self % Pardiso_pt, 1, 1, self % mtype, 12, self % ALU % num_of_Rows, self % ALU % values, & self % ALU % rows, self % ALU % cols, self % perm, 1, self % Pardiso_iparm, 0, & self % b, self % x, error) #else error stop 'MKL not linked correctly' #endif call Stopwatch % Pause("Sparse LU-Factorization") end subroutine MKL_FactorizeJacobian ! !/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ! subroutine MKL_SolveLUDirect(self,error_out) implicit none class(MKLPardisoSolver_t), intent(inout) :: self integer, optional , intent(out) :: error_out ! ! --------------- ! Local variables ! --------------- ! integer :: error #ifdef HAS_MKL call pardiso(self % Pardiso_pt, 1, 1, self % mtype, 33, self % ALU % num_of_Rows, self % ALU % values, & self % ALU % rows, self % ALU % cols, self % perm, 1, self % Pardiso_iparm, 0, & self % b, self % x, error) if ( present(error_out) ) then error_out = error end if #else error stop 'MKL not linked correctly' #endif end subroutine MKL_SolveLUDirect ! !/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ! subroutine MKL_SetJacobian(this,Matrix) implicit none !-arguments----------------------------------------------------------- class(MKLPardisoSolver_t), intent(inout) :: this class(Matrix_t) , intent(in) :: Matrix !--------------------------------------------------------------------- select type(Matrix) class is(csrMat_t) this % A = Matrix class default error stop 'MKL_SetJacobian :: Wrong matrix type' end select end subroutine MKL_SetJacobian END MODULE MKLPardisoSolverClass