!
! 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 network_synthesis
!
! NAME
! network_synthesis
!
! AUTHORS
! Chris Smartt
!
! DESCRIPTION
! testing some ideas for the implementation of rational transfer functions as
! passive equivalent circuits
!
! PROCESS:
!
! 1. Test that the rational function does constitute a physical impedance function
! 2. Generate a ladder network which synthesises this impedance function where the
! elements of the ladder network consist of RLC elements
! 3. Evaluate the frequency response of the original function
! 4. Evaluate the frequency response of the continued fraction form
! 5. Write a Spice file for the network
!
!
! COMMENTS
! The network synthesis algorithm is described in the Theory Manual, section 7.2.1
!
! HISTORY
!
! started 22/08/2017 CJS
! 14/9/2017 CJS Generalise the circuit elements to RLC combinations
! using partial fraction expansions
! 10/2017 CJS include Brune synthesis method to complete the process
!
!
SUBROUTINE network_synthesis(Hin,gain_in,Hname,in1,in2,on1,on2,vref_node,next_free_node,unit)
USE type_specifications
USE general_module
USE constants
USE frequency_spec
USE filter_module
USE Sfilter_fit_module
IMPLICIT NONE
type(Sfilter),intent(IN) :: Hin ! rational transfer function model
real(dp),intent(IN) :: gain_in
character(LEN=spice_name_length),intent(IN) :: Hname
integer,intent(IN) :: in1,in2 ! controlling nodes
integer,intent(IN) :: on1,on2 ! controlled (output) nodes
integer,intent(IN) :: vref_node ! local reference node
integer,intent(INOUT):: next_free_node ! next free node node
integer,intent(IN) :: unit ! file unit for Spice sub-circuit
! local variables
type(Sfilter) :: H ! rational transfer function model
type(Sfilter) :: HR ! rational transfer function model
type(Sfilter_PR) :: H_PR ! pole-residue transfer function model
type(Sfilter) :: T1 ! temporary filter function
type(Sfilter) :: Rdc_filter ! temporary filter function
integer :: aorder,border,max_order,CF_dim
integer :: n_branches
integer :: test
integer :: i,loop,type_loop ! loop variables
type(Polynomial) :: num
type(Polynomial) :: den
real(dp) :: value
real(dp),allocatable :: CFterm(:,:)
integer,allocatable :: CFtype(:)
real(dp) :: wmin,wmax,wstep
integer :: nw
complex(dp) :: s,sn,num_fs,den_fs,CF_term,last_CF_term
complex(dp) :: H_rational,H_CF
real(dp) :: R,L,C
real(dp) :: R_min ! minimum value of the resistance of the input function
real(dp) :: w_R_min ! angular frequency for minimum value of the resistance of the input function
real(dp) :: R_add ! additional resistance required to make the function positive real
logical :: stable
logical :: found
logical :: remainder_OK
logical :: remainder_zero
logical :: multiple_poles
integer :: type,last_type
real(dp) :: term
integer :: on,od
logical :: pole_at_zero
logical :: zero_at_zero
real(dp) :: ascale,bscale,scale
real(dp) :: gain
! START
if (verbose) then
write(*,*)'******************************'
write(*,*)'CALLED Equivalent_Circuit_Test'
write(*,*)'******************************'
end if
! verbose=.TRUE.
if (verbose) then
write(*,*)'CALLED with function:'
CALL write_Sfilter(Hin,0)
end if
! copy the input filter and renormalise it
! Set an appropriate normalisation for the filter function
! so that the coefficients don't get out of hand.
H=renormalise_Sfilter(Hin)
if (verbose) then
write(*,*)'Renormalised filter:'
CALL write_Sfilter(H,0)
end if
! CALL get_min_order_poly(H%a)
! CALL get_min_order_poly(H%b)
! get a 'scale' for the filter function and include this in the gain term
! to prevent component values getting too big or small
ascale=abs(H%a%coeff(H%a%order))
bscale=abs(H%b%coeff(H%b%order))
H%a%coeff(:)=H%a%coeff(:)/ascale
H%b%coeff(:)=H%b%coeff(:)/bscale
scale=ascale/bscale
gain=gain_in*scale
if (verbose) then
write(*,*)'Scaled function:'
CALL write_Sfilter(H,0)
write(*,*)'Calculate minimum resistance of function:'
end if
CALL calculate_min_resistance_value(H,R_min,w_R_min)
if (verbose) then
write(*,*)'Minimum Resistance value is',R_min
write(*,*)'at w=',w_R_min,' f=',w_R_min/(2d0*pi)
end if
if (R_min.LT.0d0) then
if (verbose) write(*,*)'Adding 1.5*abs(Minimum Resistance value) to H'
R_add=1.5d0*abs(R_min)
Rdc_filter=allocate_Sfilter(0,0)
Rdc_filter%wnorm=Hin%wnorm
Rdc_filter%a%coeff(0)=R_add
Rdc_filter%b%coeff(0)=1d0
CALL deallocate_Sfilter(T1)
T1=H+Rdc_filter
CALL deallocate_Sfilter(H)
H=T1
CALL deallocate_Sfilter(T1)
CALL deallocate_Sfilter(Rdc_filter)
else
R_add=0d0
end if
if (verbose) then
write(*,*)'Revised H:'
CALL write_Sfilter(H,0)
end if
! now we have ensured that Re(H)>=0 at all frequencies, do the checks again.
! Check the transfer funcion for stability and for whether it is positive real
CALL check_transfer_function(H,stable)
if (stable) then
if (verbose) write(*,*)'INPUT FUNCTION IS A STABLE, PHYSICAL IMPEDANCE'
else
if (verbose) write(*,*)'INPUT FUNCTION IS NOT STABLE'
run_status='INPUT FUNCTION IS NOT STABLE, even after adding a stabilising d.c. resistance'
CALL write_program_status()
STOP
end if
! Max_order is the maximum number of components, including
! resistive and reactive components
max_order=2*max(H%a%order,H%b%order) +1
if (verbose) then
write(*,*)'Maximum order is estimated to be ',max_order
end if
! allocate the continued fraction data
CF_dim=max_order
allocate( CFterm(1:max_order,1:5) ) ! note 5 terms
CFterm(:,:)=0d0
allocate( CFtype(1:max_order) )
CFtype(:)=0
n_branches=0
type=type_impedance ! H(s) is an impedance function to start with
do loop=1,max_order
remainder_OK=.FALSE.
remainder_zero=.FALSE.
! Loop for trying both impedance and admittance functions
do type_loop=1,2
if (verbose) then
write(*,*)'Stage ',loop,' of ',max_order
if (type.EQ.type_impedance) then
write(*,*)'TRYING TO CALCULATE AN IMPEDANCE'
else
write(*,*)'TRYING TO CALCULATE AN ADMITTANCE'
end if
end if
! check the function for multiple poles. If there are no multiple poles then we can
! go on and look for viable branches
CALL check_for_multiple_roots(H%b,multiple_poles,.TRUE.)
if (.NOT.multiple_poles) then
! Calculate the partial fraction expansion of the function H(s)
H_PR=Convert_filter_S_to_S_PR(H)
if (verbose) CALL write_S_PR_filter(H_PR)
do test=1,8
! Test number 1: looking for RLC branch
select case (test)
case(1)
! Test number 1: looking for RLC branch
CALL RLC_test(H_PR,type,CFtype(loop),CFterm(loop,1),CFterm(loop,2),CFterm(loop,3),found, &
HR,remainder_OK,remainder_zero)
case(2)
! Test number 2: looking for LC branch
CALL LC_test(H_PR,type,CFtype(loop),CFterm(loop,1),CFterm(loop,2),CFterm(loop,3),found, &
HR,remainder_OK,remainder_zero)
case(3)
! Test number 3: looking for RC branch
CALL RC_test(H_PR,type,CFtype(loop),CFterm(loop,1),CFterm(loop,2),CFterm(loop,3),found, &
HR,remainder_OK,remainder_zero)
case(4)
! Test number 4: looking for RL branch
CALL RL_test(H_PR,type,CFtype(loop),CFterm(loop,1),CFterm(loop,2),CFterm(loop,3),found, &
HR,remainder_OK,remainder_zero)
case(5)
! Test number 5: looking for C branch
CALL C_test(H_PR,type,CFtype(loop),CFterm(loop,1),CFterm(loop,2),CFterm(loop,3),found, &
HR,remainder_OK,remainder_zero)
case(6)
! Test number 6: looking for L branch
CALL L_test(H_PR,type,CFtype(loop),CFterm(loop,1),CFterm(loop,2),CFterm(loop,3),found, &
HR,remainder_OK,remainder_zero)
case(7)
! Test number 7: looking for R branch
CALL R_test(H_PR,type,CFtype(loop),CFterm(loop,1),CFterm(loop,2),CFterm(loop,3),found, &
HR,remainder_OK,remainder_zero)
case(8)
! Test number 8: looking for R2 branch
CALL R2_test(H_PR,type,CFtype(loop),CFterm(loop,1),CFterm(loop,2),CFterm(loop,3),found, &
HR,remainder_OK,remainder_zero)
end select
if (found.AND.remainder_OK) then ! Adopt this as a viable branch
n_branches=n_branches+1
if (remainder_zero) then
GOTO 2000
else
GOTO 1000
end if
end if
end do ! next test
end if
! If we have been unsuccessful finding a circuit element
! change from impedance->admittance of vice vers
if (type.EQ.type_impedance) then
type=type_admittance
else
type=type_impedance
end if
! Calculate the reciprocal filter function
num=H%a
den=H%b
CALL deallocate_poly(H%a)
CALL deallocate_poly(H%b)
H%a=den
H%b=num
CALL deallocate_poly(num)
CALL deallocate_poly(den)
end do ! next type_loop
! if we get here then we have not found a viable way to proceed with
! building the circuit
if (verbose) write(*,*)'CANNOT FIND VIABLE COMPONENT'
if (verbose) write(*,*)'TRYING BRUNE CYCLE'
! convert to an impedance function if required
if (type.EQ.type_admittance) then
type=type_impedance
! Calculate the reciprocal filter function
num=H%a
den=H%b
CALL deallocate_poly(H%a)
CALL deallocate_poly(H%b)
H%a=den
H%b=num
CALL deallocate_poly(num)
CALL deallocate_poly(den)
end if
CALL BRUNE_test(H,type,CFtype(loop),CFterm(loop,1),CFterm(loop,2),CFterm(loop,3), &
CFterm(loop,4),CFterm(loop,5),found, &
HR,remainder_OK,remainder_zero)
if (found.AND.remainder_OK) then ! Adopt this as a viable branch
n_branches=n_branches+1
if (remainder_zero) then
GOTO 2000
else
GOTO 1000
end if
end if
run_status='ERROR: CANNOT FIND VIABLE COMPONENT'
CALL write_program_status()
STOP
! jump here when we have found the next circuit element
1000 CONTINUE
if (verbose) write(*,*)'Prepare for the next stage'
CALL deallocate_Sfilter(H)
H=HR
end do ! next circuit element
! If we end up here then there is a problem because the remainder is not zero
! and we are supposed to have worked out all the circuit elements by now.
! jump here when the continued fraction truncates with a zero remainder
2000 continue
! CALL write_CF_local(CFterm,CFtype,CF_dim,n_branches)
if (verbose) write(*,*)''
if (verbose) then
INCLUDE 'include_write_frequency_response.F90'
end if
! Write a spice circuit model for the ladder network derived from the
! continued fraction expansion
CALL write_ladder_network(Hname,gain,CFterm,CFtype,CF_dim,n_branches,R_add,nw,wmin,wmax, &
in1,in2,on1,on2,vref_node,next_free_node,unit)
! deallocate memory and finish up
CALL deallocate_poly(num)
CALL deallocate_poly(den)
CALL deallocate_Sfilter(H)
CALL deallocate_Sfilter(HR)
CALL deallocate_Sfilter(T1)
CALL deallocate_Sfilter(Rdc_filter)
deallocate( CFterm )
RETURN
9000 write(*,*)'Error reading transfer function file'
STOP
END SUBROUTINE network_synthesis