!
! 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 PUL_LC_Laplace
!
! NAME
! SUBROUTINE PUL_LC_Laplace
!
! DESCRIPTION
! Wrapping subroutine to control the calculation of PUL_parameters by the Finite Element method
! which is implemented in Laplace.F90
!
! The finite element mesh is greated by the open source software gmsh: see http://gmsh.info/
!
! The process is divided into the following stages:
! STAGE 1: work out the configuration for the calculation i.e. is there a ground plane, is the outer boundary free space or a conductor (overshield)
! STAGE 2: Work out the numbers of conductors, points, lines, line loops and surfaces in the cross section geometry
! STAGE 3: Create the input file for the mesh generator, gmsh.
! STAGE 4: Call the mesh generator, gmsh
! STAGE 5: Call Laplace to calculate the L,C and G matrices, using the mesh that we have just generated
!
! COMMENTS
! This subroutine is never called in the case of a homogeneous frequency dependent dielectric region
! If this were to change then the logic of how Laplace is called in stage 5 will have to change to
! take this into account.
!
! HISTORY
! started 5/7/2016 CJS.
! add dielectric regions 13/7/2016 CJS.
! Consolidated ground_plane, overhsield and open_boundary subroutines into a single subroutine here 13/12/2016 CJS.
! 14/10/2017 CJS make the filter fitting minimum aorder=1, border=0 and
! ensure that border=aorder-1 to make the choice of model order more sensible
! 16/11/2017 CJS Include network synthesis process to replace s-domain transfer functions
! 4/3/2018 CJS Start to include the infinite ground plane option
!
! 16/3/2018 CJS add offset to x and y for shapes. Relates to ML_flex cables
!
SUBROUTINE PUL_LC_Laplace(PUL,name,fit_order,freq_spec,domain)
!
USE type_specifications
USE constants
USE general_module
USE maths
USE filter_module
USE filter_module
USE Sfilter_fit_module
USE frequency_spec
!
IMPLICIT NONE
! variables passed to subroutine
type(PUL_type), intent(INOUT) :: PUL ! per-unit-length parameter calculation structure
character(LEN=line_length),intent(IN) :: name ! string used as the base for filenames
integer, intent(IN) :: fit_order ! filter fit_order for frequency dependent dielectrics
type(frequency_specification),intent(IN) :: freq_spec ! filter frequency range specification for frequency dependent dielectrics
integer, intent(IN) :: domain ! domain number used to label the mesh files
! parameters
integer,parameter :: inside =1
integer,parameter :: outside=2
! local variables
! flags to indicate the configuration
logical :: ground_plane
logical :: finite_ground_plane
logical :: infinite_ground_plane
logical :: overshield
logical :: open_boundary
! counters and loop limits which depend on the configuration required
integer :: end_conductor_loop
integer :: end_conductor_to_dielectric_loop
integer :: tot_n_boundaries_without_dielectric
integer :: first_conductor_shape
! local variables
character(LEN=filename_length) :: geom_filename ! filename for the geometry - i.e. the input file for gmsh
character(LEN=filename_length) :: mesh_filename ! filename for the mesh - i.e. the output file from gmsh
! temposrary strings used to construct filenames
character(LEN=filename_length) :: string1,string2
character(LEN=filename_length) :: temp_string
character(LEN=filename_length) :: mesh_name
real(dp) :: xmin,xmax,ymin,ymax ! extent of bundle conductors
real(dp) :: xc,yc ! centre of bundle conductors
real(dp) :: rboundary ! free space boundary radius
! variables required for generating the gmsh geometry input file: we may be able to simplify
! things here as there is some overlap with the PUL structure
integer,allocatable :: shape_type(:) ! holds a nuber which indicates the shape type
integer,allocatable :: loop_number(:) ! holds the loop number for this shape
real(dp),allocatable :: x(:) ! x coordinate of the centre of the cable to which this shape belongs
real(dp),allocatable :: y(:) ! y coordinate of the centre of the cable to which this shape belongs
real(dp),allocatable :: r(:) ! radius of a circular shape or curve radius for a Dshape
real(dp),allocatable :: rw(:) ! width1 (x dimension) of rectangular/ Dshape
real(dp),allocatable :: rw2(:) ! width2 (x dimension) of rectangular/ Dshape
real(dp),allocatable :: rh(:) ! height (y dimension) of rectangular shape
real(dp),allocatable :: rox(:) ! offset in the x direction of this shape from the centre (x():),y(:) above)
real(dp),allocatable :: roy(:) ! offset in the y direction of this shape from the centre (x():),y(:) above)
real(dp),allocatable :: rtheta(:) ! rotation angle of conductor
real(dp),allocatable :: dl(:) ! edge length for the mesh on this shape
logical,allocatable :: conductor_has_dielectric(:) ! flag to indicate that a conductor has a dielectric around it
real(dp) :: dl_gp ! edge length at the centre of the ground plane
real(dp) :: dl_local ! edge length for the mesh at rectangle edge centre
complex(dp) :: epsr ! relative permittivity value
integer,allocatable :: shape_to_region(:,:) ! array which stores the inner and outer region numbers for each shape
! undefined regions (inside PEC or outside the outer boundary) are set to -1
logical :: first_surface_is_free_space_boundary ! flag to indicate properties on the first surface defined
! counters for conductors, points, lines etc. Should be self explanatory
integer :: n_conductors_without_ground_plane
integer :: tot_n_conductors,conductor
integer :: tot_n_boundaries
integer :: n_points,point
integer :: n_lines,line
integer :: n_line_loops,line_loop
integer :: n_surfaces,surface
integer :: n_dielectric_regions,tot_n_dielectric_regions,dielectric_region
integer :: n_shapes,shape,conductor_shape
integer :: matrix_dimension
complex(dp),allocatable :: dielectric_region_epsr(:) ! list of the relative permittivity of each region
! temporary variables used in writing the geometry input file for gmsh
real(dp) :: rl,rt,rdl,rx1,rx2,ry1,ry2
real(dp) :: xp,yp,xt,yt
integer :: p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12
real(dp) :: edge_length
character(LEN=filename_length) :: command ! string used for the command which runs gmsh from the shell
integer :: gmsh_exit_stat ! exit status from the shell command which runs gmsh.
type(matrix) :: mat1 ! temporary matrix
type(matrix) :: mat2 ! temporary matrix
! frequency dependent dielectric variables
integer :: floop ! frequency loop variable
real(dp) :: f ! frequency
type(matrix),allocatable :: C_freq(:) ! frequency dependent Capacitance matrices for each frequency of evaluation
type(matrix),allocatable :: G_freq(:) ! frequency dependent Conductance matrices for each frequency of evaluation
complex(dp),allocatable :: function_to_fit(:) ! complex function to be fitted using Sfilter_fit
logical :: dielectric_defined ! flag to indicate whether there is a dielectric
logical :: frequency_dependent_dielectric ! flag to indicate whether there is a frequency dependent dielectric
integer :: gpline1,gpline2 ! line segments for infinite ground plane
integer :: row,col ! loop variables for matrix elements
integer :: i,ii ! temporary loop variables
! START
if (verbose) write(*,*)'CALLED: PUL_LC_Laplace'
! STAGE 1: work out the configuration for the calculation i.e. is there a ground plane, is the outer boundary free space or a conductor (overshield)
ground_plane=.FALSE.
infinite_ground_plane=.FALSE.
finite_ground_plane=.FALSE.
overshield=.FALSE.
open_boundary=.FALSE.
if ((.NOT.PUL%overshield_present).AND.(.NOT.PUL%ground_plane_present)) then
! SOLUTION TYPE 1: NO OVERSHIELD, NO GROUND PLANE
open_boundary=.TRUE.
first_surface_is_free_space_boundary=.TRUE.
end_conductor_loop=PUL%n_conductors
end_conductor_to_dielectric_loop=PUL%n_conductors
tot_n_boundaries_without_dielectric=PUL%n_conductors+1 ! add a free space boundary
first_conductor_shape=2 ! shape 1 is for the outer boundary
else if ((.NOT.PUL%overshield_present).AND.(PUL%ground_plane_present)) then
! SOLUTION TYPE 2: NO OVERSHIELD, WITH GROUND PLANE
ground_plane=.TRUE.
if (inf_gnd) then
infinite_ground_plane=.TRUE.
else
finite_ground_plane=.TRUE.
end if
tot_n_boundaries_without_dielectric=PUL%n_conductors+1 ! add a free space boundary
first_surface_is_free_space_boundary=.TRUE.
end_conductor_loop=PUL%n_conductors-1 ! the ground plane is not included here - added separately
end_conductor_to_dielectric_loop=PUL%n_conductors
first_conductor_shape=2 ! shape 1 is for the outer boundary
else
! SOLUTION TYPE 3: THERE IS AN OVERSHIELD WHICH FORMS THE OUTER BOUNDARY OF THE DOMAIN
overshield=.TRUE.
first_surface_is_free_space_boundary=.FALSE.
end_conductor_loop=PUL%n_conductors-1
end_conductor_to_dielectric_loop=PUL%n_conductors-1 ! overshield has no dielectric in the internal domain
tot_n_boundaries_without_dielectric=PUL%n_conductors
first_conductor_shape=1 ! shape 1 is a conductor
end if ! no overshield
if (verbose) then
write(*,*)'ground_plane:',ground_plane
write(*,*)'infinite_ground_plane:',infinite_ground_plane
write(*,*)'finite_ground_plane:',finite_ground_plane
write(*,*)'open_boundary:',open_boundary
write(*,*)'overshield:',overshield
write(*,*)'first_surface_is_free_space_boundary:',first_surface_is_free_space_boundary
write(*,*)'N_conductors:',PUL%n_conductors
write(*,*)'end_conductor_loop:',end_conductor_loop
write(*,*)'end_conductor_to_dielectric_loop:',end_conductor_to_dielectric_loop
write(*,*)'tot_n_boundaries_without_dielectric:',tot_n_boundaries_without_dielectric
write(*,*)'first_conductor_shape:',first_conductor_shape
end if ! verbose
! STAGE 2: Work out the numbers of conductors, points, lines, line loops and surfaces in the cross section geometry
tot_n_conductors = PUL%n_conductors ! the conductor count includes the ground plane if present
! Count the dielectric regions:
! Each dielectric region adds a boundary and another surface to mesh so work out whether a dielectric
! is present for each conductor.
! A dielectric region is assumed to exist if the dielectric radius is larger than the conductor radius and
! the relative permittivity at d.c. is not equal to 1 or the permittivity model order is not equal to zero
ALLOCATE ( conductor_has_dielectric(1:PUL%n_conductors) )
n_dielectric_regions=0
do conductor=1,end_conductor_loop
! assume no dielectric initially
conductor_has_dielectric(conductor)=.FALSE.
! If the low freq permittivity is not 1.0 or the order of the filter model is not 0 then assume we have a dielectric
dielectric_defined=.FALSE.
! check the low frequency value of epsr
epsr=PUL%epsr(conductor)%a%coeff(0)/PUL%epsr(conductor)%b%coeff(0)
if ( abs(epsr-1d0).GT.small ) dielectric_defined=.TRUE.
! check the model order
if ( (PUL%epsr(conductor)%a%order.GT.0).OR. (PUL%epsr(conductor)%b%order.GT.0) ) dielectric_defined=.TRUE.
! for now use the high frequency limit of epsr
epsr=evaluate_Sfilter_high_frequency_limit(PUL%epsr(conductor))
if (PUL%shape(conductor).EQ.circle) THEN
if ( ( (PUL%rd(conductor)-PUL%r(conductor)).GT.small ).AND.( dielectric_defined ) ) then
n_dielectric_regions= n_dielectric_regions+1
conductor_has_dielectric(conductor)=.TRUE.
end if
else if (PUL%shape(conductor).EQ.rectangle) THEN
! include dielectric region if the dielectric is not air and there is a dielectric region surrounding the conductor
if ( ( dielectric_defined ).AND. ( ( PUL%rdw(conductor).GT.small ).AND.( PUL%rdw(conductor).GT.small ) ) ) then
n_dielectric_regions= n_dielectric_regions+1
conductor_has_dielectric(conductor)=.TRUE.
end if
end if
end do ! next conductor
tot_n_boundaries = tot_n_boundaries_without_dielectric+n_dielectric_regions
tot_n_dielectric_regions = n_dielectric_regions+1 ! add a dielectric region for the 'free space' region
n_points = 5*tot_n_boundaries ! Each region is enclosed by a shape, defined by 5 points
n_lines = 4*tot_n_boundaries ! Each region is enclosed by a shape, defined by 4 lines
n_line_loops= tot_n_boundaries ! Each region is enclosed by a shape, defined by a single line loop
n_surfaces = n_dielectric_regions+1 ! Each dielectric region plus the free space region are a separate surface
if (verbose) then
write(*,*)'Number of dielectric regions =',n_dielectric_regions
write(*,*)'Total number of regions =',tot_n_dielectric_regions
write(*,*)'Total number of conductors =',tot_n_conductors
write(*,*)'Total number of boundaries =',tot_n_boundaries
write(*,*)'first_surface_is_free_space_boundary? ',first_surface_is_free_space_boundary
end if
! STAGE 3: Create the input file for the mesh generator, gmsh.
! Allocate the memory required for all the boundaries in the cross section geometry
n_shapes=tot_n_boundaries
ALLOCATE( shape_type(1:n_shapes) )
shape_type(1:n_shapes)=circle ! set all boudaries to type circle initially
ALLOCATE( loop_number(1:n_shapes) )
loop_number(1:n_shapes)=-1 ! set all boudaries to -1 i.e. not set
ALLOCATE( x(1:n_shapes) )
ALLOCATE( y(1:n_shapes) )
ALLOCATE( r(1:n_shapes) )
ALLOCATE( rw(1:n_shapes) )
ALLOCATE( rw2(1:n_shapes) )
ALLOCATE( rh(1:n_shapes) )
ALLOCATE( rox(1:n_shapes) )
ALLOCATE( roy(1:n_shapes) )
ALLOCATE( rtheta(1:n_shapes) )
ALLOCATE( dl(1:n_shapes) )
ALLOCATE( shape_to_region(1:n_shapes,1:2) )
shape_to_region(1:n_shapes,1:2)=-1 ! set all regions to undefined initially
ALLOCATE( dielectric_region_epsr(0:n_dielectric_regions) )
if (.NOT.overshield) then
! Set the position of the outer free space boundary. This is related to the size of the cable bundle enclosed
! by the Laplace_boundary_constant
! First get the extent of the bundle
xmin=large
xmax=-large
ymin=large
ymax=-large
do i=1,end_conductor_loop ! if the last conductor is the ground plane it is included in x/y,max/min calculation later
if (PUL%shape(i).EQ.circle) then
xmin=min(xmin,PUL%x(i)-PUL%r(i)+PUL%ox(i))
xmax=max(xmax,PUL%x(i)+PUL%r(i)+PUL%ox(i))
ymin=min(ymin,PUL%y(i)-PUL%r(i)+PUL%oy(i))
ymax=max(ymax,PUL%y(i)+PUL%r(i)+PUL%oy(i))
else if (PUL%shape(i).EQ.rectangle) then
xmin=min(xmin,PUL%x(i)-PUL%rw(i)+PUL%ox(i))
xmax=max(xmax,PUL%x(i)+PUL%rw(i)+PUL%ox(i))
ymin=min(ymin,PUL%y(i)-PUL%rh(i)+PUL%oy(i))
ymax=max(ymax,PUL%y(i)+PUL%rh(i)+PUL%oy(i))
else if (PUL%shape(i).EQ.Dshape) then
!*** Does this have to take rotation into account ? *****
xmin=min(xmin,PUL%x(i)-PUL%rw(i))
xmax=max(xmax,PUL%x(i)+PUL%rw(i))
ymin=min(ymin,PUL%y(i)-PUL%rh(i))
ymax=max(ymax,PUL%y(i)+PUL%rh(i))
! note don't check semicircle as this is only for infinite ground only
else if (PUL%shape(i).NE.semicircle) then
write(*,*)'Unknown shape type',PUL%shape(i)
end if
end do ! next conductor
if (ground_plane) then
! include the ground plane in the bundle sizing by adding the origin point
! here we assume that the ground plane is along the x axis.
xmin=min(xmin,0d0)
xmax=max(xmax,0d0)
ymin=min(ymin,0d0)
ymax=max(ymax,0d0)
end if ! ground_plane
! Centre of conductor bundle
xc=(xmax+xmin)/2d0
yc=(ymax+ymin)/2d0
rboundary=max((xmax-xmin),(ymax-ymin))*Laplace_boundary_constant
if (verbose) then
write(*,*)'bundle xmin=',xmin,' bundle xmax=',xmax
write(*,*)'bundle ymin=',ymin,' bundle ymax=',ymax
write(*,*)'bundle centre=',xc,yc
write(*,*)'boundary radius=',rboundary
end if
! set the first shape information to relate to the outer boundary
!
! If we have a finite gound plane of free space boundary then this is a circle, centred on the bundle centre
! note that for the Laplace calculation the geometry will be shifted so that the origin is at xc,yc so the
! free space outer boundary is centred on the origin. This is required for the free space boundary condition in Laplace to work correctly.
if (.NOT.infinite_ground_plane) then
x(1)=xc
y(1)=yc
r(1)=rboundary
rw(1)=0d0
rw2(1)=0d0
rh(1)=0d0
rox(1)=0d0
roy(1)=0d0
rtheta(1)=0d0
dl(1)=r(1)/(2*Laplace_surface_mesh_constant)
shape_to_region(1,inside) =0 ! inside is the dielectric region
shape_to_region(1,outside)=-1 ! no outside region
else
!
! If we have an infinite ground plane then we have a semicircle centred on the origin
shape_type(1)=semicircle
xc=0d0
yc=0d0
x(1)=xc
y(1)=yc
r(1)=rboundary
rw(1)=0d0
rw2(1)=0d0
rh(1)=0d0
rox(1)=0d0
roy(1)=0d0
rtheta(1)=0d0
dl(1)=r(1)/(2*Laplace_surface_mesh_constant)
shape_to_region(1,inside) =0 ! inside is the dielectric region
shape_to_region(1,outside)=-1 ! no outside region
dl_gp=min(gp_edge_length,dl(1))
end if ! infinite ground plane
else ! there is an overshield specified so we do not need to offset the bundle to define a free space outer boundary
xc=0d0
yc=0d0
end if ! overshield
dielectric_region_epsr(0)= evaluate_Sfilter_high_frequency_limit(PUL%epsr_background) ! set material properties in region 0, the background region
! copy the conductor information from the PUL structure to the shape based structure
do i=1,end_conductor_loop ! all conductors except the ground plane if it exists
shape=first_conductor_shape-1+i
shape_type(shape)=PUL%shape(i)
x(shape)=PUL%x(i)
y(shape)=PUL%y(i)
r(shape)=PUL%r(i)
rw(shape)=PUL%rw(i)
rw2(shape)=PUL%rw2(i)
rh(shape)=PUL%rh(i)
rox(shape)=PUL%ox(i)
roy(shape)=PUL%oy(i)
rtheta(shape)=PUL%rtheta(i)
if (shape_type(shape).EQ.circle) then
dl(shape)=r(shape)/Laplace_surface_mesh_constant
else if (shape_type(shape).EQ.rectangle) then
dl(shape)=min(rw(shape),rh(shape))/Laplace_surface_mesh_constant
else if (shape_type(shape).EQ.Dshape) then
dl(shape)=r(shape)/(2D0*Laplace_surface_mesh_constant)
else if (shape_type(shape).EQ.semicircle) then
dl(shape)=r(shape)/Laplace_surface_mesh_constant
else
write(*,*)'Unknown shape type',shape_type(shape),' i=',i,' shape=',shape
end if
end do
if (ground_plane) then ! Add the ground plane information
shape=PUL%n_conductors+1 ! ground plane
if (finite_ground_plane) then
shape_type(shape)=rectangle
rl=rboundary*Laplace_ground_plane_ratio ! width of the ground plane (x dimension) see constants.F90 for parameter
rt=rl*Laplace_ground_plane_thickness_ratio ! height of the ground plane (y dimension) see constants.F90 for parameter
rdl=rt/2d0
x(shape)=xc ! x centre of ground plane rectangle
y(shape)=-rt ! y centre of ground plane rectangle
r(shape)=0d0 ! not used
rw(shape)=rl*2d0 ! x extent of the ground plane
rw2(shape)=rw(shape) ! x extent of the ground plane
rh(shape)=rt*2d0 ! y extent of the ground plane
rox(shape)=0d0 ! no offset from centre
roy(shape)=0d0 ! no offset from centre
rtheta(shape)=0d0 ! always zero for ground plane
dl(shape)=rdl ! this is the thickness of the ground plane
shape_to_region(shape,inside) =-1 ! no internal region
shape_to_region(shape,outside) = 0 ! no dielectric so a free space region
else
shape_type(shape)=semicircle_diameter
rl=0d0 ! not used
rt=0d0 ! not used
rdl=0d0 ! not used
x(shape)=0d0 ! x centre of ground plane
y(shape)=0d0 ! y centre of ground plane
r(shape)=rboundary ! semicircle radius
rw(shape)=0d0 ! not used
rw2(shape)=0d0 ! not used
rh(shape)=0d0 ! not used
rox(shape)=0d0 ! no offset from centre
roy(shape)=0d0 ! no offset from centre
rtheta(shape)=0d0 ! always zero for infinite ground plane
dl(shape)=r(1)/(2*Laplace_surface_mesh_constant)
shape_to_region(shape,inside) =-1 ! no internal region
shape_to_region(shape,outside) = 0 ! no dielectric so a free space region
end if ! infinite ground plane
else if (overshield) then ! Add the overshield conductor information
shape=PUL%n_conductors
shape_type(shape)=PUL%overshield_shape
x(shape)=PUL%overshield_x
y(shape)=PUL%overshield_y
r(shape)=PUL%overshield_r
rw(shape)=PUL%overshield_w
rw2(shape)=PUL%overshield_w2
rh(shape)=PUL%overshield_h
rox(shape)=0d0
roy(shape)=0d0
rtheta(shape)=0d0
dl(shape)=r(shape)/(2d0*Laplace_surface_mesh_constant)
shape_to_region(shape,inside) = 0 ! inside the overshield is the free space region
end if ! overshield
! loop over conductors copying the associated dielectric information from the PUL structure,
! also associate shapes with regions
dielectric_region=0
do i=1,end_conductor_loop ! exclude the ground plane if it exists as it has no dielectric region
conductor_shape=first_conductor_shape-1+i ! shape number for the conductor
shape_to_region(conductor_shape,inside) =-1 ! conductors have no inner surface
if (conductor_has_dielectric(i)) then
dielectric_region=dielectric_region+1
shape=shape+1 ! create a new shape number for the dielectric
shape_type(shape)=PUL%shape(i) ! the dielectric is the same shape as the conductor
x(shape)=PUL%x(i)
y(shape)=PUL%y(i)
r(shape)=PUL%rd(i)
rw(shape)=PUL%rdw(i)
rw2(shape)=PUL%rw2(i)
rh(shape)=PUL%rdh(i)
rox(shape)=PUL%rdox(i)
roy(shape)=PUL%rdoy(i)
rtheta(shape)=PUL%rtheta(i)
if (shape_type(shape).EQ.circle) then
dl(shape)=r(shape)/Laplace_surface_mesh_constant
else if (shape_type(shape).EQ.rectangle) then
dl(shape)=min(rw(shape),rh(shape))/Laplace_surface_mesh_constant
else if (shape_type(shape).EQ.Dshape) then
dl(shape)=r(shape)/(2D0*Laplace_surface_mesh_constant)
else
write(*,*)'Unknown shape type',shape_type(shape)
end if
shape_to_region(shape,inside) =dielectric_region
shape_to_region(shape,outside) =0 ! free space region
dielectric_region_epsr(dielectric_region)=evaluate_Sfilter_high_frequency_limit(PUL%epsr(i))
else
! no dielectric
shape_to_region(conductor_shape,outside) =0 ! no dielectric so a free space region
end if ! this conductor has a dielectric or not
end do ! next conductor
! Work out which conductors are associated with which dielectrics
do i=1,end_conductor_loop
conductor_shape=first_conductor_shape-1+i ! shape number for the conductor
dielectric_region=0
! calculate the centre of the conductor (the test point)
! xp,yp is the coordinate of the centre of the conductor relative to the centre of the cable
xp=rox(conductor_shape)
yp=roy(conductor_shape)
! rotate the cable about it's origin then translate to the cable position to give the final conductor position
xt=x(conductor_shape)+xp*cos(rtheta(conductor_shape))-yp*sin(rtheta(conductor_shape))
yt=y(conductor_shape)+xp*sin(rtheta(conductor_shape))+yp*cos(rtheta(conductor_shape))
! loop over dielectrc shapes
do shape=tot_n_boundaries_without_dielectric+1,tot_n_boundaries
dielectric_region=dielectric_region+1
! check whether the conductor is inside the dielectric
if (point_is_inside(xt,yt,shape_type(shape),x(shape),y(shape),r(shape),rh(shape),rw(shape), &
rox(shape),roy(shape),rtheta(shape))) then
shape_to_region(conductor_shape,outside) =dielectric_region
write(*,*)'Conductor ',i,' is inside dielectric',dielectric_region,'(shape',shape,'):'
endif
end do ! next dielectric shape
end do ! next conductor
if (verbose) then
write(*,*)' shape shape_type x(shape) y(shape) r(shape) rh(shape) rw(shape) rtheta(shape) dl(shape)'
do shape=1,n_shapes
write(*,'(2I10,7F12.6)')shape,shape_type(shape),x(shape),y(shape),r(shape),rw(shape),rh(shape),rtheta(shape),dl(shape)
end do
end if
! Open a file for the gmsh geometry input data
temp_string=trim(name)//'_geometry_domain_'
CALL add_integer_to_string(temp_string,domain,geom_filename)
geom_filename=trim(geom_filename)//gmsh_geometry_file_extn
open (unit=gmsh_geometry_file_unit,file=trim(geom_filename))
if (verbose) write(*,*)'Opened file:',trim(geom_filename)
! open a file for the boundary information
open(unit=boundary_file_unit,file=boundary_file_name)
if (verbose) write(*,*)'Opened file:',boundary_file_name
! write the points required to define the each shape
! Translate all coordinates by (-xc,-yc) such that a free space outer boundary is centred at the origin
point=0
do i=1,n_shapes
if (shape_type(i).EQ.circle) then
write(gmsh_geometry_file_unit,*)' // circle ',i
point=point+1
write(gmsh_geometry_file_unit,*)'Point(',point,' ) = {',x(i)-xc+rox(i),',' &
,y(i)-yc+roy(i),',',0.0,',' ,dl(i),' };'
point=point+1
write(gmsh_geometry_file_unit,*)'Point(',point,' ) = {',x(i)-xc+rox(i),',' &
,y(i)-yc-r(i)+roy(i),',',0.0,',',dl(i),' };'
point=point+1
write(gmsh_geometry_file_unit,*)'Point(',point,' ) = {',x(i)-xc+rox(i)+r(i),',' &
,y(i)-yc+roy(i),',',0.0,',' ,dl(i),' };'
point=point+1
write(gmsh_geometry_file_unit,*)'Point(',point,' ) = {',x(i)-xc+rox(i),',' &
,y(i)-yc+r(i)+roy(i),',',0.0,',',dl(i),' };'
point=point+1
write(gmsh_geometry_file_unit,*)'Point(',point,' ) = {',x(i)-xc+rox(i)-r(i),',' &
,y(i)-yc+roy(i),',',0.0,',' ,dl(i),' };'
else if (shape_type(i).EQ.rectangle) then
write(gmsh_geometry_file_unit,*)' // rectangle ',i
! corner point
point=point+1
xt=rw(i)/2d0+rox(i)
yt=rh(i)/2d0+roy(i)
xp=x(i)-xc+xt*cos(rtheta(i))-yt*sin(rtheta(i))
yp=y(i)-yc+xt*sin(rtheta(i))+yt*cos(rtheta(i))
edge_length=min(max_mesh_edge_length,dl(i))
write(gmsh_geometry_file_unit,*)'Point(',point,' ) = {',xp,',',yp,',',0.0,',',edge_length,' };'
! edge centre point
point=point+1
xt=rox(i)
yt=rh(i)/2d0+roy(i)
xp=x(i)-xc+xt*cos(rtheta(i))-yt*sin(rtheta(i))
yp=y(i)-yc+xt*sin(rtheta(i))+yt*cos(rtheta(i))
dl_local=min(max_mesh_edge_length,rw(i)/Laplace_surface_mesh_constant)
write(gmsh_geometry_file_unit,*)'Point(',point,' ) = {',xp,',',yp,',',0.0,',',dl_local,' };'
! corner point
point=point+1
xt=-rw(i)/2d0+rox(i)
yt=rh(i)/2d0+roy(i)
xp=x(i)-xc+xt*cos(rtheta(i))-yt*sin(rtheta(i))
yp=y(i)-yc+xt*sin(rtheta(i))+yt*cos(rtheta(i))
edge_length=min(max_mesh_edge_length,dl(i))
write(gmsh_geometry_file_unit,*)'Point(',point,' ) = {',xp,',',yp,',',0.0,',',edge_length,' };'
! edge centre point
point=point+1
xt=-rw(i)/2d0+rox(i)
yt=roy(i)
xp=x(i)-xc+xt*cos(rtheta(i))-yt*sin(rtheta(i))
yp=y(i)-yc+xt*sin(rtheta(i))+yt*cos(rtheta(i))
dl_local=min(max_mesh_edge_length,rh(i)/Laplace_surface_mesh_constant)
write(gmsh_geometry_file_unit,*)'Point(',point,' ) = {',xp,',',yp,',',0.0,',',dl_local,' };'
! corner point
point=point+1
xt=-rw(i)/2d0+rox(i)
yt=-rh(i)/2d0+roy(i)
xp=x(i)-xc+xt*cos(rtheta(i))-yt*sin(rtheta(i))
yp=y(i)-yc+xt*sin(rtheta(i))+yt*cos(rtheta(i))
edge_length=min(max_mesh_edge_length,dl(i))
write(gmsh_geometry_file_unit,*)'Point(',point,' ) = {',xp,',',yp,',',0.0,',',edge_length,' };'
! edge centre point
point=point+1
xt=rox(i)
yt=-rh(i)/2d0+roy(i)
xp=x(i)-xc+xt*cos(rtheta(i))-yt*sin(rtheta(i))
yp=y(i)-yc+xt*sin(rtheta(i))+yt*cos(rtheta(i))
dl_local=min(max_mesh_edge_length,rw(i)/Laplace_surface_mesh_constant)
write(gmsh_geometry_file_unit,*)'Point(',point,' ) = {',xp,',',yp,',',0.0,',',dl_local,' };'
! corner point
point=point+1
xt=rw(i)/2d0+rox(i)
yt=-rh(i)/2d0+roy(i)
xp=x(i)-xc+xt*cos(rtheta(i))-yt*sin(rtheta(i))
yp=y(i)-yc+xt*sin(rtheta(i))+yt*cos(rtheta(i))
edge_length=min(max_mesh_edge_length,dl(i))
write(gmsh_geometry_file_unit,*)'Point(',point,' ) = {',xp,',',yp,',',0.0,',',edge_length,' };'
! edge centre point
point=point+1
xt=rw(i)/2d0+rox(i)
yt=roy(i)
xp=x(i)-xc+xt*cos(rtheta(i))-yt*sin(rtheta(i))
yp=y(i)-yc+xt*sin(rtheta(i))+yt*cos(rtheta(i))
dl_local=min(max_mesh_edge_length,rh(i)/Laplace_surface_mesh_constant)
write(gmsh_geometry_file_unit,*)'Point(',point,' ) = {',xp,',',yp,',',0.0,',',dl_local,' };'
else if (shape_type(i).EQ.Dshape) then
CALL write_Dshape_gmsh(x(i),y(i),rw(i),rw2(i),rh(i),r(i),rox(i),roy(i),rtheta(i),dl(i),point,i,gmsh_geometry_file_unit)
else if (shape_type(i).EQ.semicircle) then
write(gmsh_geometry_file_unit,*)' // semicircle ',i
point=point+1
write(gmsh_geometry_file_unit,*)'Point(',point,' ) = {',x(i)-xc+rox(i),',' &
,y(i)-yc+roy(i),',',0.0,',' ,dl_gp,' };' ! middle of ground plane point
point=point+1
write(gmsh_geometry_file_unit,*)'Point(',point,' ) = {',x(i)-xc+rox(i)+r(i),',' &
,y(i)-yc+roy(i),',',0.0,',',dl(i),' };'
point=point+1
write(gmsh_geometry_file_unit,*)'Point(',point,' ) = {',x(i)-xc+rox(i) ,',' &
,y(i)-yc+r(i)+roy(i),',',0.0,',' ,dl(i),' };'
point=point+1
write(gmsh_geometry_file_unit,*)'Point(',point,' ) = {',x(i)-xc+rox(i)-r(i),',' &
,y(i)-yc+roy(i),',',0.0,',',dl(i),' };'
end if
end do ! next shape
! write the boudary line segment definitions for each shape, these are constructed from the previously defined points
write(gmsh_geometry_file_unit,*)' '
point=0 ! initialise point counter
line=0 ! initialise line counter
do i=1,n_shapes
if (shape_type(i).EQ.circle) then
p1=point+1
p2=point+2
p3=point+3
p4=point+4
p5=point+5
write(gmsh_geometry_file_unit,*)' // circle ',i
line=line+1
write(gmsh_geometry_file_unit,*)'Circle(',line,' ) = {',p2,',',p1,',',p3,' };'
line=line+1
write(gmsh_geometry_file_unit,*)'Circle(',line,' ) = {',p3,',',p1,',',p4,' };'
line=line+1
write(gmsh_geometry_file_unit,*)'Circle(',line,' ) = {',p4,',',p1,',',p5,' };'
line=line+1
write(gmsh_geometry_file_unit,*)'Circle(',line,' ) = {',p5,',',p1,',',p2,' };'
point=point+5
else if (shape_type(i).EQ.rectangle) then
p1=point+1
p2=point+2
p3=point+3
p4=point+4
p5=point+5
p6=point+6
p7=point+7
p8=point+8
write(gmsh_geometry_file_unit,*)' // rectangle line segments ',i
line=line+1
write(gmsh_geometry_file_unit,*)'Line(',line,' ) = {',p1,',',p2,' };'
line=line+1
write(gmsh_geometry_file_unit,*)'Line(',line,' ) = {',p2,',',p3,' };'
line=line+1
write(gmsh_geometry_file_unit,*)'Line(',line,' ) = {',p3,',',p4,' };'
line=line+1
write(gmsh_geometry_file_unit,*)'Line(',line,' ) = {',p4,',',p5,' };'
line=line+1
write(gmsh_geometry_file_unit,*)'Line(',line,' ) = {',p5,',',p6,' };'
line=line+1
write(gmsh_geometry_file_unit,*)'Line(',line,' ) = {',p6,',',p7,' };'
line=line+1
write(gmsh_geometry_file_unit,*)'Line(',line,' ) = {',p7,',',p8,' };'
line=line+1
write(gmsh_geometry_file_unit,*)'Line(',line,' ) = {',p8,',',p1,' };'
point=point+8
else if (shape_type(i).EQ.Dshape) then
p1=point+1
p2=point+2
p3=point+3
p4=point+4
p5=point+5
p6=point+6
p7=point+7
p8=point+8
p9=point+9
p10=point+10
p11=point+11
p12=point+12
write(gmsh_geometry_file_unit,*)' // Dshape line segments',i
line=line+1
write(gmsh_geometry_file_unit,*)'Line(',line,' ) = {',p1,',',p2,' };'
line=line+1
write(gmsh_geometry_file_unit,*)'Circle(',line,' ) = {',p2,',',p3,',',p4,' };'
line=line+1
write(gmsh_geometry_file_unit,*)'Line(',line,' ) = {',p4,',',p5,' };'
line=line+1
write(gmsh_geometry_file_unit,*)'Circle(',line,' ) = {',p5,',',p6,',',p7,' };'
line=line+1
write(gmsh_geometry_file_unit,*)'Line(',line,' ) = {',p7,',',p8,' };'
line=line+1
write(gmsh_geometry_file_unit,*)'Circle(',line,' ) = {',p8,',',p9,',',p10,' };'
line=line+1
write(gmsh_geometry_file_unit,*)'Line(',line,' ) = {',p10,',',p11,' };'
line=line+1
write(gmsh_geometry_file_unit,*)'Circle(',line,' ) = {',p11,',',p12,',',p1,' };'
point=point+12
else if (shape_type(i).EQ.semicircle) then
p1=point+1
p2=point+2
p3=point+3
p4=point+4
write(gmsh_geometry_file_unit,*)' // semicircle ',i
line=line+1
write(gmsh_geometry_file_unit,*)'Circle(',line,' ) = {',p2,',',p1,',',p3,' };'
line=line+1
write(gmsh_geometry_file_unit,*)'Circle(',line,' ) = {',p3,',',p1,',',p4,' };'
point=point+4
else if (shape_type(i).EQ.semicircle_diameter) then
! this is the infinite ground plane line so pick points from the outer boundary circle
p1=4 ! semicircle end point 2
p2=1 ! centre
p3=2 ! semicircle end point 1
write(gmsh_geometry_file_unit,*)' // semicircle_diameter ',i
line=line+1
write(gmsh_geometry_file_unit,*)'Line(',line,' ) = {',p1,',',p2,' };'
gpline1=line
line=line+1
write(gmsh_geometry_file_unit,*)'Line(',line,' ) = {',p2,',',p3,' };'
gpline2=line
end if
end do ! next shape
! write the number of boundary segments to the boundary file
write(boundary_file_unit,*)line,' # number of boundary segments'
! Create the closed boundary line loops for each shape, these are constructed from the preciously defined line segments.
write(gmsh_geometry_file_unit,*)' '
line_loop=0 ! initialise line_loop counter
line=0 ! reset the line counter to the first line
do i=1,n_shapes
if ( (shape_type(i).EQ.circle) ) then
line_loop=line_loop+1
loop_number(i)=line_loop
write(gmsh_geometry_file_unit,*)'Line Loop(',line_loop,' ) = {',line+1,',',line+2,',',line+3,',',line+4,' };'
! write the boundary segment and the boundary number to the boundary file
do ii=1,4
write(boundary_file_unit,*)line+ii,i,' # boundary segment number and boundary number'
end do
line=line+4
else if ( shape_type(i).EQ.rectangle ) then
line_loop=line_loop+1
loop_number(i)=line_loop
write(gmsh_geometry_file_unit,*)'Line Loop(',line_loop,' ) = {',line+1,',',line+2,',',line+3,',',line+4,',', &
line+5,',',line+6,',',line+7,',',line+8,' };'
! write the boundary segment and the boundary number to the boundary file
do ii=1,8
write(boundary_file_unit,*)line+ii,i,' # boundary segment number and boundary number'
end do
line=line+8
else if (shape_type(i).EQ.semicircle) then
! this is the outer boundary with infinite ground plane.
! the line loop consists of 4 lines, 2 for the semicircle and 2 for the ground plane
line_loop=line_loop+1
loop_number(i)=line_loop
write(gmsh_geometry_file_unit,*)'Line Loop(',line_loop,' ) = {',line+1,',',line+2,',',gpline1,',',gpline2,' };'
! write the boundary segment and the boundary number to the boundary file
do ii=1,2
write(boundary_file_unit,*)line+ii,i,' # boundary segment number and boundary number'
end do
line=line+2
else if (shape_type(i).EQ.Dshape) then
line_loop=line_loop+1
loop_number(i)=line_loop
write(gmsh_geometry_file_unit,*)'Line Loop(',line_loop,' ) = {',line+1,',',line+2,',',line+3,',',line+4,',', &
line+5,',',line+6,',',line+7,',',line+8,' };'
! write the boundary segment and the boundary number to the boundary file
do ii=1,8
write(boundary_file_unit,*)line+ii,i,' # boundary segment number and boundary number'
end do
line=line+8
else if (shape_type(i).EQ.semicircle_diameter) then
! note there is no line loop for the semicircle_diameter shape as this is dealt with along with the semicircle
! just skip these lines
! write the boundary segment and the boundary number to the boundary file
do ii=1,2
write(boundary_file_unit,*)line+ii,i,' # boundary segment number and boundary number'
end do
line=line+2
end if
end do ! next shape
! Create the plane Surfaces, one for each dielectric region. Surfaces are defined by their boundling line loops
! Note that the order of the line loop specification is important for the orientation of the elements in the mesh
write(gmsh_geometry_file_unit,*)' '
surface=0 ! initialise surface counter
! loop over the dielectric regions (including free space) as each one contributes a surface to be meshed.
do dielectric_region=0,n_dielectric_regions ! 0 is the free space region which always exists
surface=surface+1
string1=' Plane Surface('
CALL add_integer_to_string(string1,surface,string2)
string1=trim(string2)//') = {'
! for this surface, write a list of all the line loops bounding the surface
! in order to construct this list we loop over all shapes (line loops)
! and check whether the inside or outside region of the line loop is associated with the current region
if (surface.eq.1) then
! order the line loops for the free space region outer boundary first
if (overshield) then
! The overshield reference conductor should be the last conductor
i=PUL%n_conductors
line_loop=loop_number(i)
if( (shape_to_region(i,inside).EQ.dielectric_region).OR. &
(shape_to_region(i,outside).EQ.dielectric_region) ) then
CALL add_integer_to_string(string1,line_loop,string2)
string1=trim(string2)//', '
end if
end if ! overshield
do i=1,n_shapes ! don't include ground plane in check
line_loop=loop_number(i)
if ((.NOT.overshield).OR.(i.NE.PUL%n_conductors)) then
if( (shape_to_region(i,inside).EQ.dielectric_region).OR. &
(shape_to_region(i,outside).EQ.dielectric_region) ) then
if (line_loop.GT.0) then
CALL add_integer_to_string(string1,line_loop,string2)
string1=trim(string2)//', '
end if
end if
end if
end do ! next shape
else ! not the fisrt surface
! dielectric boundaries are defined second so reverse the order of the loop for dielectric regions
do i=n_shapes,1,-1
line_loop=loop_number(i)
if( (shape_to_region(i,inside).EQ.dielectric_region).OR. &
(shape_to_region(i,outside).EQ.dielectric_region) ) then
if (line_loop.GT.0) then
CALL add_integer_to_string(string1,line_loop,string2)
string1=trim(string2)//', '
end if
end if
end do
end if
string1=trim(string2)//'} '
write(gmsh_geometry_file_unit,'(A,A)')trim(string1),' ;'
end do ! next region (surface) to mesh
write(gmsh_geometry_file_unit,*)' '
! Close the file for the gmsh input data
close(gmsh_geometry_file_unit)
if (verbose) write(*,*)'Closed file:',trim(geom_filename)
! close the boundary information file
close(unit=boundary_file_unit)
if (verbose) write(*,*)'Closed file:',boundary_file_name
! Dealllocate the local shape geometry data.
if (allocated( shape_type )) DEALLOCATE( shape_type )
if (allocated( loop_number )) DEALLOCATE( loop_number )
if (allocated( x )) DEALLOCATE( x )
if (allocated( y )) DEALLOCATE( y )
if (allocated( r )) DEALLOCATE( r )
if (allocated( rw )) DEALLOCATE( rw )
if (allocated( rw2 )) DEALLOCATE( rw2 )
if (allocated( rh )) DEALLOCATE( rh )
if (allocated( rox )) DEALLOCATE( rox )
if (allocated( roy )) DEALLOCATE( roy )
if (allocated( rtheta )) DEALLOCATE( rtheta )
if (allocated( dl )) DEALLOCATE( dl )
if (allocated( shape_to_region )) DEALLOCATE( shape_to_region )
! STAGE 4: Call the mesh generator, gmsh
temp_string=trim(name)//'_mesh_domain_'
CALL add_integer_to_string(temp_string,domain,mesh_filename)
mesh_filename=trim(mesh_filename)//mesh_file_extn
write(*,*)'CALLING gmsh for domain',domain
command='gmsh -2 -o '//trim(mesh_filename)//' '//trim(geom_filename)
CALL execute_command_line(command,EXITSTAT=gmsh_exit_stat)
! Check that the mesh generator finished correctly, if not exit and write the gmsh error code to the screen
if (gmsh_exit_stat.NE.0) then
! gmsh returned with a non zdero exit code indicating an error
write(run_status,*)'ERROR in PUL_LC_Laplace. gmsh exit status=',gmsh_exit_stat
CALL write_program_status()
STOP 1
end if
! STAGE 5: Call Laplace to calculate the L,C and G matrices, using the mesh that we have just generated
! Work out the dimension of the per-unit-length matrices
matrix_dimension=PUL%n_conductors-1
if (verbose) then
write(*,*)'Number of conductors=',PUL%n_conductors
write(*,*)'matrix dimension=',matrix_dimension
end if
! check we have a valid system
if (matrix_dimension.LT.1) then
run_status='ERROR in PUL_LC_Laplace, Matrix dimension is less than 1 '
CALL write_program_status()
STOP 1
end if
! work out if we have any frequency dependent dielectric
frequency_dependent_dielectric=.FALSE.
dielectric_region=0
if ( (PUL%epsr_background%a%order.GT.0).OR. (PUL%epsr_background%b%order.GT.0) ) then
frequency_dependent_dielectric=.TRUE.
end if
! check the external dielectric first
do i=1,end_conductor_loop ! don't include the ground plane in the conductor loop for the dielectric region count
write(*,*)'Check dielectric loop',i,' conductor has dielectric:',conductor_has_dielectric(i)
if (conductor_has_dielectric(i)) then
write(*,*)'aorder,border',PUL%epsr(i)%a%order,PUL%epsr(i)%b%order
if ( (PUL%epsr(i)%a%order.GT.0).OR. (PUL%epsr(i)%b%order.GT.0) ) then
frequency_dependent_dielectric=.TRUE.
end if
end if
end do ! next conductor
if (verbose) write(*,*)'Frequency dependent dielectric:',frequency_dependent_dielectric
! Check for special case which is not currently used but could happen in future developments maybe...
if ( (frequency_dependent_dielectric).AND.(n_dielectric_regions.EQ.0) ) then
run_status='Error in PUL_LC_Laplace. Conductors are situated in a homogeneous frequency dependent dielectric'
CALL write_program_status()
STOP 1
end if
! Allocate the per-unit-length matrices
PUL%L%dim=matrix_dimension
ALLOCATE( PUL%L%mat(1:PUL%L%dim,1:PUL%L%dim) )
PUL%C%dim=matrix_dimension
ALLOCATE( PUL%C%mat(1:PUL%C%dim,1:PUL%C%dim) )
PUL%G%dim=matrix_dimension
ALLOCATE( PUL%G%mat(1:PUL%G%dim,1:PUL%G%dim) )
! Allocate the impedance and admittance filter matrices
PUL%Zfilter%dim=matrix_dimension
ALLOCATE(PUL%Zfilter%sfilter_mat(1:PUL%Zfilter%dim,1:PUL%Zfilter%dim))
PUL%Yfilter%dim=matrix_dimension
ALLOCATE(PUL%Yfilter%sfilter_mat(1:PUL%Yfilter%dim,1:PUL%Yfilter%dim))
if (verbose) write(*,*)'CALLING: Laplace'
if (n_dielectric_regions.EQ.0) then
! no dielectrics or frequency dependent proximity effects so we can calculate L, C and G in the same solution
f=1D0 ! arbitrary frequency here for lossless dielectric but a frequency must be set. The value will have no effect in this case.
CALL Laplace(mesh_filename,matrix_dimension,first_surface_is_free_space_boundary, &
n_dielectric_regions,dielectric_region_epsr,f,PUL%L%mat,PUL%C%mat,PUL%G%mat,xc,yc)
! calculate the impedance and admittance filters for a frequency independent model
CALL Z_Y_from_L_C(PUL%L,PUL%C,PUL%Zfilter,PUL%Yfilter)
else
! there are dielectrics present or proximity effects
ALLOCATE( mat1%mat(1:matrix_dimension,1:matrix_dimension) )
mat1%dim=matrix_dimension
ALLOCATE( mat2%mat(1:matrix_dimension,1:matrix_dimension) )
mat2%dim=matrix_dimension
! Firstly solve using the 'high frequency' dielectric material properties
dielectric_region=0
do i=1,end_conductor_loop ! don't include the ground plane in the dielectric region count
if (conductor_has_dielectric(i)) then
dielectric_region=dielectric_region+1
dielectric_region_epsr(dielectric_region)=evaluate_Sfilter_high_frequency_limit(PUL%epsr(i))
end if
end do ! next conductor
! call first with the dielectric materials and solve for C and G first
f=1D0 ! arbitrary frequency here for lossless dielectric
CALL Laplace(mesh_filename,matrix_dimension,first_surface_is_free_space_boundary, &
n_dielectric_regions,dielectric_region_epsr,f,mat1%mat,PUL%C%mat,PUL%G%mat,xc,yc)
! call second with all materials set to free space properties and solve for L
dielectric_region_epsr(:)=(1d0,0d0)
f=1D0 ! arbitrary frequency here for lossless dielectric
CALL Laplace(mesh_filename,matrix_dimension,first_surface_is_free_space_boundary, &
n_dielectric_regions,dielectric_region_epsr,f,PUL%L%mat,mat1%mat,mat2%mat,xc,yc)
if ((.NOT.frequency_dependent_dielectric).OR.(freq_spec%n_frequencies.EQ.1)) then
! calculate the impedance and admittance filters
CALL Z_Y_from_L_C(PUL%L,PUL%C,PUL%Zfilter,PUL%Yfilter)
else
! There are frequency dependent dielectrics present
! We calculate the capacitance matrix at a number of frequencies before fitting filter functions
! to each of the frequency dependent admittance matrix entries.
! allocate the frequency dependent matrices
ALLOCATE( C_freq(1:freq_spec%n_frequencies) )
ALLOCATE( G_freq(1:freq_spec%n_frequencies) )
! loop over frequency
do floop=1,freq_spec%n_frequencies
! allocate the matrices for this frequency
ALLOCATE( C_freq(floop)%mat(1:matrix_dimension,1:matrix_dimension) )
C_freq(floop)%dim=matrix_dimension
ALLOCATE( G_freq(floop)%mat(1:matrix_dimension,1:matrix_dimension) )
G_freq(floop)%dim=matrix_dimension
! evaluate the complex dielectric constant at the current frequency
f=freq_spec%freq_list(floop)
! set material properties in region 0, the background region
dielectric_region=0
dielectric_region_epsr(dielectric_region)=evaluate_Sfilter_frequency_response(PUL%epsr_background,f)
do i=1,end_conductor_loop ! don't include the ground plane in the dielectric region count
if (conductor_has_dielectric(i)) then
dielectric_region=dielectric_region+1
dielectric_region_epsr(dielectric_region)=evaluate_Sfilter_frequency_response(PUL%epsr(i),f)
end if
end do ! next conductor
! solve for C and G matrices at this frequency
! call Laplace to solve for C and G at this frequency
CALL Laplace(mesh_filename,matrix_dimension,first_surface_is_free_space_boundary, &
n_dielectric_regions,dielectric_region_epsr,f,mat1%mat,C_freq(floop)%mat,G_freq(floop)%mat,xc,yc)
end do ! next frequency
! we now have frequency domain C and G matrices.
! loop over the elements of C and G and calculate the Y matrix by filter fitting to Y=G(f)+jwC(f), and the Z matrix as jwL
ALLOCATE( function_to_fit(1:freq_spec%n_frequencies) )
do row=1,matrix_dimension
do col=row,matrix_dimension
! get the function values for this matrix element function_to_fit=G+jwC
do floop=1,freq_spec%n_frequencies
function_to_fit(floop)=G_freq(floop)%mat(row,col)+ &
j*2d0*pi*freq_spec%freq_list(floop)*C_freq(floop)%mat(row,col)
end do
! calculate the Yfilter matrix using the filter fitting process
! note aorder=border and no restrictions are put on the function
CALL Calculate_Sfilter(function_to_fit,freq_spec%freq_list,freq_spec%n_frequencies, &
PUL%Yfilter%sfilter_mat(row,col),fit_order+1,-1,0)
PUL%Zfilter%sfilter_mat(row,col)=jwA_filter( PUL%L%mat(row,col) )
if (col.NE.row) then
! make the matrix symmetrical
PUL%Yfilter%sfilter_mat(col,row)=PUL%Yfilter%sfilter_mat(row,col)
PUL%Zfilter%sfilter_mat(col,row)=PUL%Zfilter%sfilter_mat(row,col)
end if
end do ! next col
end do ! next row
DEALLOCATE( function_to_fit )
! deallocate the temporary matrices associated with the frequency dependent C and G matrices
do floop=1,freq_spec%n_frequencies
! allocate the matrices for this frequency
DEALLOCATE( C_freq(floop)%mat )
DEALLOCATE( G_freq(floop)%mat )
end do ! next frequency
DEALLOCATE( C_freq )
DEALLOCATE( G_freq )
end if ! frequency dependent dielectric
DEALLOCATE( mat1%mat )
DEALLOCATE( mat2%mat )
end if ! any dielectrics
if (verbose) then
write(*,*)'FINISHED: Laplace'
write(*,*)'Inductance matrix, L'
CALL dwrite_matrix(PUL%L%mat,matrix_dimension,matrix_dimension,matrix_dimension,0)
write(*,*)'Capacitance matrix, C'
CALL dwrite_matrix(PUL%C%mat,matrix_dimension,matrix_dimension,matrix_dimension,0)
write(*,*)'Conductance matrix, G'
CALL dwrite_matrix(PUL%G%mat,matrix_dimension,matrix_dimension,matrix_dimension,0)
end if
if (allocated( dielectric_region_epsr )) DEALLOCATE( dielectric_region_epsr )
if (verbose) write(*,*)'FINISHED PUL_LC_Laplace'
END SUBROUTINE PUL_LC_Laplace