! ! 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 BRUNE_test(H,type,CFtype,R,L1out,C,L2out,K,found,HR,remainder_OK,remainder_zero) ! SUBROUTINE LC_test_BRUNE(H_PR,L,C,found,HR,remainder_OK,remainder_zero) ! SUBROUTINE L_test_BRUNE(H_PR,L,found,HR,remainder_OK,remainder_zero) ! ! ! NAME ! BRUNE_test ! ! DESCRIPTION ! look for a viable BRUNE branch in a given impedance function ! See sections 7.2.4 of the Theory manual ! ! SEE ALSO ! ! ! HISTORY ! ! started 14/09/17 CJS ! SUBROUTINE BRUNE_test(H,type,CFtype,R,L1out,C,L2out,K,found,HR,remainder_OK,remainder_zero) USE type_specifications USE general_module USE constants USE filter_module IMPLICIT NONE type(Sfilter),INTENT(INOUT) :: H integer :: type integer :: CFtype real(dp):: R,L1out,L2out,C,K logical :: found type(Sfilter),INTENT(INOUT) :: HR logical :: remainder_OK,remainder_zero ! local variables real(dp) :: w0,f0 complex(dp) :: Xw0 type(Sfilter) :: Z2,Y2 type(Sfilter_PR) :: Y2_PR type(Sfilter) :: Y3 type(Sfilter) :: Z3 type(Sfilter_PR) :: Z3_PR type(Sfilter) :: temp_filter real(dp):: L1,L2,L3,M !START if (verbose) write(*,*)'CALLED: BRUNE_test' found=.FALSE. ! Calculate the resistance which reduces H_PR to a minimum resistive impedance function ! and the frequency at which the resistance is zero CALL calculate_min_resistance_function(H,R,w0) ! evaluate the min_resistance filter at w0. At this frequency the response is purely imaginary. f0=w0/(2d0*pi) f0=w0*H%wnorm/(2d0*pi) if (abs(f0).LT.zero_test_small) then w0=zero_test_small f0=w0*H%wnorm/(2d0*pi) end if Xw0=evaluate_Sfilter_frequency_response(H,f0) L1=aimag(Xw0)/w0 if (verbose) then write(*,*)'L1=',L1 end if ! subtract this inductive impedance from the filter function Z2(s)=H(s)-L1*s temp_filter=allocate_Sfilter(1,0) temp_filter%wnorm=H%wnorm temp_filter%a%coeff(0)=0d0 temp_filter%a%coeff(1)=-L1 temp_filter%b%coeff(0)=1d0 Z2=AddSfilter(H,temp_filter) CALL get_min_order_poly(Z2%a) if (verbose) then write(*,*)'Z2 filter function:' CALL write_Sfilter(Z2,0) end if Y2=reciprocal_Sfilter(Z2) if (verbose) then write(*,*)'Y2 filter function:' CALL write_Sfilter(Y2,0) end if ! now extract an L-C series admittance from Y2 Y2_PR=Convert_filter_S_to_S_PR(Y2) if (verbose) CALL write_S_PR_filter(Y2_PR) CALL LC_test_BRUNE(Y2_PR,L2,C,found,Y3,remainder_OK,remainder_zero) if (verbose) then write(*,*)'CALLED LC_test_BRUNE' write(*,*)'L2=',L2 write(*,*)'C =',C write(*,*)'found =',found write(*,*)'remainder_OK =',remainder_OK write(*,*)'remainder_zero =',remainder_zero write(*,*)'remainder pole residue function' CALL write_Sfilter(Y3,0) end if Z3=reciprocal_Sfilter(Y3) ! write(*,*)'Z3:' ! CALL write_Sfilter(Z3,0) Z3_PR=Convert_filter_S_to_S_PR(Z3) ! extract a series inductance (which may be negative) CALL L_test_BRUNE(Z3_PR,L3,found,HR,remainder_OK,remainder_zero) if (verbose) then write(*,*)'CALLED L_test_BRUNE' write(*,*)'L3=',L3 write(*,*)'found =',found write(*,*)'remainder_OK =',remainder_OK write(*,*)'remainder_zero =',remainder_zero write(*,*)'remainder pole residue function' end if ! we can only proceed if the remainder is OK at this point if (found.AND.remainder_OK) then ! carry on and work out the mutual impedance form which ! should eliminate the negative inductance L1out=L1+L2 L2out=L3+L2 M=L2 K=1d0 L1out=L1out/H%wnorm L2out=L2out/H%wnorm C=C/H%wnorm ! check for positive component values if (R.LT.0d0) then if (verbose) write(*,*)'Error, R<0, R =',R found=.FALSE. end if if (L1out.LT.0d0) then if (verbose) write(*,*)'Error, L1<0, L1=',L1 found=.FALSE. end if if (L2out.LT.0d0) then if (verbose) write(*,*)'Error, L2<0, L2=',L2 found=.FALSE. end if if (K.LT.0d0) then if (verbose) write(*,*)'Error, K<0, K =',K found=.FALSE. end if if (C.LT.0d0) then if (verbose) write(*,*)'Error, C<0, C =',C found=.FALSE. end if if (found) GOTO 8000 end if remainder_OK=.FALSE. found=.FALSE. remainder_zero=.FALSE. RETURN 8000 CONTINUE ! jump here if we have found a viable BRUNE branch CFtype=series_BRUNE found=.TRUE. if (verbose) then write(*,*)'FOUND VIABLE BRUNE BRANCH' write(*,*)'R =',R write(*,*)'L1=',L1out write(*,*)'L2=',L2out write(*,*)'C =',C write(*,*)'K=',K write(*,*)'remainder_OK :',remainder_OK write(*,*)'remainder_zero :',remainder_zero write(*,*)'Remainder impedance:' CALL write_Sfilter(HR,0) end if RETURN END SUBROUTINE BRUNE_test ! ! NAME ! LC_test_BRUNE ! ! DESCRIPTION ! look for a viable LC branch in a given impedance/admittance function ! This is specific to the Brune synthesis as the inductance can be negative ! so the checks are different ! ! SEE ALSO ! ! ! HISTORY ! ! started 14/09/17 CJS ! SUBROUTINE LC_test_BRUNE(H_PR,L,C,found,HR,remainder_OK,remainder_zero) USE type_specifications USE general_module USE constants USE filter_module IMPLICIT NONE type(Sfilter_PR),INTENT(IN) :: H_PR integer :: type integer :: CFtype real(dp):: R,L,C logical :: found type(Sfilter),INTENT(INOUT) :: HR logical :: remainder_OK,remainder_zero ! local variables integer :: first_complex_pole,pole1,pole2,pole type(Sfilter_PR) :: HR_PR_local logical :: stable integer :: i,ii real(dp) :: diff,mag_diff,mag1,mag2 real(dp) :: const_term logical :: imaginary_poles,positive_residues,zero_const_term !START if (verbose) write(*,*)'CALLED: LC_test_BRUNE' found=.FALSE. first_complex_pole=H_PR%n_real_poles+1 ! loop over complex pole pairs do i=1,H_PR%n_complex_pole_pairs pole1=first_complex_pole+(i-1)*2 pole2=pole1+1 ! check that we have a conjugate pole pair if ( .NOT.conjugate_pair(H_PR%poles(pole1),H_PR%poles(pole2)) ) then write(*,*)'ERROR in LC_test_BRUNE: poles are not a conjugate pair' write(*,*)'pole 1:',H_PR%poles(pole1) write(*,*)'pole 2:',H_PR%poles(pole2) STOP end if ! check that we have a conjugate residue pair if ( .NOT.conjugate_pair(H_PR%residues(pole1),H_PR%residues(pole2)) ) then write(*,*)'ERROR in LC_test_BRUNE: residues are not a conjugate pair' write(*,*)'residues 1:',H_PR%residues(pole1) write(*,*)'residues 2:',H_PR%residues(pole2) STOP end if ! test for whether we have an LC branch here... imaginary_poles =imaginary_pair(H_PR%poles(pole1),H_PR%poles(pole2)) positive_residues=(dble(H_PR%residues(pole1)).GT.0d0) const_term=dble(H_PR%residues(pole1))*dble(H_PR%poles(pole1))+ & aimag(H_PR%poles(pole1))*aimag(H_PR%residues(pole1)) zero_const_term=(abs(const_term).LT.zero_test_small) if (verbose) then write(*,*)'Testing pole pair',i write(*,*)'Tests:' write(*,*)'imaginary pole test : ',imaginary_poles,' ',H_PR%poles(pole1) write(*,*)'positive residue test : ',positive_residues,' ',H_PR%residues(pole1) write(*,*)'zero constant term test: : ',zero_const_term,' ', const_term end if ! check for stable poles which are not on the imaginary s=jw axis AND positive residues if (imaginary_poles.AND.positive_residues.AND.zero_const_term) then if (verbose) write(*,*)'Found possible LC branch' ! this could be a viable LC branch - calculate the remainder when this pole pair is removed CALL deallocate_Sfilter(HR) ! Test whether the remainder is zero ! build a local pole-residue filter without the test pole pair. ! allocate the structure for the local pole-residue form function and copy the ! required information across HR_PR_local%wnorm=H_PR%wnorm HR_PR_local%order=H_PR%order-2 HR_PR_local%n_real_poles=H_PR%n_real_poles HR_PR_local%n_complex_poles=H_PR%n_complex_poles-2 HR_PR_local%n_complex_pole_pairs=H_PR%n_complex_pole_pairs-1 HR_PR_local%n_real_poles=H_PR%n_real_poles ! constant term and sL term HR_PR_local%R=H_PR%R HR_PR_local%L=H_PR%L ! Test whether the remainder is zero if ( (HR_PR_local%order.EQ.0).AND. & (abs(HR_PR_local%R).LT.zero_test_R).AND. & (abs(HR_PR_local%L).LT.zero_test_L) ) then remainder_zero=.TRUE. GOTO 8000 end if ! copy any poles/ residues in the remainder ALLOCATE( HR_PR_local%complex_pole(HR_PR_local%order) ) ALLOCATE( HR_PR_local%poles(HR_PR_local%order) ) ALLOCATE( HR_PR_local%residues(HR_PR_local%order) ) ! copy real poles pole1=0 pole2=0 do ii=1,HR_PR_local%n_real_poles pole1=pole1+1 pole2=pole2+1 HR_PR_local%complex_pole(pole1)=.FALSE. HR_PR_local%poles(pole1) =H_PR%poles(pole2) HR_PR_local%residues(pole1)=H_PR%residues(pole2) end do ! next real pole ! copy complex poles in pairs do ii=1,H_PR%n_complex_pole_pairs if (ii.NE.i) then ! this is not the pole pair we are trying to remove so copy them over pole1=pole1+1 pole2=pole2+1 HR_PR_local%complex_pole(pole1)=.TRUE. HR_PR_local%poles(pole1)=H_PR%poles(pole2) HR_PR_local%residues(pole1)=H_PR%residues(pole2) pole1=pole1+1 pole2=pole2+1 HR_PR_local%complex_pole(pole1)=.TRUE. HR_PR_local%poles(pole1)=H_PR%poles(pole2) HR_PR_local%residues(pole1)=H_PR%residues(pole2) else ! this is the pair of poles we wish to remove so just increase the pole2 counter by 2. pole2=pole2+2 end if end do ! next real pole ! convert to a rational function form HR=Convert_filter_S_PR_to_S(HR_PR_local) ! Check the transfer funcion for stability and for whether it is positive real CALL check_transfer_function(HR,stable) CALL deallocate_Sfilter_PR(HR_PR_local) if (verbose) then if (stable) then write(*,*)'Remainder is stable' else write(*,*)'Remainder is unstable' end if end if remainder_zero=.FALSE. GOTO 8000 end if ! positive residues for this complex pole pair end do ! next complex pole pair ! we only get here if we have not found a viable LC branch remainder_OK=.FALSE. found=.FALSE. remainder_zero=.FALSE. RETURN 8000 CONTINUE ! jump here if we have found a viable LC branch remainder_OK=.TRUE. found=.TRUE. CALL deallocate_Sfilter_PR(HR_PR_local) pole1=first_complex_pole+(i-1)*2 pole2=pole1+1 L=1d0/dble(H_PR%residues(pole1)+H_PR%residues(pole2)) C= dble( (H_PR%residues(pole1)+H_PR%residues(pole2))/(H_PR%poles(pole1)*H_PR%poles(pole2)) ) if (verbose) then write(*,*)'FOUND VIABLE LC BRANCH' write(*,*)'L=',L write(*,*)'C=',C write(*,*)'remainder_OK :',remainder_OK write(*,*)'remainder_zero :',remainder_zero end if RETURN END SUBROUTINE LC_test_BRUNE ! ! NAME ! L_test_BRUNE ! ! DESCRIPTION ! look for a viable series inductance in the BRUNE model which may be negative. ! ! SEE ALSO ! ! ! HISTORY ! ! started 14/09/17 CJS ! SUBROUTINE L_test_BRUNE(H_PR,L,found,HR,remainder_OK,remainder_zero) USE type_specifications USE general_module USE constants USE filter_module IMPLICIT NONE type(Sfilter_PR),INTENT(IN) :: H_PR real(dp):: L logical :: found type(Sfilter),INTENT(INOUT) :: HR logical :: remainder_OK,remainder_zero ! local variables integer :: pole,pole1,pole2 type(Sfilter_PR) :: HR_PR_local logical :: stable integer :: i,ii !START if (verbose) write(*,*)'CALLED: L_test_BRUNE' found=.FALSE. if (verbose) write(*,*)'Found possible L branch' ! this could be a viable L branch - calculate the remainder when this pole is removed CALL deallocate_Sfilter(HR) ! Test whether the remainder is zero ! build a local pole-residue filter without the test pole ! allocate the structure for the local pole-residue form function and copy the ! required information across HR_PR_local%wnorm=H_PR%wnorm HR_PR_local%order=H_PR%order HR_PR_local%n_real_poles=H_PR%n_real_poles HR_PR_local%n_complex_poles=H_PR%n_complex_poles HR_PR_local%n_complex_pole_pairs=H_PR%n_complex_pole_pairs HR_PR_local%n_real_poles=H_PR%n_real_poles ! constant term and sL term HR_PR_local%R=H_PR%R HR_PR_local%L=0d0 ! Test whether the remainder is zero if ( (HR_PR_local%order.EQ.0).AND. & (abs(HR_PR_local%R).LT.zero_test_R).AND. & (abs(HR_PR_local%L).LT.zero_test_L) ) then remainder_zero=.TRUE. GOTO 8000 end if ! copy any poles/ residues in the remainder ALLOCATE( HR_PR_local%complex_pole(HR_PR_local%order) ) ALLOCATE( HR_PR_local%poles(HR_PR_local%order) ) ALLOCATE( HR_PR_local%residues(HR_PR_local%order) ) ! copy real poles pole1=0 pole2=0 do ii=1,HR_PR_local%n_real_poles pole1=pole1+1 pole2=pole2+1 HR_PR_local%complex_pole(pole1)=.FALSE. HR_PR_local%poles(pole1) =H_PR%poles(pole2) HR_PR_local%residues(pole1)=H_PR%residues(pole2) end do ! next real pole ! copy complex poles in pairs do ii=1,H_PR%n_complex_pole_pairs pole1=pole1+1 pole2=pole2+1 HR_PR_local%complex_pole(pole1)=.TRUE. HR_PR_local%poles(pole1)=H_PR%poles(pole2) HR_PR_local%residues(pole1)=H_PR%residues(pole2) pole1=pole1+1 pole2=pole2+1 HR_PR_local%complex_pole(pole1)=.TRUE. HR_PR_local%poles(pole1)=H_PR%poles(pole2) HR_PR_local%residues(pole1)=H_PR%residues(pole2) end do ! next real pole ! convert to a rational function form HR=Convert_filter_S_PR_to_S(HR_PR_local) ! Check the transfer funcion for stability and for whether it is positive real CALL check_transfer_function(HR,stable) CALL deallocate_Sfilter_PR(HR_PR_local) if (verbose) then if (stable) then write(*,*)'Remainder is stable' else write(*,*)'Remainder is unstable' end if end if if (stable) then remainder_zero=.FALSE. GOTO 8000 end if ! we only get here if we have not found a viable L branch remainder_OK=.FALSE. found=.FALSE. remainder_zero=.FALSE. RETURN 8000 CONTINUE ! jump here if we have found a viable RC branch remainder_OK=.TRUE. found=.TRUE. CALL deallocate_Sfilter_PR(HR_PR_local) L=H_PR%L if (verbose) then write(*,*)'FOUND VIABLE SERIES L BRANCH' write(*,*)'L=',L write(*,*)'remainder_OK :',remainder_OK write(*,*)'remainder_zero :',remainder_zero end if RETURN END SUBROUTINE L_test_BRUNE