!
! This file is part of SACAMOS, State of the Art CAble MOdels in 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-2017 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
!
! SUBROUTINE test_filter_positive_real(H,stable,local_verbose)
!
!
! NAME
! test_filter_positive_real
!
! DESCRIPTION
! test whether a rational function is positive real. The definition of positive real
! is described in section 7.2 of the Theory Manual.
! This implements the Sturm test as described in Kim and Meadows section 9.1.3
!
! SEE ALSO
!
!
! HISTORY
!
! started 9/2017 CJS
! 24/10/2017 CJS Use the minimum resistance value rather than the Sturm test method.
!
SUBROUTINE test_filter_positive_real(H,stable,local_verbose)
USE type_specifications
USE general_module
USE constants
USE frequency_spec
USE filter_module
USE Sfilter_fit_module
IMPLICIT NONE
! variables passed to subroutine
type(Sfilter),intent(IN) :: H
logical,intent(OUT) :: stable
logical,intent(IN) :: local_verbose
! local variables
type(polynomial) :: m1,m2,n1,n2,P,Q,t1,t2,A2,A,Ap,Am,C,R
integer :: i,ii,order,m_order,n_order,a_order
real(dp),allocatable :: value_inf(:)
real(dp),allocatable :: value_0(:)
integer :: n_sturm,sturm_coeff
integer :: S_inf,S_0,n_real_zeros
real(dp) :: last_value_inf,last_value_0
logical :: odd_multiplicity_flag
real(dp) :: Rmin,Wmin
type(Sfilter) :: Hlocal
!START
if (local_verbose) then
write(*,*)'CALLED test_filter_positive_real'
write(*,*)'Input filter function:'
CALL write_Sfilter(H,0)
end if
Hlocal=H
CALL calculate_min_resistance_value(Hlocal,Rmin,wmin)
if (Rmin.LT.0d0) then
stable=.FALSE.
else
stable=.TRUE.
end if
CALL deallocate_Sfilter(Hlocal)
RETURN
! assume the filter is stable initially
stable=.TRUE.
! assemble the odd and even polynomials m1 and n1 from the numerator of H
order=H%a%order
if (mod(order,2).EQ.0) then
m_order=order
n_order=max(order-1,0)
else
m_order=max(order-1,0)
n_order=order
end if
m1=allocate_polynomial(m_order)
n1=allocate_polynomial(n_order)
m1%coeff(:)=0d0
do i=0,m_order,2
m1%coeff(i)=H%a%coeff(i)
end do
n1%coeff(:)=0d0
do i=1,n_order,2
n1%coeff(i)=H%a%coeff(i)
end do
if (local_verbose) then
write(*,*)'Numerator:'
CALL write_poly_local(H%a)
write(*,*)'m1: even order terms'
CALL write_poly_local(m1)
write(*,*)'n1: odd order terms'
CALL write_poly_local(n1)
end if
! assemble the odd and even polynomials m2 and n2 from the denominator of H
order=H%b%order
if (mod(order,2).EQ.0) then
m_order=order
n_order=max(order-1,0)
else
m_order=max(order-1,0)
n_order=order
end if
m2=allocate_polynomial(m_order)
n2=allocate_polynomial(n_order)
m2%coeff(:)=0d0
do i=0,m_order,2
m2%coeff(i)=H%b%coeff(i)
end do
n2%coeff(:)=0d0
do i=1,n_order,2
n2%coeff(i)=H%b%coeff(i)
end do
if (local_verbose) then
write(*,*)'Denominator:'
CALL write_poly_local(H%b)
write(*,*)'m2: even order terms'
CALL write_poly_local(m2)
write(*,*)'n2: odd order terms'
CALL write_poly_local(n2)
end if
! Calculate the polynomial A2(jw)=m1(jw)m2(jw)-n1(jw)n2(jw)
! This is in fact a function of w**2 only (no odd order terms in jw)
t1=m1*m2
t2=n1*n2
A2=t1-t2
CALL get_min_order_poly(A2)
if (local_verbose) then
write(*,*)'A2(jw)=m1m2-n1n2'
CALL write_poly_local(A2)
end if
! Calculate the coefficients of A(w**2)
order=A2%order
if (mod(order,2).EQ.0) then
a_order=order/2
else
write(*,*)'Error in test_filter_positive_real. Order of A2(jw) is not an even number'
end if
A=allocate_polynomial(a_order)
do i=0,a_order
A%coeff(i)=A2%coeff(2*i)
end do
! At the moment A is a function of (jw)**2 not w**2.
! We get the function of w**2 by making the odd order coefficients negative
do i=1,a_order,2
A%coeff(i)=-A%coeff(i)
end do
if (local_verbose) then
write(*,*)'A(w**2)'
CALL write_poly_local(A)
write(*,*)'Full precision coefficients'
do i=0,A%order
write(*,*)i,A%coeff(i)
end do
end if
CALL get_min_order_poly(A)
if (local_verbose) then
write(*,*)'minimum order: A(w**2)'
CALL write_poly_local(A)
write(*,*)'Full precision coefficients'
do i=0,A%order
write(*,*)i,A%coeff(i)
end do
end if
! Get the high frequency value of the function.
! If it is less than zero the funciton is not positive real so return
if (A%coeff(A%order).LT.-zero_test_small) then
stable=.FALSE.
if (local_verbose) then
write(*,*)'Real part of function is less than zero at high frequency'
write(*,*)'test value:',A%coeff(A%order)
CALL write_poly_local(A)
end if
RETURN
end if
!! **** QUESTION: If we calculate all the roots to remove any even multiple roots then
!! maybe we should just test for odd multiplicity real roots directly...
!
CALL remove_even_multiple_zeros(A,odd_multiplicity_flag,local_verbose)
if (local_verbose) then
write(*,*)'remove even multiple zeros: A(w**2)'
CALL write_poly_local(A)
write(*,*)'Full precision coefficients'
do i=0,A%order
write(*,*)i,A%coeff(i)
end do
end if
! Checks for a valid at this point
if (a%coeff(a%order).EQ.0d0) then
write(*,*)'Error in test_filter_positive_real. Highest order A coefficient =0d0'
end if
! Start to assemble the terms of the Sturm sequence
n_sturm=a_order+1
allocate( value_inf(1:n_sturm) )
allocate( value_0(1:n_sturm) )
! We already have the first term which is A
Ap=A
sturm_coeff=1
! Get a function sign for x->infinity
value_inf(sturm_coeff)=0d0
do ii=Ap%order,0,-1
if (abs(Ap%coeff(ii)).GT.zero_test_small) then
value_inf(sturm_coeff)=Ap%coeff(ii)
exit
end if
end do
! Get a function sign for x->0
value_0(sturm_coeff)=0d0
do ii=0,Ap%order
if (abs(Ap%coeff(ii)).GT.zero_test_small) then
value_0(sturm_coeff)=Ap%coeff(ii)
exit
end if
end do
if (local_verbose) then
write(*,*)'A0(x)='
CALL write_poly_local2('A','x',Ap)
end if
if (Ap%order.Eq.0) then
! this is a zero order function so exit
if (local_verbose) then
write(*,*)'Zero order function, exiting'
end if
RETURN
end if
! second term is the derivative of Ap wrt x
Am=allocate_polynomial(ap%order-1)
do i=0,am%order
am%coeff(i)=ap%coeff(i+1)*dble(i+1)
end do
sturm_coeff=sturm_coeff+1
! Get a function sign for x->infinity
value_inf(sturm_coeff)=0d0
do ii=Am%order,0,-1
if (abs(Am%coeff(ii)).GT.zero_test_small) then
value_inf(sturm_coeff)=Am%coeff(ii)
exit
end if
end do
! Get a function sign for x->0
value_0(sturm_coeff)=0d0
do ii=0,Am%order
if (abs(Am%coeff(ii)).GT.zero_test_small) then
value_0(sturm_coeff)=Am%coeff(ii)
exit
end if
end do
if (local_verbose) then
write(*,*)'A1(x)='
CALL write_poly_local2('A','x',Am)
end if
! now iterate to get the remaining Sturm coefficients
do i=3,n_sturm
! we have the two previous terms Ap and Am.
! The next term is found as the remainder when Ap is divided by Am
! R(x)=Ap(x)-Am(x)(C(x)) where C(x)=k1x+k2
! New Ap=Am
! New Am=-R
CALL divide_poly(Ap,Am,C,R,.FALSE.)
if (polynomial_is_zero(R)) then
n_sturm=i-1
if (local_verbose) then
write(*,*)'Zero remainder in Sturm sequence calculation'
write(*,*)'Number of Sturm coefficients=',n_sturm
end if
exit
end if
deallocate( Ap%coeff )
Ap=Am
deallocate( Am%coeff )
Am=R
do ii=0,Am%order
Am%coeff(ii)=-Am%coeff(ii)
end do
deallocate( R%coeff )
deallocate( C%coeff )
sturm_coeff=sturm_coeff+1
! Get a function sign for x->infinity
value_inf(sturm_coeff)=0d0
do ii=Am%order,0,-1
if (abs(Am%coeff(ii)).GT.zero_test_small) then
value_inf(sturm_coeff)=Am%coeff(ii)
exit
end if
end do
! Get a function sign for x->0
value_0(sturm_coeff)=0d0
do ii=0,Am%order
if (abs(Am%coeff(ii)).GT.zero_test_small) then
value_0(sturm_coeff)=Am%coeff(ii)
exit
end if
end do
if (local_verbose) then
write(*,'(A1,I1,A4)')'A',i-1,'(x)='
CALL write_poly_local2('A','x',Am)
end if
end do
! Work out the number of sign changes in value_inf and value_0 in the sequence
if (local_verbose) then
write(*,*)'Number of Sturm coefficients=',n_sturm
write(*,*)'Sturm coefficients'
write(*,*)' A(0) A(infinity) '
do i=1,n_sturm
write(*,*)value_0(i),value_inf(i)
end do
end if
S_inf=0
S_0=0
last_value_inf=value_inf(1)
last_value_0=value_0(1)
do i=2,n_sturm
if (value_0(i)*last_value_0.LT.0d0) S_0=S_0+1
last_value_0=value_0(i)
if (value_inf(i)*last_value_inf.LT.0d0) S_inf=S_inf+1
last_value_inf=value_inf(i)
end do
n_real_zeros=abs(S_inf-S_0)
if (local_verbose) then
write(*,8000)'n_sign changes_0=',S_0,' n_sign_changes_inf=',S_inf
8000 format(A,I3,A,I3)
write(*,*)'Number of real zeros =',n_real_zeros
write(*,*)'Odd_multiplicity_flag=',odd_multiplicity_flag
end if
if (n_real_zeros.GT.0) stable=.FALSE.
! check the consistency between the Sturm test and
! the previously calculated odd_multiplicity_flag
if ( (n_real_zeros.GT.0).AND.(odd_multiplicity_flag) ) then
! consistent result
if (local_verbose) write(*,*)'Sturm test and explicit zero analysis agree: stable=.FALSE.'
else if ( (n_real_zeros.EQ.0).AND.(.NOT.odd_multiplicity_flag) ) then
! consistent result
if (local_verbose) write(*,*)'Sturm test and explicit zero analysis agree: stable=.TRUE.'
else
! inconsistent result
write(*,*)'Sturm test and explicit zero analysis do NOT agree'
write(*,8000)'n_sign changes_0=',S_0,' n_sign_changes_inf=',S_inf
write(*,*)'Number of real zeros =',n_real_zeros
write(*,*)'Odd_multiplicity_flag=',odd_multiplicity_flag
! STOP
end if
! Deallocate arrays and polynomials
deallocate( value_inf )
deallocate( value_0 )
RETURN
END SUBROUTINE test_filter_positive_real