mod_MPA.F Source File


Source Code

!
! License-Identifier: GPL
!
! Copyright (C) 2019 The Yambo Team
!
! Authors (see AUTHORS file for details): DALV,AF
!
! headers
!
#include <y_memory.h>
#include <dev_defs.h>
!
!------------------------------------------------------------
! Multipole interpolation:
!                         - analytical solution for 1-3 poles
!                         - Linear solver for n poles*
!                         - Pade-Thiele solver for n poles*
!
! Failure condition for the position of the poles*
!
! *DA. Leon et al, PRB 104, 115157 (2021)
!
! Notes:
!
!   1) X(w) is approximated as a sum of poles
!   2) Form of one pole: -P/(w**2-Q) = 2*E*R/(w**2-E**2)
!   3) The input are two w and X(w) for each pole
!   4) The output are E and R coefficients
!   5) Use real(R), imaginary(I) or complex(C) w
!
! **The module works for scalar polarizabilities, so if 
!   one wants the solution for matrix element X(G,G',q)
!   then RQ_solver should be called for each G, G' and q.  
!------------------------------------------------------------
!
module mpa_m
 !
 use pars,            ONLY:SP,DP,cZERO,cONE,cI
 use functions,       ONLY:NAN
 use matrix,          ONLY:PAR_matrix
 use gpu_m,           ONLY:have_gpu
 use y_memory_alloc
 !
 implicit none

 complex(SP),      allocatable, target :: MPA_freqs(:)
 type(PAR_matrix), allocatable, target :: MPA_E_par(:)
 type(PAR_matrix), allocatable, target :: MPA_R_par(:)
 complex(SP), allocatable DEV_ATTR, target :: MPA_E_par_d(:,:,:)
 complex(SP), allocatable DEV_ATTR, target :: MPA_R_par_d(:,:,:)
 !
 public :: mpa_RE_solver,err_func_X
 public :: MPA_alloc,MPA_free
 !
contains
 !
 subroutine MPA_alloc(d)
   implicit none
   integer :: d(4)
   !
   call MPA_free()
   YAMBO_ALLOC(MPA_freqs,(d(4)))
 end subroutine

 subroutine MPA_free()
   YAMBO_FREE(MPA_freqs)
 end subroutine
 !
 subroutine mpa_cond1(z, E, PPcond_rate)
   !
   complex(SP), intent(in)     :: z(2)
   complex(SP), intent(inout)  :: E
   real(SP),    intent(out)    :: PPcond_rate
   !
   PPcond_rate=0._SP
   !
   if(NAN(abs(E)) .or. abs(E)<0.00001_SP) then
     E=abs(z(2))-cI*epsilon(1._SP) 
     PPcond_rate=1._SP  
   elseif(real(E)>0._SP) then     
     E = sqrt(E)
   else
     E = sqrt(-conjg(E)) ! DALV: PPA uses E = 1._SP
     PPcond_rate=1._SP 
   endif
   !
   ! DALV: since MPA uses complex poles 
   !       we need to guarantee the time ordering
   !
   if(real(E)<0._SP) E=-E
   if(aimag(E)>-epsilon(1._SP)) E=real(E,SP)-cI*epsilon(1._SP)
   !
 end subroutine mpa_cond1
 !
 function pole_is_out(i, wmax, thr, E)
   implicit none
   !
   integer ,    intent(in)     :: i
   real(SP),    intent(in)     :: wmax, thr
   complex(SP), intent(inout)  :: E(:)
   !
   integer     :: j
   logical     :: pole_is_out
   !
   pole_is_out=.false.
   !
   if(real(E(i),SP)>wmax) pole_is_out=.true.
   !
   j=1
   do while(j<i.and..not.pole_is_out)
     !
     if(abs(real(E(i),SP)-real(E(j),SP))<thr) then
       !
       pole_is_out=.true.
       !
       if(abs(real(E(j),SP))>max(abs(aimag(E(j))),abs(aimag(E(i))))) then
         E(j)=(real(E(j),SP)+real(E(i),SP))/2._SP-cI*max(abs(aimag(E(j))),abs(aimag(E(i))))
       else
         E(j)=(real(E(j),SP)+real(E(i),SP))/2._SP-cI*min(abs(aimag(E(j))),abs(aimag(E(i))))
       endif
       !
     endif
     !
     j=j+1
     !
   enddo
   !
 endfunction pole_is_out
 !
 subroutine mpa_cond(np, npr, z, E, PPcond)
   !
   integer,     intent(in)     :: np
   integer,     intent(out)    :: npr
   logical,     intent(out)    :: PPcond(np)
   complex(SP), intent(in)     :: z(2*np)
   complex(SP), intent(inout)  :: E(np)
   !
   integer     :: i, j
   complex(SP) :: Eaux(np)
   real(SP)    :: wmax, thr=0.00001_SP
   !
   npr=np
   !
   !DALV: we were using the square before, 
   !      now we use 1.5* the extreme of the interval
   wmax=maxval(sqrt(real(z,SP)))*1.5_SP 
   !
   i=1
   do while(i<=npr)
     !
     Eaux(i)=sqrt(E(i))
     !
     do while(pole_is_out(i,wmax,thr,Eaux).and.i<=npr)
       Eaux(i)=sqrt(E(npr))
       PPcond(npr)=.true.
       npr=npr-1
     enddo
     !
     if(.not.pole_is_out(i,wmax,thr,Eaux).and.abs(real(Eaux(i),SP))<abs(aimag(Eaux(i)))) PPcond(i)=.false.
     Eaux(i)=max(abs(real(Eaux(i),SP)),abs(aimag(Eaux(i))))-cI*min(abs(real(Eaux(i),SP)),abs(aimag(Eaux(i))))
     !
     i=i+1
     !
   enddo
   !
   E(1:npr)=Eaux(1:npr)
   if(npr<np) then
     E(npr+1:np)=1._SP-cI*epsilon(1._SP)
     PPcond(npr+1:np)=.true.
   endif
   !
 end subroutine mpa_cond
 !
 subroutine mpa_E_1p_solver(z, x, E, PPcond_rate) 
   !
   ! DALV: analytical form of the E position of the 1 pole solution
   !
   implicit none
   !
   complex(SP), intent(in)   :: z(2)
   complex(SP), intent(in)   :: x(2)
   complex(SP), intent(out)  :: E
   real(SP),    intent(out)  :: PPcond_rate
   !
   E = (x(1)*z(1)**2 -x(2)*z(2)**2 )/(x(1)-x(2))
   call mpa_cond1(z, E, PPcond_rate)
   !
 end subroutine mpa_E_1p_solver
 !
 subroutine mpa_R_fit(np,npr, w, xq, R, E)
   implicit none
   integer,     intent(in)   :: np, npr
   complex(SP), intent(in)   :: w(2*np)
   complex(SP), intent(in)   :: xq(2*np), E(np)
   complex(SP), intent(out)  :: R(np)
   !
   complex(SP)               :: A(2*np,npr), B(2*np)
   integer                   :: i, k, info, rank, lwork, lwmax 
   parameter                 (lwmax=1000)
   real(SP)                  :: rcond
   integer                   :: iwork(3*npr*0+11*npr)
   real(SP)                  :: S(npr), rwork(10*npr+2*npr*25+8*npr*0+3*25+26*26)
   complex(SP)               :: work(lwmax)
   !
   do k = 1, 2*np
     B(k) = xq(k)
     do i = 1, npr
       A(k,i) = 2._SP*E(i)/(w(k)**2 -E(i)**2)
     enddo
   enddo 
   !
   rcond=-1._SP
   !
#ifdef _DOUBLE
   call zgelsd(2*np,npr,1,A,2*np,B,2*np,S,rcond,rank,work,-1,   rwork,iwork,info)
   lwork=min(lwmax,int(work(1)))
   call zgelsd(2*np,npr,1,A,2*np,B,2*np,S,rcond,rank,work,lwork,rwork,iwork,info)
#else
   call cgelsd(2*np,npr,1,A,2*np,B,2*np,S,rcond,rank,work,-1,   rwork,iwork,info)
   lwork=min(lwmax,int(work(1)))
   call cgelsd(2*np,npr,1,A,2*np,B,2*np,S,rcond,rank,work,lwork,rwork,iwork,info)
#endif
   !
   R(1:npr)=B(1:npr)
   if (npr<np) R(npr+1:np)=0.0_SP
   !
 end subroutine mpa_R_fit
 !
 subroutine mpa_R_solver(np, w, xq, R, E, rcond)
   implicit none
   integer,     intent(in)   :: np
   complex(SP), intent(in)   :: w(2*np)
   complex(SP), intent(in)   :: xq(2*np), E(np)
   complex(SP), intent(out)  :: R(np)
   real(SP),    intent(out)  :: rcond
   !
   complex(SP) :: A(np,np)
   real(SP)    :: anorm
   integer     :: i, k, info, ind(np)    
   complex(SP) :: rwork(2*np), work(2*np)
   !
   anorm=abs(2._SP*E(1)/(w(1)**2 -E(1)**2))
   !
   do k = 1, np
     R(k) = xq(k)
     do i = 1, np
       A(k,i) = 2._SP*E(i)/(w(k)**2 -E(i)**2)
       if(abs(A(k,i))>anorm) anorm=abs(A(k,i))
     enddo
   enddo 
   !
#ifdef _DOUBLE
   call zgecon('I', np, A, np, anorm, rcond, work, rwork, info)
   call zgesv(np, 1, A, np, ind, R, np, info) 
#else
   call cgecon('I', np, A, np, anorm, rcond, work, rwork, info)
   call cgesv(np, 1, A, np, ind, R, np, info) 
#endif
   !
 end subroutine mpa_R_solver
 !
 subroutine mpa_E_solver(np, npr, z, xq, E, PPcond, econd)
   implicit none
   integer,     intent(in)   :: np
   integer,     intent(out)  :: npr
   complex(SP), intent(in)   :: z(2*np)
   complex(SP), intent(in)   :: xq(2*np)
   complex(SP), intent(out)  :: E(np)
   logical,     intent(out)  :: PPcond(np)
   real(SP),    intent(out)  :: econd(2)
   !
   complex(SP) :: Z1(np,np), Z2(np,np), M1(np,np), M2(np,np), v1(np)
   complex(SP) :: M(np,np), b(np), Companion(np,np)
   complex(SP) :: rwork(2*np),work(2*np),VR(np),VL(np),S(np),RCE(np,np),RCV(np,np),ABNRM
   integer     :: i, j, info, ind(np), ILO,IHI    
   real(SP)    :: rcond, anorm, Wm
   !
   PPcond(:)=.true.
   Wm = maxval(abs(z(1:np)))
   !
   do j = 1, np
     ind(j) = j
     v1(j)  = xq(j   )*z(j   )**np 
     b (j)  = xq(j+np)*z(j+np)**np 
     Z1(j,1) = 1._SP
     Z2(j,1) = 1._SP  
     M1(j,1) = -xq(j) 
     M2(j,1) = -xq(j+np) 
     M (j,1) = 0._SP
     M (j,j) = 1._SP
     !
     do i = 2, np
       Z1(j,i) =  (z(j)/Wm)**(i-1)
       Z2(j,i) =  (z(j+np)/Wm)**(i-1)
       M1(j,i) = -xq(j   )*Z1(j,i)
       M2(j,i) = -xq(j+np)*Z2(j,i)
       if(i/=j) M(j,i) = 0._SP 
     enddo
     !
   enddo
   !
   anorm=abs(Z1(1,1))
   !
   do j = 1, np
     do i = 1, np
       if(abs(Z1(j,i))>anorm) anorm=abs(Z1(j,i))
     enddo
   enddo
   !
#ifdef _DOUBLE
   call zgecon('I', np, Z1, np, anorm, econd(1), work, rwork, info)
   call zgesv(np, np, Z1, np, ind, M, np,info) 
#else
   call cgecon('I', np, Z1, np, anorm, econd(1), work, rwork, info)
   call cgesv(np, np, Z1, np, ind, M, np,info) 
#endif
   !
   M  = matmul(Z2,M)
   M2 = matmul(M,M1) - M2
   b  = matmul(M,v1) - b
   !
   M = 0.0_SP
   !
   do i =1,np
     M(i,i) = 1.0_SP
   enddo
   !
   anorm=abs(M2(1,1))
   !
   do j = 1, np
     do i = 1, np
       if(abs(M2(j,i))>anorm) anorm=abs(M2(j,i))
     enddo
   enddo
   !
#ifdef _DOUBLE
   call zgecon('I', np, M2, np, anorm, econd(2), work, rwork, info)
   call zgesv(np, 1, M2, np, ind, b, np, info)
#else
   call cgecon('I', np, M2, np, anorm, econd(2), work, rwork, info)
   call cgesv(np, 1, M2, np, ind, b, np, info)
#endif
   !
   Companion=0._SP
   Companion(1,np) = -b(1)
   !
   do i = 2, np
     Companion(i,np)  = -b(i)/Wm**(i-1)
     Companion(i,i-1) = 1._SP
   enddo
   !
#ifdef _DOUBLE
   call zgeev( 'N', 'N', np, Companion, np, E, VL, 1, VR, 1, work, 2*np, rwork, info )
#else
   call cgeev( 'N', 'N', np, Companion, np, E, VL, 1, VR, 1, work, 2*np, rwork, info )
#endif
   !
   ! DALV: here we need to force real(E) to be positive.
   ! This is because of the way the residue integral is performed, later.
   !
   call mpa_cond(np, npr, z, E, PPcond)
   !
 end subroutine mpa_E_solver
 !
 subroutine mpa_E_solver_Pade(np, npr, z, X, E, PPcond)
   implicit none
   integer,     intent(in)   :: np
   integer,     intent(out)  :: npr
   complex(SP), intent(in)   :: z(2*np)
   complex(SP), intent(in)   :: X(2*np)
   complex(SP), intent(out)  :: E(np)
   logical,     intent(out)  :: PPcond(np)
   !
   complex(SP) :: c(2*np), b(np+1), Companion(np,np)
   complex(SP) :: c_m1(2*np), b_m1(np+1), b_m2(np+1)
   complex(SP) :: rwork(2*np),work(2*np),VR(np),VL(np)
   integer     :: i, j, info   
   real(SP)    :: rcond, anorm, Wm
   !
   PPcond(:)=.true.
   b_m1(1)=1._SP
   b_m2(1)=1._SP
   b_m1(2:)=0._SP
   b_m2(2:)=0._SP
   b=b_m1
   c=X
   c_m1=c
   !
   do i=2, 2*np
     !
     do j=i, 2*np
       c(j)=(c_m1(i-1)-c_m1(j))/((z(j)-z(i-1))*c_m1(j))
     enddo
     !
     c_m1=c
     !
     do j=1, np+1
       b(j)=b_m1(j)-z(i-1)*c(i)*b_m2(j)
     enddo
     !
     do j=1, np
       b_m2(np+2-j)=c(i)*b_m2(np+1-j)
     enddo
     !
     do j=1, np
       b(j+1)=b(j+1)+b_m2(j+1)
     enddo
     !
     b_m2=b_m1
     b_m1=b
     !
   enddo
   !
   Companion=0._SP
   Companion(1,np) = -b(1)/b(np+1)
   !
   do i = 2, np
     Companion(i,np)  = -b(i)/b(np+1)
     Companion(i,i-1) = 1._SP
   enddo
   !
#ifdef _DOUBLE
   call zgeev( 'N', 'N', np, Companion, np, E, VL, 1, VR, 1, work, 2*np, rwork, info )
#else
   call cgeev( 'N', 'N', np, Companion, np, E, VL, 1, VR, 1, work, 2*np, rwork, info )
#endif
   !
   ! DALV: here we need to force real(E) to be positive.
   ! This is because of the way the residue integral is performed, later.
   !
   call mpa_cond(np, npr, z, E, PPcond)
   !
 end subroutine mpa_E_solver_Pade
 !
 subroutine mpa_RE_solver(np, w, x, R, E,mpa_sol,MPred,PPcond_rate,MP_err,cond_num)
   implicit none
   !
   integer,      intent(in)   :: np
   complex(SP),  intent(in)   :: w(2*np), x(2*np)
   complex(SP),  intent(out)  :: R(np), E(np)
   character(2), intent(in)   :: mpa_sol
   logical,      intent(out)  :: MPred
   real(SP),     intent(out)  :: PPcond_rate,MP_err
   !
   integer  :: i,npr
   logical  :: PPcond(np)
   real(SP) :: cond_num(2)
   !
   PPcond_rate=0._SP
   !
   select case(np)
   case(1)
     !
     call mpa_E_1p_solver(w, x, E(1), PPcond_rate)
     call mpa_R_fit(1, 1, w, x, R, E)
   !
   ! DALV: we can also use the particular solutions in the cases of 2 and 3 poles
   !
   case default
     !
     if(mpa_sol=='LA') then
       call mpa_E_solver(np, npr, w**2, x, E, PPcond, cond_num)
       cond_num=log10(1._SP/cond_num)
     else ! DALV: we use Pade-Thiele instead (mpa_sol='PT')
       cond_num=0.
       call mpa_E_solver_Pade(np, npr, w**2, x, E, PPcond)
     endif
     !
     if(npr<np) MPred=.true.
     !
     call mpa_R_fit(np, npr, w, x, R, E)
     !
     do i=1,np
       if(.not.PPcond(i)) PPcond_rate=PPcond_rate+abs(R(i))
     enddo
     !
     PPcond_rate=PPcond_rate/sum(abs(R(:)))
     !
   end select
   !
   MP_err=err_func_X(np, R, E, w, x)
   !
 end subroutine mpa_RE_solver
 !
 !
 function err_func_X(np, R, E, w, x)
   implicit none
   !
   integer ,    intent(in)  :: np
   complex(SP), intent(in)  :: x(2*np), w(2*np)
   complex(SP), intent(in)  :: R(np), E(np)
   !
   complex(SP) :: aux
   real(SP)    :: err_func_X
   integer     :: i,j
   !
   err_func_X = 0._SP
   !
   do j=1, 2*np
     !
     aux = cZERO
     !
     do i=1, np
       aux = aux + 2._SP*E(i)*R(i)/(w(j)**2-E(i)**2)     
     enddo
     !
     err_func_X = err_func_X + abs( aux - x(j) )**2
     !
   enddo  
   !
   ! DALV: this is the Standard Deviation relative to the maximum: 
   !       (Coefficient of variation)
   !
   err_func_X = sqrt(err_func_X/(2._SP*np-1._SP))/maxval(abs(X(:)))
   !
 endfunction err_func_X
 !
 !
 ! ----------------------------------------------------------------
 ! ------- DALV: Not in use ---------------------------------------
 ! ----------------------------------------------------------------
 !
 subroutine mpa_RE_1p_solver(z, x, R, E, PPcond_rate) 
   !
   ! DALV: analytical form of the 1 pole solution
   !
   implicit none
   !
   complex(SP), intent(in)   :: z(2)
   complex(SP), intent(in)   :: x(2)
   complex(SP), intent(out)  :: R, E
   real(SP),    intent(out)  :: PPcond_rate
   !
   E = (x(1)*z(1)**2 -x(2)*z(2)**2 )/(x(1)-x(2))
   !
   call mpa_cond1(z, E, PPcond_rate)
   !
   R = x(2)*(z(2)**2-E**2)/(2._SP*E)
   !
 end subroutine mpa_RE_1p_solver
 !
 !
 subroutine mpa_RQ_2p_solver(w, x, r, q) 
   implicit none
   !
   complex(SP), intent(in)   :: w(4)
   complex(SP), intent(in)   :: x(4)
   complex(SP), intent(out)  :: r(2), q(2)
   !
   integer     :: i, j
   complex(SP) :: a, b, c ! DALV: coefficients of the w^2 polynomial
   complex(SP) :: den, det
   !
   !
   a =  -(x(1)-x(2))*(x(3)-x(4))*( w(1)**2*w(2)**2 + w(3)**2*w(4)**2 ) &
        +(x(1)-x(3))*(x(2)-x(4))*( w(1)**2*w(3)**2 + w(2)**2*w(4)**2 ) &
        -(x(1)-x(4))*(x(2)-x(3))*( w(1)**2*w(4)**2 + w(2)**2*w(3)**2 ) 
   !
   b =   (x(1)-x(2))*w(3)**2*w(4)**2*( x(3)*w(3)**2 - x(4)*w(4)**2 ) - &
         (x(1)-x(3))*w(2)**2*w(4)**2*( x(2)*w(2)**2 - x(4)*w(4)**2 ) + &
         (x(1)-x(4))*w(2)**2*w(3)**2*( x(2)*w(2)**2 - x(3)*w(3)**2 ) + &
         (x(2)-x(3))*w(1)**2*w(4)**2*( x(1)*w(1)**2 - x(4)*w(4)**2 ) - &
         (x(2)-x(4))*w(1)**2*w(3)**2*( x(1)*w(1)**2 - x(3)*w(3)**2 ) + &
         (x(3)-x(4))*w(1)**2*w(2)**2*( x(1)*w(1)**2 - x(2)*w(2)**2 )   
   !
   c =   (x(1)-x(2))*w(1)**2*w(2)**2*( x(3)*w(3)**4 - x(4)*w(4)**4 ) - &
         (x(1)-x(3))*w(1)**2*w(3)**2*( x(2)*w(2)**4 - x(4)*w(4)**4 ) + &
         (x(1)-x(4))*w(1)**2*w(4)**2*( x(2)*w(2)**4 - x(3)*w(3)**4 ) + &
         (x(2)-x(3))*w(2)**2*w(3)**2*( x(1)*w(1)**4 - x(4)*w(4)**4 ) - &
         (x(2)-x(4))*w(2)**2*w(4)**2*( x(1)*w(1)**4 - x(3)*w(3)**4 ) + &
         (x(3)-x(4))*w(3)**2*w(4)**2*( x(1)*w(1)**4 - x(2)*w(2)**4 ) 
   !
   det  = b**2 -4._SP*a*c
   q(1) = -(b + sqrt(det))/2._SP
   q(2) = -(b - sqrt(det))/2._SP
   !
   if(abs(a)>epsilon(1._SP)) then
     q(1)=q(1)/a
     q(2)=q(2)/a
   endif
   !
   den  =  (q(1)-q(2))*(w(2)-w(3))*(w(2)+w(3))
   r(1) = -(q(1) - w(2)**2)*(q(1) - w(3)**2)*( x(3)*w(3)**2 - x(2)*w(2)**2  + q(2)*(x(2) - x(3)) )
   r(2) =  (q(2) - w(2)**2)*(q(2) - w(3)**2)*( x(3)*w(3)**2 - x(2)*w(2)**2  + q(1)*(x(2) - x(3)) )
   !
   if(abs(den)>epsilon(1._SP)) then
     r(1)=r(1)/den
     r(2)=r(2)/den
   endif
   !
 end subroutine mpa_RQ_2p_solver
 !
 !
 function f1(t, w, x) 
   implicit none
   complex(SP), intent(in)  :: w(6)
   complex(SP), intent(in)  :: x(6)
   integer,     intent(in)  :: t
   !
   complex(SP) :: f1
   !
   f1 = (x(1)-x(2)) *w(3)**2 *w(t)**2 *(w(3)**2 - w(t)**2) -  &
        (x(1)-x(3)) *w(2)**2 *w(t)**2 *(w(2)**2 - w(t)**2) +  &
        (x(1)-x(t)) *w(2)**2 *w(3)**2 *(w(2)**2 - w(3)**2) +  &
        (x(2)-x(3)) *w(1)**2 *w(t)**2 *(w(1)**2 - w(t)**2) -  &
        (x(2)-x(t)) *w(1)**2 *w(3)**2 *(w(1)**2 - w(3)**2) +  &
        (x(3)-x(t)) *w(1)**2 *w(2)**2 *(w(1)**2 - w(2)**2)
   !
 end function
 !
 !
 function f2(t, w, x) 
   implicit none
   complex(SP), intent(in)  :: w(6)
   complex(SP), intent(in)  :: x(6)
   integer,     intent(in)  :: t
   !
   complex(SP) :: f2
   !
   f2 = (x(1)-x(2)) *w(1)**2 *w(2)**2 *(w(3)**4 - w(t)**4) -  &
        (x(1)-x(3)) *w(1)**2 *w(3)**2 *(w(2)**4 - w(t)**4) +  &
        (x(1)-x(t)) *w(1)**2 *w(t)**2 *(w(2)**4 - w(3)**4) +  &
        (x(2)-x(3)) *w(2)**2 *w(3)**2 *(w(1)**4 - w(t)**4) -  &
        (x(2)-x(t)) *w(2)**2 *w(t)**2 *(w(1)**4 - w(3)**4) +  &
        (x(3)-x(t)) *w(3)**2 *w(t)**2 *(w(1)**4 - w(2)**4)
   !
 end function
 !
 !
 function f3(t, w, x) 
   implicit none
   complex(SP), intent(in)  :: w(6)
   complex(SP), intent(in)  :: x(6)
   integer,     intent(in)  :: t
   !
   complex(SP) :: f3
   !
   f3 = (x(1)-x(2)) *w(1)**4 *w(2)**4 *(w(3)**2 - w(t)**2) -  &
        (x(1)-x(3)) *w(1)**4 *w(3)**4 *(w(2)**2 - w(t)**2) +  &
        (x(1)-x(t)) *w(1)**4 *w(t)**4 *(w(2)**2 - w(3)**2) +  &
        (x(2)-x(3)) *w(2)**4 *w(3)**4 *(w(1)**2 - w(t)**2) -  &
        (x(2)-x(t)) *w(2)**4 *w(t)**4 *(w(1)**2 - w(3)**2) +  &
        (x(3)-x(t)) *w(3)**4 *w(t)**4 *(w(1)**2 - w(2)**2)
   !
 end function
 !
 !
 function f4(t, w, x) 
   implicit none
   complex(SP), intent(in)  :: w(6)
   complex(SP), intent(in)  :: x(6)
   integer,     intent(in)  :: t
   !
   complex(SP) :: f4
   !
   f4 = x(1) *w(1)**6 * (w(2)**2 - w(3)**2) * (w(2)**2 - w(t)**2) * (w(3)**2 - w(t)**2) - &
        x(2) *w(2)**6 * (w(1)**2 - w(3)**2) * (w(1)**2 - w(t)**2) * (w(3)**2 - w(t)**2) + &
        x(3) *w(3)**6 * (w(1)**2 - w(2)**2) * (w(1)**2 - w(t)**2) * (w(2)**2 - w(t)**2) - &
        x(t) *w(t)**6 * (w(1)**2 - w(2)**2) * (w(1)**2 - w(3)**2) * (w(2)**2 - w(3)**2)
   !
 end function
 !
 !
 subroutine mpa_RQ_3p_solver(w, x, r, q) 
   implicit none
   complex(SP), intent(in)   :: w(6)
   complex(SP), intent(in)   :: x(6)
   complex(SP), intent(out)  :: r(3), q(3)
   !
   integer     :: i, j
   complex(SP) :: a, b, c, d ! DALV: coefficients of the w^2 polynomial
   complex(SP) :: D1, D2, den, deR 
   real(SP)    :: t=0.33333333333333334_SP 
   complex(SP) :: s=(1._SP, 1.7320508075688772_SP)
   !
   !
   a =  f1(4, w, x)* ( f2(5, w, x)*f3(6, w, x) - f2(6, w, x)*f3(5, w, x) ) - &
        f1(5, w, x)* ( f2(4, w, x)*f3(6, w, x) - f2(6, w, x)*f3(4, w, x) ) + &
        f1(6, w, x)* ( f2(4, w, x)*f3(5, w, x) - f2(5, w, x)*f3(4, w, x) ) 
   !
   b = -f1(4, w, x)* ( f2(5, w, x)*f4(6, w, x) - f2(6, w, x)*f4(5, w, x) ) + &
        f1(5, w, x)* ( f2(4, w, x)*f4(6, w, x) - f2(6, w, x)*f4(4, w, x) ) - &
        f1(6, w, x)* ( f2(4, w, x)*f4(5, w, x) - f2(5, w, x)*f4(4, w, x) ) 
   !
   c = -f4(4, w, x)* ( f1(5, w, x)*f3(6, w, x) - f1(6, w, x)*f3(5, w, x) ) + &
        f4(5, w, x)* ( f1(4, w, x)*f3(6, w, x) - f1(6, w, x)*f3(4, w, x) ) - &
        f4(6, w, x)* ( f1(4, w, x)*f3(5, w, x) - f1(5, w, x)*f3(4, w, x) ) 
   !
   d = -f4(4, w, x)* ( f2(5, w, x)*f3(6, w, x) - f2(6, w, x)*f3(5, w, x) ) + &
        f4(5, w, x)* ( f2(4, w, x)*f3(6, w, x) - f2(6, w, x)*f3(4, w, x) ) - &
        f4(6, w, x)* ( f2(4, w, x)*f3(5, w, x) - f2(5, w, x)*f3(4, w, x) ) 
   !
   D1   = 3._SP*a*c - b**2
   D2   = 2._SP*b*D1 + 3._SP*a*(b*c - 9._SP*a*d)
   den  = ( D2 + sqrt(4.0_SP*D1**3 + D2**2) )**(t)
   !
   q(1) = -( den*(b- den/2._SP**(t)) + 2.0_SP**(t)*D1)/3.0_SP
   q(2) = -( den*(b+ conjg(s)* den/2._SP**(4._SP*t)) - s* D1/2._SP**(2._SP*t)  )/3._SP
   q(3) = -( den*(b+ s*den/2._SP**(4._SP*t)) - conjg(s)* D1/2._SP**(2._SP*t)  )/3._SP
   !
   if(abs(den*a)>epsilon(1._SP)) then
     q(1)=q(1)/(den*a)
     q(2)=q(2)/(den*a)
     q(3)=q(3)/(den*a)
   endif
   !
   deR  =  (w(1)**2-q(1)) * (w(1)**2-q(2)) * (w(2)**2-q(3)) * (w(3)**2-q(3))*   &
          ((w(2)**2-q(1)) * (w(3)**2-q(2)) - (w(2)**2-q(2)) * (w(3)**2-q(1)))   &
          +(w(1)**2-q(1)) * (w(1)**2-q(3)) * (w(2)**2-q(2)) * (w(3)**2-q(2))*   &
          ((w(2)**2-q(3)) * (w(3)**2-q(1)) - (w(2)**2-q(1)) * (w(3)**2-q(3)))   &
          +(w(1)**2-q(2)) * (w(1)**2-q(3)) * (w(2)**2-q(1)) * (w(3)**2-q(1))*   &
          ((w(2)**2-q(2)) * (w(3)**2-q(3)) - (w(2)**2-q(3)) * (w(3)**2-q(2))) 
   !
   r(1) = (w(1)**2-q(1))*(w(2)**2-q(1))*(w(3)**2-q(1))*(                                                         &
         -(w(1)**2-q(2))*(w(1)**2-q(3))*( (w(2)**2-q(2))*(w(3)**2-q(3)) - (w(2)**2-q(3))*(w(3)**2-q(2)) )*x(1)   &
         +(w(2)**2-q(2))*(w(2)**2-q(3))*( (w(1)**2-q(2))*(w(3)**2-q(3)) - (w(1)**2-q(3))*(w(3)**2-q(2)) )*x(2)   &
         -(w(3)**2-q(2))*(w(3)**2-q(3))*( (w(1)**2-q(2))*(w(2)**2-q(3)) - (w(1)**2-q(3))*(w(2)**2-q(2)) )*x(3) ) 
   !
   r(2) = (w(1)**2-q(2))*(w(2)**2-q(2))*(w(3)**2-q(2))*(                                                         &
         +(w(1)**2-q(1))*(w(1)**2-q(3))*( (w(2)**2-q(1))*(w(3)**2-q(3)) - (w(2)**2-q(3))*(w(3)**2-q(1)) )*x(1)   &
         -(w(2)**2-q(1))*(w(2)**2-q(3))*( (w(1)**2-q(1))*(w(3)**2-q(3)) - (w(1)**2-q(3))*(w(3)**2-q(1)) )*x(2)   &
         +(w(3)**2-q(1))*(w(3)**2-q(3))*( (w(1)**2-q(1))*(w(2)**2-q(3)) - (w(1)**2-q(3))*(w(2)**2-q(1)) )*x(3) ) 
   !
   r(3) = (w(1)**2-q(3))*(w(2)**2-q(3))*(w(3)**2-q(3))*(                                                        &
         -(w(1)**2-q(1))*(w(1)**2-q(2))*( (w(2)**2-q(1))*(w(3)**2-q(2)) - (w(2)**2-q(2))*(w(3)**2-q(1)) )*x(1)  & 
         +(w(2)**2-q(1))*(w(2)**2-q(2))*( (w(1)**2-q(1))*(w(3)**2-q(2)) - (w(1)**2-q(2))*(w(3)**2-q(1)) )*x(2)  & 
         -(w(3)**2-q(1))*(w(3)**2-q(2))*( (w(1)**2-q(1))*(w(2)**2-q(2)) - (w(1)**2-q(2))*(w(2)**2-q(1)) )*x(3) )
   !
   if(abs(deR)>epsilon(1._SP)) then
     r(1)=r(1)/deR
     r(2)=r(2)/deR
     r(3)=r(3)/deR
   endif
   !
 end subroutine mpa_RQ_3p_solver
 !
!
end module mpa_m

📚 Documentation Pages
⏱️ Real-Time
🔗 Code API