!
! This file is part of SACAMOS, State of the Art CAble MOdels for Spice.
! It was developed by the University of Nottingham and the Netherlands Aerospace
! Centre (NLR) for ESA under contract number 4000112765/14/NL/HK.
!
! Copyright (C) 2016-2018 University of Nottingham
!
! SACAMOS 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 3 of the License, or (at your option) any later
! version.
!
! SACAMOS 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.
!
! A copy of the GNU General Public License version 3 can be found in the
! file GNU_GPL_v3 in the root or at .
!
! SACAMOS uses the EISPACK library (in /SRC/EISPACK). EISPACK is subject to
! the GNU Lesser General Public License. A copy of the GNU Lesser General Public
! License version can be found in the file GNU_LGPL in the root of EISPACK
! (/SRC/EISPACK ) or at .
!
! The University of Nottingham can be contacted at: ggiemr@nottingham.ac.uk
!
!
! File Contents:
! SUBROUTINE modal_decomposition_global
! SUBROUTINE modal_decomposition_global_ZY
! SUBROUTINE calc_eigenvectors
! SUBROUTINE test_decomposition
!
! NAME
! modal_decomposition_global
!
! AUTHORS
! Chris Smartt
!
! DESCRIPTION
! This subroutine calculates the modal decomposition for the global YZ product
! The theory is in the Theory Manual_Section 3.3 however there is more detailed discussion in
! C. R. Paul,'Analasys of Multiconductor transmission lines,'
!
! COMMENTS
! In practice we operate on the domain based YZ product as it seems to be numerically
! better in the case of degenerate eigenvalues, then we transform the eigenvector matrix
! with the domain decomposition matrix MII to give TI. The eigenvaluse are unchanged.
! i.e.(V_domain)=[MV] (V_global), (I_domain)=[MI] (I_global)
! (V_domain)=[Z_domain] (I_domain), (I_domain)=[Y_domain](V_domain)
! (V_global)=[Z_global] (I_global), (I_global)=[Y_global](V_global)
! if [TI] diagonalises [Y_global][Z_global] i.e. [Y_global][Z_global]=[TI][gammasqr][TI^-1]
! and [MPI]diagonalises [Y_domain][Z_domain] i.e. [Y_domain][Z_domain] = [MPI][gammasqr][MPI^-1] then
! [TI]=[MI^-1][MPI]
!
! There were originally problems if the system was highly degenerate....
! This problem has been fudged by applying a small perturbation to the diagonal elements of
! the YZ product. Maybe we need to assess the impact of this on mode identificaton for
! the propagation correction?
!
! HISTORY
!
! started 7/12/2015 CJS: STAGE_1 developments
! April 2016 CJS. Use impedance and admittance matrices for frequency dependent model
! May 2016 CJS. Attempt to improve the reliability of the solution for degenerate modes (problem with spacewire model)
! 8/5/2017 CJS: Include references to Theory_Manual
!
SUBROUTINE modal_decomposition_global(dim,Z_domain,Y_domain,MV,MVI,MI,MII, &
Y,Z,TI,TII,TV,TVI,GAMMA_C,GAMMA_SQR,gamma_r,D,sqrtDI,ZC,YC,Zm,Ym,Zmd,Ymd)
USE type_specifications
USE general_module
USE constants
USE eispack
USE maths
IMPLICIT NONE
! variables passed to the subroutine
integer,intent(IN) :: dim ! dimension of matrix system
complex(dp),intent(IN) :: Z_domain(dim,dim) ! domain based impedance matrix
complex(dp),intent(IN) :: Y_domain(dim,dim) ! domain based admittance matrix
complex(dp),intent(IN) :: MV(dim,dim) ! domain voltage decomposition matrix
complex(dp),intent(IN) :: MVI(dim,dim) ! inverse domain voltage decomposition matrix
complex(dp),intent(IN) :: MI(dim,dim) ! domain current decomposition matrix
complex(dp),intent(IN) :: MII(dim,dim) ! inverse domain current decomposition matrix
complex(dp) :: Z(dim,dim) ! glabal based impedance matrix
complex(dp) :: Y(dim,dim) ! glabal based admittance matrix
complex(dp) :: GAMMA_SQR(dim) ! diagonal matrix elements in YZ/ ZY diagonalisation
complex(dp) :: TV(dim,dim) ! modal decomposition matrix [Z][Y]=[TV][GAMMA_SQR][TVI]
complex(dp) :: TVI(dim,dim) ! inverse modal decomposition matrix
complex(dp) :: TI(dim,dim) ! modal decomposition matrix [Y][Z]=[TI][GAMMA_SQR][TII]
complex(dp) :: TII(dim,dim) ! inverse modal decomposition matrix
complex(dp) :: Zm(dim,dim) ! modal characteristic impedance matrix
complex(dp) :: Ym(dim,dim) ! modal characteristic admittance matrix
complex(dp) :: Zmd(dim) ! modal characteristic impedance list
complex(dp) :: Ymd(dim) ! modal characteristic impedance list
complex(dp) :: GAMMA_C(dim) ! complex square root of GAMMA_SQR
real(dp) :: gamma_r(dim) ! real part of the complex square root of GAMMA_SQR
complex(dp) :: D(dim,dim) ! full matrix with GAMMA_C elements on the diagonal
complex(dp) :: sqrtDI(dim,dim) ! full matrix with 1/sqrt(GAMMA_C) elements on the diagonal
complex(dp) :: ZC(dim,dim) ! Characteristic impedance matrix
complex(dp) :: YC(dim,dim) ! Characteristic admittance matrix
! local variables
complex(dp) :: YZ(dim,dim) ! YZ matrix product to be diagonalised
! temposrary matrices used in the calculations
complex(dp) :: T1(dim,dim)
complex(dp) :: T2(dim,dim)
complex(dp) :: YDZD(dim,dim)
complex(dp) :: MPI(dim,dim)
complex(dp) :: TM1(dim,dim)
real(dp) :: condition_number ! matrix condition number
! loop variables
integer :: row,col
! error code for matrix inverse calculation
integer :: ierr
! START
! Calculate the product ofthe transmission line impedance (Z) and admittance (Y) matrices
if(verbose) write(*,*)'CALLED modal_decomposition_global'
YZ=matmul(Y,Z)
YDZD=matmul(Y_domain,Z_domain)
! perform a modal decomposition on the YZ product Theory_Manual_Eqn 2.36
! using eispack routines to calculate the eigenvalues and eigenvectors of the YZ
! Perturb the diagonal elements very slightly to eliminate the
! problem with finding all the eigenvectors corresponding to degenerate eigenvalues.
! The perturbation is very small compared with the approximations applied to obtain the
! original L and C matrices... See Theory_Manual_Section 2.3.1
do row=1,dim
YDZD(row,row)=YDZD(row,row)*(1d0+1D-8*row)
end do
! calculate the eigenvectors and eigenvalues of YDZD
CALL calc_eigenvectors(YDZD,GAMMA_SQR,MPI,dim)
! Calculate the modal decomposition matrix for the global based currents.
TI=matmul(MII,MPI)
GAMMA_C(:)=sqrt(GAMMA_SQR(:))
if(verbose) write(*,*)'Invert TI'
ierr=1 ! set ierr=1 on input to matrix inverse to cause the program to return here if we have a singular matrix
CALL cinvert_Gauss_Jordan(TI,dim,TII,dim,ierr)
if (ierr.NE.0) then
! we have a singular matrix here
run_status='ERROR in modal_decomposition_global. Singular matrix'
CALL write_program_status()
STOP 1
end if
if(verbose) write(*,*)'Done: Invert TI'
GAMMA_SQR(:)=GAMMA_C(:)*GAMMA_C
gamma_r(:)=sqrt(-dble(GAMMA_SQR(:))) ! real part of gamma
! get the diagonal matrix with diagonal elements gamma, and its inverse
D(:,:)=0d0
sqrtDI(:,:)=0d0
do row=1,dim
D(row,row)=GAMMA_C(row)*GAMMA_C(row)
sqrtDI(row,row)=1d0/GAMMA_C(row)
end do
! Calculate the voltage transformation matrices based on Theory_Manual_Eqn 2.37
! noting that the transpose of the inverse of a matrix is equal to the inverse of the transpose of a matrix
TV=transpose(TII)
if(verbose) write(*,*)'Invert TV'
ierr=1 ! set ierr=1 on input to matrix inverse to cause the program to return here if we have a singular matrix
CALL cinvert_Gauss_Jordan(TV,dim,TVI,dim,ierr)
if (ierr.NE.0) then
! we have a singular matrix here
run_status='ERROR in modal_decomposition_global. Singular matrix'
CALL write_program_status()
STOP 1
end if
if(verbose) write(*,*)'Done: Invert TV'
! Calculate the characteristic impedance matrix Theory_Manual_Eqn 2.41
T1=matmul(sqrtDI,TII)
T2=matmul(TI,T1)
ZC=matmul(Z,T2)
if(verbose) write(*,*)'Invert ZC'
ierr=1 ! set ierr=1 on input to matrix inverse to cause the program to return here if we have a singular matrix
CALL cinvert_Gauss_Jordan(ZC,dim,YC,dim,ierr)
if (ierr.NE.0) then
! we have a singular matrix here
run_status='ERROR in modal_decomposition_global. Singular matrix'
CALL write_program_status()
STOP 1
end if
if(verbose) write(*,*)'Done: Invert ZC'
! Calculate the modal impedance matrix
T1=matmul(Z,TI)
Zm=matmul(TVI,T1)
! Calculate the modal admittance matrix
T1=matmul(Y,TV)
Ym=matmul(TII,T1)
! Calculate Modal impedance
do row=1,dim
Zmd(row)=Zm(row,row)
Ymd(row)=Ym(row,row)
end do
if (verbose) then
write(*,*)'Modal decomposition of Global matrix YZ:'
CALL write_cmatrix_re(YZ,dim,0)
do col=1,dim
write(*,*)
write(*,*)'Modal decomposition: Mode number',col
write(*,*)
write(*,*)'Eigenvalue',GAMMA_SQR(col)
write(*,*)
write(*,*)'Eigenvector'
do row=1,dim
write(*,*)row,TI(row,col)
end do
end do
CALL c_condition_number(TI,dim,condition_number,dim)
write(*,*)'Condition number of TI matrix =',condition_number
CALL test_decomposition(YZ,GAMMA_SQR,TI,dim)
end if
RETURN
END SUBROUTINE modal_decomposition_global
!
! NAME
! modal_decomposition_global_ZY
!
! AUTHORS
! Chris Smartt
!
! DESCRIPTION
! This subroutine calculates the modal decomposition for the global ZY product
!
! COMMENTS
! Adapted from modal_decomposition_global
!
! HISTORY
!
! started 7/12/2015 CJS: STAGE_1 developments
! April 2016 CJS. Use impedance and admittance matrices for frequency dependent model
! May 2016 CJS. Attempt to improve the reliability of the solution for degenerate modes (problem with spacewire model)
! 21/4/2017 CJS Adapted from modal_decomposition_global
!
SUBROUTINE modal_decomposition_global_ZY(dim,Z_domain,Y_domain,MV,MVI,MI,MII, &
Y,Z,TI,TII,TV,TVI,GAMMA_C,GAMMA_SQR,gamma_r,D,sqrtDI,ZC,YC,Zm,Ym,Zmd,Ymd)
USE type_specifications
USE general_module
USE constants
USE eispack
USE maths
IMPLICIT NONE
! variables passed to the subroutine
integer,intent(IN) :: dim ! dimension of matrix system
complex(dp),intent(IN) :: Z_domain(dim,dim) ! domain based impedance matrix
complex(dp),intent(IN) :: Y_domain(dim,dim) ! domain based admittance matrix
complex(dp),intent(IN) :: MV(dim,dim) ! domain voltage decomposition matrix
complex(dp),intent(IN) :: MVI(dim,dim) ! inverse domain voltage decomposition matrix
complex(dp),intent(IN) :: MI(dim,dim) ! domain current decomposition matrix
complex(dp),intent(IN) :: MII(dim,dim) ! inverse domain current decomposition matrix
complex(dp) :: Z(dim,dim) ! glabal based impedance matrix
complex(dp) :: Y(dim,dim) ! glabal based admittance matrix
complex(dp) :: GAMMA_SQR(dim) ! diagonal matrix elements in YZ/ ZY diagonalisation
complex(dp) :: TV(dim,dim) ! modal decomposition matrix [Z][Y]=[TV][GAMMA_SQR][TVI]
complex(dp) :: TVI(dim,dim) ! inverse modal decomposition matrix
complex(dp) :: TI(dim,dim) ! modal decomposition matrix [Y][Z]=[TI][GAMMA_SQR][TII]
complex(dp) :: TII(dim,dim) ! inverse modal decomposition matrix
complex(dp) :: Zm(dim,dim) ! modal characteristic impedance matrix
complex(dp) :: Ym(dim,dim) ! modal characteristic admittance matrix
complex(dp) :: Zmd(dim) ! modal characteristic impedance list
complex(dp) :: Ymd(dim) ! modal characteristic impedance list
complex(dp) :: GAMMA_C(dim) ! complex square root of GAMMA_SQR
real(dp) :: gamma_r(dim) ! real part of the complex square root of GAMMA_SQR
complex(dp) :: D(dim,dim) ! full matrix with GAMMA_C elements on the diagonal
complex(dp) :: sqrtDI(dim,dim) ! full matrix with 1/sqrt(GAMMA_C) elements on the diagonal
complex(dp) :: ZC(dim,dim) ! Characteristic impedance matrix
complex(dp) :: YC(dim,dim) ! Characteristic admittance matrix
! local variables
complex(dp) :: ZY(dim,dim) ! YZ matrix product to be diagonalised
! temposrary matrices used in the calculations
complex(dp) :: T1(dim,dim)
complex(dp) :: T2(dim,dim)
complex(dp) :: ZDYD(dim,dim)
complex(dp) :: MPI(dim,dim)
complex(dp) :: TM1(dim,dim)
real(dp) :: condition_number ! matrix condition number
! loop variables
integer :: row,col
! error code for matrix inverse calculation
integer :: ierr
! START
! Calculate the product ofthe transmission line admittance (Y) and impedance (Z) matrices
if(verbose) write(*,*)'CALLED modal_decomposition_global_ZY'
ZY=matmul(Z,Y)
ZDYD=matmul(Z_domain,Y_domain)
! perform a modal decomposition on the ZY product (equation 2.8)
! using eispack routines to calculate the eigenvalues and eigenvectors of the ZY
! In practice we operate on the domain based ZY product as it seems to be numerically
! better in the case of degenerate eigenvalues, then we transform the eigenvector matrix
! with the domain decomposition matrix MVI to give TV The eigenvaluse are unchanged.
! i.e.(V_domain)=[MV] (V_global), (I_domain)=[MI] (I_global)
! (V_domain)=[Z_domain] (I_domain), (I_domain)=[Y_domain](V_domain)
! (V_global)=[Z_global] (I_global), (I_global)=[Y_global](V_global)
! if [TV] diagonalises [Z_global][Y_global] i.e. [Z_global][Y_global]=[TV][gammasqr][TV^-1]
! and [MPI]diagonalises [D_domain][Y_domain] i.e. [Z_domain][Y_domain] = [MPI][gammasqr][MPI^-1] then
! [TV]=[MV^-1][MPI]
! Perturb the diagonal elements very slightly to eliminate the
! problem with finding all the eigenvectors corresponding to degenerate eigenvalues.
! The perturbation is very small compared with the approximations applied to obtain the
! original L and C matrices... See Theory_Manual_Section 2.3.1
do row=1,dim
ZDYD(row,row)=ZDYD(row,row)*(1d0+1D-8*row)
end do
! calculate the eigenvectors and eigenvalues of YDZD
CALL calc_eigenvectors(ZDYD,GAMMA_SQR,MPI,dim)
TV=matmul(MVI,MPI)
GAMMA_C(:)=sqrt(GAMMA_SQR(:))
if(verbose) write(*,*)'Invert TV'
ierr=1 ! set ierr=1 on input to matrix inverse to cause the program to return here if we have a singular matrix
CALL cinvert_Gauss_Jordan(TV,dim,TVI,dim,ierr)
if (ierr.NE.0) then
! we have a singular matrix here
run_status='ERROR in modal_decomposition_global_ZY. Singular matrix'
CALL write_program_status()
STOP 1
end if
if(verbose) write(*,*)'Done: Invert TV'
GAMMA_SQR(:)=GAMMA_C(:)*GAMMA_C
gamma_r(:)=sqrt(-dble(GAMMA_SQR(:)))
! get the diagonal matrix
D(:,:)=0d0
sqrtDI(:,:)=0d0
do row=1,dim
D(row,row)=GAMMA_C(row)*GAMMA_C(row)
sqrtDI(row,row)=1d0/GAMMA_C(row)
end do
! Calculate the voltage transformation matrices based on Theory_Manual_Eqn 2.37
TII=transpose(TV)
if(verbose) write(*,*)'Invert TII'
ierr=1 ! set ierr=1 on input to matrix inverse to cause the program to return here if we have a singular matrix
CALL cinvert_Gauss_Jordan(TII,dim,TI,dim,ierr)
if (ierr.NE.0) then
! we have a singular matrix here
run_status='ERROR in modal_decomposition_global_ZY. Singular matrix'
CALL write_program_status()
STOP 1
end if
if(verbose) write(*,*)'Done: Invert TII'
! Calculate the characteristic impedance matrix Zc=Tv gamma^-1 TVI Z (derived in a mannaer similar to Theory_Manual_Eqn 2.41)
T1=matmul(sqrtDI,TVI)
T2=matmul(TV,T1)
ZC=matmul(T2,Z)
if(verbose) write(*,*)'Invert ZC'
ierr=1 ! set ierr=1 on input to matrix inverse to cause the program to return here if we have a singular matrix
CALL cinvert_Gauss_Jordan(ZC,dim,YC,dim,ierr)
if (ierr.NE.0) then
! we have a singular matrix here
run_status='ERROR in modal_decomposition_global_ZY. Singular matrix'
CALL write_program_status()
STOP 1
end if
if(verbose) write(*,*)'Done: Invert ZC'
! Calculate the modal impedance matrix
T1=matmul(Z,TI)
Zm=matmul(TVI,T1)
! Calculate the modal admittance matrix
T1=matmul(Y,TV)
Ym=matmul(TII,T1)
! Calculate Modal impedance
do row=1,dim
Zmd(row)=Zm(row,row)
Ymd(row)=Ym(row,row)
end do
if (verbose) then
write(*,*)'Modal decomposition of Global matrix ZY:'
CALL write_cmatrix_re(ZY,dim,0)
do col=1,dim
write(*,*)
write(*,*)'Modal decomposition: Mode number',col
write(*,*)
write(*,*)'Eigenvalue',GAMMA_SQR(col)
write(*,*)
write(*,*)'Eigenvector'
do row=1,dim
write(*,*)row,TV(row,col)
end do
end do
CALL c_condition_number(TV,dim,condition_number,dim)
write(*,*)'Condition number of TV matrix =',condition_number
CALL test_decomposition(ZY,GAMMA_SQR,TV,dim)
end if
RETURN
END SUBROUTINE modal_decomposition_global_ZY
!
! NAME SUBROUTINE calc_eigenvectors
!
! wrapper for the eispack codes cg and rg
!
! DESCRIPTION
! Calculate the eigenvalues and eigenvectors of a general complex matrix.
! We automatically detect if the system is real and use SUBROUTINE rg from eispack
! otherwise we use SUBROUTINE cg from eispack
! A small perturbation is applied to the matrix to prevent a problem with degenerate
! systems where the eispack subroutine would occasionally return the same eigenvector
! for more than one of the degenerate eigenvalues (i.e. the diagonalised system
! would be singular which causes problems in the subsequent analysis.)
!
! COMMENTS
! Order the eigenvalues (and eigenvectors) with largest first.
!
! HISTORY
!
! 17/06/2016 CJS. Use the eispack routine rg for real matrices to try and improve robustness
!
SUBROUTINE calc_eigenvectors(M,gamma,P,dim)
USE type_specifications
USE general_module
USE eispack
USE maths
IMPLICIT NONE
integer,intent(IN) :: dim ! matrix dimension
complex(dp),intent(IN) :: M(dim,dim) ! input matrix
complex(dp),intent(OUT) :: gamma(dim) ! vector of eigenvalues to be returned
complex(dp),intent(OUT) :: P(dim,dim) ! matrix of eigenvectors to be returned, eigenvectors in columns
! local variables
complex(dp) :: lambda
complex(dp) :: AML(dim,dim)
complex(dp) c1,c2
complex(dp) x1,x2
! EISPACK variables (see eispack routines rg,cg for their meanings)
integer(i4) n
real(dp) :: ai(dim,dim)
real(dp) :: ar(dim,dim)
integer(i4) :: ierr
integer(i4):: matz
real(dp) :: wi(dim)
real(dp) :: wr(dim)
real(dp) :: zi(dim,dim)
real(dp) :: zr(dim,dim)
! normalisation variables
real(dp) norm
real(dp) max_value
integer row,col
complex(dp) :: largest_element
real(dp) :: mag_largest_element
! variables for testing whether we have a real or complex system
real(dp) :: max_re,max_im,re_test
! variables for sorting eigenvalues and eigenvectors
real(dp) :: min_eigenvalue
integer :: min_pos
complex(dp) :: swap
complex(dp) :: vswap(1:dim)
! condition number calculation
real(dp) :: condition_number
! eigenvalue loop variable
integer :: ie
! START
! copy variables into the format required for EISPACK subroutines
n=dim
ar(:,:)=dble(M(:,:))
ai(:,:)=aimag(M(:,:))
! test to see whether we have a real matrix
max_re=0d0
max_im=0d0
do row=1,dim
do col=1,dim
max_re=max(abs(ar(row,col)),max_re)
max_im=max(abs(ai(row,col)),max_im)
end do
end do
if (max_im.GT.0d0) then
re_test=max_re/max_im
else
! maximum imaginary part is equal to zero so use the real eigen solver from eispack
re_test=1d13
end if
matz=1 ! eigenvalues and eigenvectors required
! call the eispack routine
if (re_test.GT.1D12) then ! This tolerance should be set in constants
! Assume that this is a real problem so call the EISPACK routine for real matrices
CALL rg ( n, ar, wr, wi, matz, zr, ierr )
if (ierr.NE.0) then
run_status='ERROR in eispack routine rg called from calc_eigenvectors'
CALL write_program_status()
STOP 1
end if
gamma(:)=cmplx( wr(:),wi(:), dp )
! assemble the complex eigenvector matrix. See header for eispack subroutine cg for explanation
ie=1
do while (ie.LE.dim)
if (wi(ie).NE.0d0) then
! complex eigenvalue
P(1:dim,ie) =cmplx( zr(1:dim,ie), zr(1:dim,ie+1), dp )
P(1:dim,ie+1)=cmplx( zr(1:dim,ie),-zr(1:dim,ie+1), dp )
ie=ie+2
else
! real eigenvalue
P(1:dim,ie)=cmplx( zr(1:dim,ie),0d0, dp )
ie=ie+1
end if
end do ! next eigenvector
else
! Assume that we have a complex problem so call the EISPACK routine for the general complex form
CALL cg ( n, ar, ai, wr, wi, matz, zr, zi, ierr )
if (ierr.NE.0) then
run_status='ERROR in eispack routine cg called from calc_eigenvectors'
CALL write_program_status()
STOP 1
end if
gamma(:)=cmplx( wr(:),wi(:), dp )
P(:,:)=cmplx( zr(:,:),zi(:,:), dp )
end if ! real or complex problem
! multiply the eigenvectors such that the largest element is real and positive
do col=1,dim
mag_largest_element=0d0
do row=1,dim
if (abs(P(row,col)).GT.mag_largest_element) then
mag_largest_element=abs(P(row,col))
largest_element=P(row,col)
end if
end do
do row=1,dim
P(row,col)=P(row,col)/largest_element
end do
end do
! normalise the eigenvectors to a length of 1
do col=1,dim
norm=0d0
do row=1,dim
norm=norm+abs(P(row,col))**2
end do
norm=sqrt(norm)
do row=1,dim
P(row,col)=P(row,col)/norm
end do
end do
! At this stage we have all the eigenvalues and eigenvectors though the order is uncertain
! Put the eigenvalues and the corresponding eigenvectors into order of magnitude, smallest first
! This corresponds to the process for the lossless homogeneous case in SUBROUTINE modal_decomposition_LC
do ie=1,dim-1
min_eigenvalue=abs(gamma(ie))
min_pos=ie
do row=ie+1,dim
if (abs(gamma(row)).LT.min_eigenvalue) then
! this eigenvalue is the smallest so far
min_eigenvalue=abs(gamma(row))
min_pos=row
end if
if (min_pos.NE.ie) then
! swap the eigenvalue and eigenvector into the ie-th position
swap=gamma(ie)
gamma(ie)=gamma(min_pos)
gamma(min_pos)=swap
vswap(1:dim)=P(1:dim,ie)
P(1:dim,ie)=P(1:dim,min_pos)
P(1:dim,min_pos)=vswap(1:dim)
end if ! we need to swap the eigenvalue and eigenvector position
end do ! next eigenvalue to check
end do ! next eigenvalue to put in order
RETURN
END SUBROUTINE calc_eigenvectors
!
! NAME SUBROUTINE test_decomposition
!
! DESCRIPTION
! check the results of a modal decomposition to see whether it is accurate...
! Checks are:
! 1. [Pinverse][M][P]=[gamma]
! 2. evaluate [M](x)-gamma (x) and calculate the maximum residual over all of the eigenvalues,(x)
! 3. evaluate [P][gamma][PI]-[M] and check the error
!
! COMMENTS
!
!
! HISTORY
!
!
!
SUBROUTINE test_decomposition(M,gamma,P,dim)
USE type_specifications
USE eispack
USE maths
IMPLICIT NONE
integer,intent(IN) :: dim ! matrix dimension
complex(dp),intent(IN) :: M(dim,dim) ! input matrix
complex(dp),intent(IN) :: gamma(dim) ! vector of eigenvalues to be returned
complex(dp),intent(IN) :: P(dim,dim) ! matrix of eigenvectors to be returned, eigenvectors in columns
! Local variables
complex(dp) :: gamma2(dim,dim)
complex(dp) :: PI(dim,dim)
complex(dp) :: T1(dim,dim)
complex(dp) :: PH(dim,dim)
complex(dp) :: PgammaPI(dim,dim)
complex(dp) :: x(dim),M_x(dim),gamma_x(dim)
real(dp) :: norm
real(dp) :: diag_diff
real(dp) :: err
real(dp) :: max_off_diag
real(dp) :: max_residual
real(dp) :: residual
complex(dp) :: v1v2
integer :: row,col,eigenvalue,e1,e2
integer :: n_eigenvectors
integer :: ierr
! START
write(*,*)'Test modal decomposition'
ierr=0
CALL cinvert_Gauss_Jordan(P,dim,PI,dim,ierr)
T1=matmul(PI,M)
gamma2=matmul(t1,P)
! check the difference between gamma and gamma2
norm=0d0
diag_diff=0d0
do row=1,dim
norm=norm+abs(gamma(row))**2
diag_diff=diag_diff+abs(gamma(row)-gamma2(row,row))**2
end do
norm=sqrt(norm)
diag_diff=diag_diff/norm
max_off_diag=0d0
do row=1,dim
do col=1,dim
if (row.NE.col) then
max_off_diag=max( max_off_diag,ABS(gamma2(row,col)) )
end if
end do
end do
! evaluate [M](x)-gamma (x) and calculate the maximum residual over all of the eigenvalues
max_residual=0d0
do eigenvalue=1,dim
x(1:dim)=P(1:dim,eigenvalue)
M_x=matmul(M,x)
gamma_x(:)=gamma(eigenvalue)*x(:)
residual=0d0
do row=1,dim
residual=residual+abs(M_x(row)-gamma_x(row))**2
end do
residual=sqrt(residual)/norm
max_residual=max(residual,max_residual)
end do
! evaluate PgammaPI-M and check the error
T1=matmul(P,gamma2)
PgammaPI=matmul(T1,PI)
err=0d0
norm=0d0
do row=1,dim
do col=1,dim
err=err+abs(M(row,col)-PgammaPI(row,col))
norm=norm+abs(M(row,col))**2
end do
end do
err=err/sqrt(norm)
Write(*,8100)'Diagonalisation check: diag_diff=',diag_diff,' max_off_diag=',&
max_off_diag,' max_residual=',max_residual,' max_error=',err
8100 format(A,ES12.4,A,ES12.4,A,ES12.4,A,ES12.4)
write(*,*)'M'
CALL write_cmatrix(M,dim,0)
write(*,*)'P'
CALL write_cmatrix(P,dim,0)
write(*,*)'Gamma'
do row=1,dim
write(*,*)row,gamma(row),abs(gamma(row))
end do
write(*,*)'PI'
CALL write_cmatrix(PI,dim,0)
write(*,*)'PgammaPi'
CALL write_cmatrix(PgammaPI,dim,0)
RETURN
END SUBROUTINE test_decomposition