!
! 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 <http://www.gnu.org/licenses/>.
! 
! 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 <http://www.gnu.org/licenses/>.
! 
! The University of Nottingham can be contacted at: ggiemr@nottingham.ac.uk
!
!
! File Contents:
! 
!     SUBROUTINE Laplace
!     FUNCTION ismember
!
! NAME
!     SUBROUTINE Laplace
!
! DESCRIPTION
!
!     Calculation of Per-Unit-Length Capacitance, Conductance and Inductance matrices 
!     for multi-conductor configutations including lossy dielectric regions.
!     The solution is based on the Finite Element method. 
!
!     The outer boundary of the problem space may be a conducting region or
!     a free space boundary. If the outer boundary is free space then it should
!     be circular and centred on the origin for correct application of the
!     free space boundary condition.
!
!     If a ground plane is present then it must be included in the input cross section
!     geometry as a finite conductor within the free space outer boundary.
!
!     The last conductor specified is always the reference conductor in the per-unit-length 
!     parameter matrices returned
!
!     Lossy dielectrics are taken into account by specifying a frequency and 
!     complex relative permittivity values at that frequency.
!
!     The solution calculates the C and G matrices, The inductance matrix, L is calculated
!     as [L]=[C^-1]*mu0*eps0*epsr where epsr is the background relative permittivity
!     so the inductance value is only correct for cross sections with homogeneous dielectric
!
!     The subroutine requires an existing mesh file created by the open source software gmsh: see http://gmsh.info/
!     This mesh format must be converted into the format required by the NLR Laplace software.
!
!     The underlying theory of the Finite Element formulation may be found in Theory_Manual_Section 4
!
!     The process implemented is as follows:
!  STAGE 1: Read the mesh information and the associated boundary information
!  STAGE 2: Convert gmsh format data to laplace format
!  STAGE 3: Create the boundary element list for PEC or outer boundaries
!  STAGE 4: Create the boundary node list
!  STAGE 5: Set the material information
!  STAGE 6: Filter out the unused nodes from gmsh and renumber the new node list
!  STAGE 7: Determine which of the nodes are unknown 
!  STAGE 8: work out the mapping of node numbers to knowns and unknowns
!  STAGE 9: Create the known voltage vectors
!  STAGE 10: Derive the necessary element properties
!      STAGE 10a: Determine K matrix elements for the unknowns
!      STAGE 10b: Determine K_rhs matrix that will be used to compute the rhs vector from the knowns.
!      STAGE 10c: Determine contribution to the K matrix do the unknowns from the asymptotic boundary condition on the open boundary
!  STAGE 11: Matrix solution of the finite element equations
!      STAGE 11a: fill the K matrix from the array of K matrix elements calculated in STAGE 10a, 10c
!      STAGE 11b: fill the K_rhs matrix from the array of K_rhs matrix elements calculated in STAGE 10b
!      STAGE 11c: Invert the K matrix
!      STAGE 11d loop over all the RHS vectors solving the matrix equation 
!  STAGE 12 Determine the voltage phi in each node of the mesh
!  STAGE 13: Capacitance and Conductance matrix computation from electric energy
!  STAGE 14: Inductance matrix computation from inverse of capacitance matrix
!  STAGE 15: Copy the inductance, capacitance and conductance matrices to the output matrices making them explicitly symmetrical
!  STAGE 16: plot potentials to vtk file for visualisation if required
!  STAGE 17: plot mesh to vtk file for visualisation if required
!  STAGE 18: deallocate memory 
!
!  References in the comments are to the Theory Manual and also the following book:
!      Jian-Ming Jin, "The Finite Element Method in Electromagnetics" 3rd Edition, John Wiley & sons 2014.
!      (Chapter 4.)
!     
! COMMENTS
!     This version uses complex arithmetic to include 
!     lossy dielectrics and hence a conductance matrix may be calculated.
!
!     The finite element equations are soolved using a (slow) direct matrix inverse.
!     Although we need to solve the equations for multiple right hand sides, a sparse matrix method may be better...
!
!     Do we need a contribution to the energy calculation from the field outside the free space boundary condition? See comment in STAGE 13
!
! HISTORY
!    started 5/7/2016 CJS. This subroutineis based on software from NLR and has been 
!                          translated from Matlab to Fortran.
!     8/5/2017         CJS: Include references to Theory_Manual
!     19/6/2018         CJS: Include iterative solver based on the biconjugate gradient method
!
SUBROUTINE Laplace(mesh_filename,dim,first_surface_is_free_space_boundary,   &
                   n_dielectric_regions,dielectric_region_epsr,frequency,Lmat,Cmat,Gmat,ox,oy)
!
! Look for and remove any nodes which do not form part of the mesh. This is required when
! running with gmsh.
!

USE type_specifications
USE constants
USE general_module
USE maths

IMPLICIT NONE

! variables passed to subroutine

character(LEN=filename_length),intent(IN) :: mesh_filename        ! name of the gmsh file for the computational mesh

integer,intent(IN)       :: dim                                              ! dimension of matrix system
logical,intent(IN)       :: first_surface_is_free_space_boundary             ! flag to indicate properties on the first surface defined
integer,intent(IN)       :: n_dielectric_regions                             ! number of dielectric regions
complex(dp),intent(IN)   :: dielectric_region_epsr(0:n_dielectric_regions)   ! complex relative permittivity for each mesh region
                                                                             ! note, include a property for region 0, the 'background' region

real(dp),intent(IN)      :: frequency        ! frequency for L,C,G matrix calculation 

real(dp),intent(OUT)     :: Lmat(dim,dim)    ! Inductance matrix to be calculated
real(dp),intent(OUT)     :: Cmat(dim,dim)    ! Capacitance matrix to be calculated
real(dp),intent(OUT)     :: Gmat(dim,dim)    ! Conductance matrix to be calculated

real(dp),intent(IN)      :: ox    ! mesh centre offset in x used for plotting meshes if required
real(dp),intent(IN)      :: oy    ! mesh centre offset in y used for plotting meshes if required

! local variables

! User defined type for boundary information
type :: boundary_data

  integer :: N_elements_boundary
  integer,allocatable :: boundary_elements(:,:)     ! list of boundary elements, boundary_elements(be,1)= element number, boundary_elements(be,2)= boundary condition number
  
  integer :: N_nodes_boundary
  integer,allocatable :: boundary_nodes(:,:)      ! list of boundary nodes, boundary_nodes(bnode,1)= node number, boundary_nodes(bnode,2)= boundary condition number
  
end type boundary_data

! variables for reading the gmsh mesh file before conversion to the Laplace format
integer :: n_conductors

integer,allocatable  :: point_to_boundary_list(:)                   ! if a point is on an external boundary then put that boundary number in this list
integer,allocatable  :: boundary_segment_to_boundary_number_list(:) ! this list relates the individual boundary segments to the boundary numbers (i.e. conductor numbers)

integer :: bs                                   ! boundary segment number

integer :: first_surface                        ! first_surface=0 if we have a free space outer boundary, 1 otherwise

! Laplace variables

integer :: N_nodes_in                                     ! number of nodes in the gmsh file (this is not necessarily the number of nodes in the FE mesh)
integer :: N_elements_in                                  ! number of elements in the gmsh file (this is not necessarily the number of triangular elements in the FE mesh)

integer :: n_boundaries                                   ! number of boundaries, not including dielectric (internal) boundaries
integer :: N_boundaries_max                               ! maximum boundary number generated by PUL_LC_Laplace and found insolve_real_symm(n, b, x,tol) the mesh file
integer :: N_boundary                                     ! number of viable boundaries in the mesh i.e. boundaries with two or more points on
integer :: boundary_number
integer :: N_elements_boundary_temp
integer :: N_nodes_boundary_temp
integer :: n_boundary_segments

integer,allocatable              :: N_elements_boundary(:)   ! number of elements on each boundary
integer,allocatable              :: N_nodes_boundary(:)      ! number of nodes on each boundary
type(boundary_data),allocatable  :: boundary_info(:)         ! for each boundary the boundary data is a list of elements and nodes on the boundary

real(dp),allocatable :: node_coordinates_in(:,:)          ! Node coordinate list read from gmsh - note that some of these nodes are not used in the finite element solution

integer :: N_nodes                                        ! number of nodes input to Laplace
real(dp),allocatable :: node_coordinates(:,:)             ! Node coordinate list input to the NLR Laplace process

integer :: N_elements                                     ! number of elements input to Laplace
integer,allocatable  :: Element_data(:,:)                 ! Element data input to the NLR Laplace process

integer,allocatable  :: old_node_to_new_node_number(:)    ! node re-numbering array

integer              :: N_materials         ! number of dielectric materials (including the background permittivity)
real(dp),allocatable :: Mat_prop(:,:)       ! material proparties

integer,allocatable :: Node_Type(:)         ! if the node is on a conductor then this holds the conductor number, otherwise it is zero

integer,allocatable :: Node_to_Known_Unknown(:) ! list of all the nodes which points to the appropriate place in the list of knows or unknowns as appropriate
integer,allocatable :: Vector_of_Knowns(:)      ! list of all the boundary nodes i.e. those which will have known potentials

integer     :: N_unknown                     ! number of known node voltages i.e. those on which fixed potential boundary conditions are applied
integer     :: N_known                       ! number of unknown node voltages

integer     :: jmax                          ! maximum boundary number (i.e. number of conductors)
                                             ! this is used to determine the number of finite element solutions (right hand solve_real_symm(n, b, x,tol)sides to solve for) to
                                             ! fill the capacitance/conductance matrix

integer     :: total_n_boundary_nodes        ! total number of boundary nodes i.e. the number of known node values

! array of potentials for each of the finite element vector solutions. This includes both initially known and initially unknown potentials
complex(dp),allocatable     :: V(:,:,:)

! Element properties
complex(dp),allocatable     :: b(:,:)      ! element based array of constants related to the element geometry
complex(dp),allocatable     :: c(:,:)      ! element based array of constants related to the element geometry
complex(dp),allocatable     :: delta(:)    ! element based array with a value related to the element geometry
complex(dp),allocatable     :: eps_r(:)    ! element based array with the complex relative permittivity of the element

!
!integer     :: n_entrysolve_real_symm(n, b, x,tol)
!! 1D arrays used in the construction of the K matrix ( K(i_K(:),j_K(:))=K(i_K(:),j_K(:))+s_K(:) )
!integer,allocatable         :: i_K(:)
!integer,allocatable         :: j_K(:)
!complex(dp),allocatable     :: s_K(:)

! 1D arrays used in the construction of the right hand side vectors ( K_rhs(i_K_rhs(:),j_K_rhs(:))=K_rhs(i_K_rhs(:),j_K_rhs(:))+s_K_rhs(:) )
integer,allocatable         :: i_K_rhs(:)        ! row number
integer,allocatable         :: j_K_rhs(:)        ! col number
complex(dp),allocatable     :: s_K_rhs(:)        ! value

complex(dp),allocatable     :: x(:,:,:)          ! array of solution vectors, one for each each finite element solution required to complete the C/G matrices
complex(dp),allocatable     :: x_tmp(:)          ! single solution vector
complex(dp),allocatable     :: b_tmp(:)          ! single right hand side vector: b_tmp=-matmul(K_rhs,v_tmp)
complex(dp),allocatable     :: v_tmp(:)          ! temporary array with known boundary voltages used for constructing the right hand side 

complex(dp),allocatable     :: K(:,:)         ! Finite element equation coefficient matrix
complex(dp),allocatable     :: KI(:,:)        ! Inverse of the Finite element equation coefficient matrix 
complex(dp),allocatable     :: K_rhs(:,:)     ! array of right hand side vectors, one for each each finite element solution required to complete the C/G matrices

complex(dp),allocatable     :: phi(:,:,:)     ! node based potential values for each finite element solution

complex(dp),allocatable     :: energy(:,:)    ! energy values for each of the finite element solutions

! matrices determined from energy calculations
real(dp),allocatable    :: Inductance_energy(:,:)
real(dp),allocatable    :: Capacitance_energy(:,:)
real(dp),allocatable    :: Conductance_energy(:,:)

! temporary variables used for loops, counters, intermediate results used in calculations etc.
integer :: node,new_node
integer :: element

integer     :: n_entry_K_rhs
integer     :: n1,n2,n3
complex(dp) :: x1,x2,x3,y1,y2,y3
integer     :: Mat_Type

integer     :: n1_el,n2_el,n3_el
integer     :: el
integer     :: n_el_bnd               ! loop variable for elements on the boundary
complex(dp) :: rho,gamma,ls

complex(dp) :: b3,bc_comp,c3,delta_energy,delta_Q,phi_comp
integer     :: nd1,nd2,p

real(dp)    :: x_centre,y_centre

character(LEN=filename_length) :: filename
character(LEN=line_length)     :: line

real(dp)     :: scale

! temporary variables and loop counters
integer :: i,ii,i1,i2,loop,count
integer :: nb1,nb2,nb3,nbcount
integer :: itmp,type,itmp2,itmp3,itmp4
integer :: jj,j1,j2
integer :: n_nodes_bnd
integer :: n_con
integer :: nr

integer      :: ierr   ! error code for matrix inversion 

! variables for iterative solver

integer  itnlim 
real(dp)   rtol   
      
real(dp),allocatable :: bt(:)                ! real vectors for conjugate gradient solution for lossless problems
real(dp),allocatable :: xt(:)

logical :: lossy_dielectric                  ! flag to indicate lossy dielectric i.e. we must solve a complex problem

logical :: wantse
integer :: n,m
real(dp) :: atol, btol, conlim, damp
real(dp) :: Arnorm, xnorm
real(dp),allocatable :: se(:)

real(dp) :: Vout

! START

if (verbose) write(*,*)'CALLED: Laplace'

!  STAGE 1. Read the mesh information and the associated boundary information

n_conductors=dim+1   ! number of conductors, used to establish which boundaries are conductors

! Establish the number of external boundaries evaluated as 
! the number of conducting boundaries + outer boundary if it exists
! note that dielectric boundaries are not external boundaries as they have mesh both sides.

N_boundaries=n_conductors
if (first_surface_is_free_space_boundary) N_boundaries=N_boundaries+1

! open the file with the boundary information produced by PUL_LC_Laplace and written to file there
open(unit=boundary_file_unit,file=boundary_file_name,status='OLD',err=9020)
if (verbose) write(*,*)'Opened file:',boundary_file_name

read(boundary_file_unit,*,end=9030)n_boundary_segments

ALLOCATE( boundary_segment_to_boundary_number_list(1:n_boundary_segments) )

do i=1,n_boundary_segments
  read(boundary_file_unit,*,end=9030)ii,boundary_segment_to_boundary_number_list(i)
end do 

! close the boundary information file
close(unit=boundary_file_unit)
if (verbose) write(*,*)'Closed file:',boundary_file_name

! open and read the mesh file produced by gmsh

open(unit=mesh_file_unit,file=mesh_filename,status='OLD',err=9000)
if (verbose) write(*,*)'opened mesh file:',trim(mesh_filename)

! read to the start of the node list

do
  read(mesh_file_unit,'(A)',end=9010)line
  if (line(1:6).EQ.'$Nodes') exit
end do

read(mesh_file_unit,*)n_nodes_in    

ALLOCATE ( node_coordinates_in(1:N_nodes_in,1:3) )
ALLOCATE( point_to_boundary_list(1:N_nodes_in) )

do i=1,n_nodes_in
  read(mesh_file_unit,*)itmp,node_coordinates_in(i,2),node_coordinates_in(i,3)
  node_coordinates_in(i,1)=i
end do

! read to the start of the element list

do
  read(mesh_file_unit,'(A)',end=9010)line
  if (line(1:9).EQ.'$Elements') exit
end do

! read the element list

read(mesh_file_unit,*)n_elements_in
ALLOCATE ( Element_data(1:n_elements_in,1:5) )  ! note that we allocate data for all the gmesh elements, even though some are line elements and not needed.

Element_data(1:n_elements_in,1:5)=0
point_to_boundary_list(:)=0
n_boundaries_max=0
n_elements=0       ! counter for triangular elements

do i=1,n_elements_in

! The gmsh file includes elements of type 1 (boundary line segments) and type 2 (triangular elements)
! These are dealt with differently so first read the line from the file into a string, then process the string as required
! for these two cases.

  read(mesh_file_unit,'(A256)')line
  read(line,*)itmp,type
  
  if (type.EQ.2) then 
! this is a triangular element so put the data in the element list
     n_elements=n_elements+1                                  ! increase the count of triangular elements
     
     read(line,*)itmp,type,itmp2,itmp3,  &
                 Element_data(n_elements,5),    &
                 Element_data(n_elements,2),    &
                 Element_data(n_elements,3),    &
                 Element_data(n_elements,4)
     Element_data(n_elements,1)=n_elements
     
  else if (type.EQ.1) then 
! this is a boundary segment
     read(line,*)itmp,type,itmp2,itmp3,itmp4,nb1,nb2
     bs=itmp4                                                       ! this is the boundary line segment number (not the boundary number)
     
     boundary_number=boundary_segment_to_boundary_number_list(bs)   ! get the boundary number for this line segment
     n_boundaries_max=max(n_boundaries_max,boundary_number)         ! update the maximum number of boundaries

! only include 'external' boundaries in the point_to_boundary_list, not internal dielectric boundaries
     if (boundary_number.LE.N_boundaries) then
       point_to_boundary_list(nb1)=boundary_number
       point_to_boundary_list(nb2)=boundary_number
     end if
     
  end if  ! element type
  
end do ! next element

! close the mesh file produced by gmsh
close(unit=mesh_file_unit)
if (verbose) write(*,*)'closed mesh file:',trim(mesh_filename)

! Establish whether the first surface is a free space boundary or a conductor
if (first_surface_is_free_space_boundary) then
  first_surface=0
else
  first_surface=1
end if

! STAGE 2: Convert gmsh format data to laplace format
! note that the gmsh node list includes points which are not in the mesh e.g. the points which define the centres of the circular arcs
! these points are removed in the conversion to the Laplace input structures

if (verbose) then
  write(*,*)'Number of nodes in the mesh file=',N_nodes_in
  write(*,*)'Number of elements=',N_elements
  write(*,*)' Allocate and read nodes'
  write(*,*)' Allocate and read boundary information'
  write(*,*)'Number of boundaries including dielectric boundaries=',n_boundaries_max
  write(*,*)'Number of boundaries not including dielectric boundaries=',n_boundaries
end if ! verbose

! check the boundary count
if(n_boundaries.GT.n_boundaries_max) then
  write(run_status,*)'ERROR in Laplace. Inconsistent boundary count ',N_boundaries_max,n_boundaries
  CALL write_program_status()
  STOP 1
end if

ALLOCATE ( N_elements_boundary(1:N_boundaries_max) )
ALLOCATE ( N_nodes_boundary(1:N_boundaries_max) )
ALLOCATE ( boundary_info(1:N_boundaries_max) )

! STAGE 3: Create the boundary element list but only for PEC or outer boundaries
N_boundary=0

do i=1,n_boundaries

  if(verbose) write(*,*)'Setting boundary number',i,'of ',n_boundaries

  count=0
  do loop=1,2
  
! The first time through the loop, count the number of elements with two or more nodes on the current boundary
! The second time through the loop, allocate and fill the boundary_info element information 
! (element number and boundary condition number)
  
    if (loop.eq.2) then
! count is the number of boundary elements
      N_elements_boundary_temp=count
      count=0
      if (N_elements_boundary_temp.GT.0) then
        N_boundary=N_boundary+1
        N_elements_boundary(N_boundary)=N_elements_boundary_temp
        boundary_info(N_boundary)%N_elements_boundary=N_elements_boundary_temp

        if(verbose) then
          write(*,*)'N_boundary=',N_boundary,' N_boundaries_max=',N_boundaries_max
          write(*,*)'Number of boundary elements =',N_elements_boundary_temp,  &
                              ' boundary condition',first_surface+i-1
        end if

        ALLOCATE ( boundary_info(N_boundary)%boundary_elements(1:N_elements_boundary_temp,1:2) )
        
      end if
    end if

! loop over elements    
    do ii=1,n_elements

      nb1=point_to_boundary_list(Element_data(ii,2))
      nb2=point_to_boundary_list(Element_data(ii,3))
      nb3=point_to_boundary_list(Element_data(ii,4))
! work out how many of the nodes on this element are on the current boundary
      nbcount=0
      if(nb1.eq.i) nbcount=nbcount+1
      if(nb2.eq.i) nbcount=nbcount+1
      if(nb3.eq.i) nbcount=nbcount+1
      
      if (nbcount.GE.2) then
! the element has at least one edge on this boundary
        count=count+1
        if (loop.eq.2) then
            
          boundary_info(N_boundary)%boundary_elements(count,1)=ii                 ! element number
          boundary_info(N_boundary)%boundary_elements(count,2)=first_surface+i-1  ! boundary number
        
        end if ! second time round loop so we can record the boundary data
        
      end if ! there are elements on this boundary
      
    end do ! next element in mesh
    
  end do ! next loop (1,2)
    
! create boundary node list
  count=0
  do loop=1,2
  
! The first time through the loop, count the number of nodes on the current boundary
! The second time through the loop, allocate and fill the boundary_info node information 
! (node number and boundary condition number)
  
    if (loop.eq.2) then
! count is the number of boundary nodes 
      N_nodes_boundary_temp=count
      count=0
      if (N_nodes_boundary_temp.GT.0) then  
        N_nodes_boundary(N_boundary)=N_nodes_boundary_temp    
        boundary_info(N_boundary)%N_nodes_boundary=N_nodes_boundary_temp
        
        if(verbose) then
          write(*,*)'N_boundary=',N_boundary,' N_boundaries_max=',N_boundaries_max
          write(*,*)'Number of boundary points   =',N_nodes_boundary_temp,  &
                              ' boundary condition',first_surface+i-1
        end if
        
        ALLOCATE ( boundary_info(N_boundary)%boundary_nodes(1:N_nodes_boundary_temp,1:2) )
       
      end if
    end if
    
    do ii=1,n_nodes_in
    
      if(point_to_boundary_list(ii).eq.i) then
! the node is on this boundary
        count=count+1
        if (loop.eq.2) then
          boundary_info(N_boundary)%boundary_nodes(count,1)=ii                 ! point number
          boundary_info(N_boundary)%boundary_nodes(count,2)=first_surface+i-1  ! boundary condition number
        end if ! second time round loop so we can record the boundary data
        
      end if ! there are nodes on this boundary
      
    end do ! next node in mesh
    
  end do ! next loop (1,2)
  
end do ! next boundary, i

if (verbose) write(*,*)'n_boundaries=',n_boundaries,' N_boundary=',N_boundary

! STAGE 5: Set the material information
if (verbose) write(*,*)' Read material information'

! Set for single material with free space properties and a frequency of 1MHz

N_materials=n_dielectric_regions+1             ! add a free space region
if (verbose) write(*,*)' Number of materials=',N_materials

ALLOCATE ( Mat_prop(1:N_materials,1:4) )

lossy_dielectric=.FALSE.

do i=1,N_materials
  Mat_prop(i,1)=i    ! material number
  Mat_prop(i,2)=dble(dielectric_region_epsr(i-1))   ! Re{epsr}
  Mat_prop(i,3)=aimag(dielectric_region_epsr(i-1))  ! Im{epsr}
  Mat_prop(i,4)=frequency
  
  if ( abs(Mat_prop(i,3)).GT.small ) lossy_dielectric=.TRUE.
    
  if (verbose) then
    count=0
    do element=1,N_elements
      if (Element_data(element,5).EQ.i) count=count+1
    end do
    write(*,*)'Region:',i,' epsr=',Mat_prop(i,2),'+j',Mat_prop(i,3),' n_elements=',count
  end if ! verbose
  
end do ! next material

if (verbose) write(*,*)'frequency=',frequency

! STAGE 6: Filter out the unused nodes from gmsh and renumber the new node list

! work out how many nodes are actually used in the mesh
! then recreate the node list. This is required for meshes produced by gmsh
! which have unused nodes in the coordinate list.

ALLOCATE( old_node_to_new_node_number(1:N_nodes_in) )
old_node_to_new_node_number(1:N_nodes_in)=0            ! set all the elements of the renumbering array to zero - this indicates a node has not been renumberd yet

N_nodes=0

! loop over elements
do element=1,N_elements
  
! loop over the three nodes in this element
  do i=1,3
  
    node=Element_data(element,i+1)
    
! check whether this node has already been found
    if (old_node_to_new_node_number(node).EQ.0) then
    
! new node found so increase the number of nodes and renumber this node to the new node number created
      N_nodes=N_nodes+1
      old_node_to_new_node_number(node)=N_nodes
      
    end if

! renumber the node    
    Element_data(element,i+1)=old_node_to_new_node_number(node)
    
  end do ! next node in this element
  
end do ! next element

if (verbose) then
  write(*,*)'Number of nodes read=',N_nodes_in
  write(*,*)'Number of nodes used=',N_nodes
end if

! renumber the nodes in the boundary list

do i=1,N_boundary

    n_nodes_bnd=N_nodes_boundary(i)
    
    do jj=1,n_nodes_bnd
    
      node=boundary_info(i)%Boundary_nodes(jj,1)
      boundary_info(i)%Boundary_nodes(jj,1)=old_node_to_new_node_number(node)
        
    end do ! next node on this boundary
    
end do ! next boundary

! create the node_coordinate list to be used in the NLR Laplace Finite Element solution

ALLOCATE ( node_coordinates(1:N_nodes,1:3) )

do node=1,N_nodes_in
  new_node=old_node_to_new_node_number(node)
  if (new_node.NE.0) then ! the node exists in the mesh
    node_coordinates(new_node,1:3)=node_coordinates_in(node,1:3)
  end if
end do

DEALLOCATE( old_node_to_new_node_number )
DEALLOCATE( node_coordinates_in )

! STAGE 7: Determine which of the nodes are unknown 

if(verbose) write(*,*)'Determine which of the nodes are unknown'

! Determine which of the nodes are unknown (these will be marked by
! the integer 0) and which are known (these will be marked by integers
! > 0 that indicate the conductor number)

ALLOCATE( Node_Type(1:N_nodes) )

Node_Type(1:N_nodes)=0           ! assume all nodes are unknown to start

do i=1,N_boundary    ! loop over boundaries

    n_nodes_bnd=N_nodes_boundary(i)               ! number of nodes on this boundary
    n_con=boundary_info(i)%boundary_nodes(1,2)    ! number of the boundary condition on this boundary
    
    do jj=1,n_nodes_bnd                           ! loop over the boundary nodes
        nr=boundary_info(i)%Boundary_nodes(jj,1)  ! get the node number of this boundary node
        Node_Type(nr)=n_con                       ! set the node type to the boundary conductor number
    end do
    
end do

! STAGE 8: work out the mapping of node numbers to knowns and unknowns

if(verbose) write(*,*)' Mapping node numbers to unknowns and knowns '

! Mapping node numbers to unknowns and knowns
! Mapping knowns to different boundary values
N_unknown=0
N_known=0
jmax=0

ALLOCATE ( Node_to_Known_Unknown(1:N_nodes) )
Node_to_Known_Unknown(1:N_nodes)=0

! count the total number of boundary nodes (this is the number of knowns)
total_n_boundary_nodes=0
do i=1,N_boundary
  total_n_boundary_nodes=total_n_boundary_nodes+boundary_info(i)%N_nodes_boundary
end do

if (verbose) write(*,*)'Total number of boundary nodes=',total_n_boundary_nodes

ALLOCATE ( Vector_of_Knowns(1:total_n_boundary_nodes) )
Vector_of_Knowns(1:total_n_boundary_nodes)=0

do i=1,N_nodes
    if (Node_Type(i).EQ.0) then
       N_unknown=N_unknown+1
       Node_to_Known_Unknown(i)=N_unknown
    else
       N_known=N_known+1
       Node_to_Known_Unknown(i)=N_known
       do jj=1,N_boundary
           if (Node_Type(i).EQ.jj) then
              Vector_of_Knowns(N_known)=jj
              if (jj.GT.jmax) then
                 jmax=jj
              end if
           end if
       end do
    end if   
end do

if (verbose) write(*,*)'Maximum boundary number, jmax=',jmax, 'N_boundary=',N_boundary

! STAGE 9: Create the known voltage vectors

if (verbose) write(*,*)' Create known voltage vectors'
! Create known voltage vectors with:
! 1) one conductor set to voltage=1V and the
!    other conductors all set to 0V.
! 2) two conductors set to voltage=1V and the
!    other conductors set to 1V.

ALLOCATE ( V(1:jmax-1,1:jmax-1,1:N_known) )
V(1:jmax-1,1:jmax-1,1:N_known)=0d0

! The last conductor is always set to 0V as
! it is taken as the reference. This means that the ground plane, if it exists is always the reference condcutor.

do j1=1,jmax-1
   do j2=j1,jmax-1
      do i=1,N_known
         if (Vector_of_Knowns(i).EQ.(j1)) then
            V(j1,j2,i)=1.0
         end if
         if (Vector_of_Knowns(i).EQ.(j2)) then
            V(j1,j2,i)=1.0   
         end if
      end do
   end do
end do

! Stage 10: Derive the necessary element properties
if (verbose) write(*,*)' Derive the necessary element properties'

ALLOCATE( b(1:N_elements,1:3) )
ALLOCATE( c(1:N_elements,1:3) )
ALLOCATE( delta(1:N_elements) )
ALLOCATE( eps_r(1:N_elements) )

b(1:N_elements,1:3)=0d0
c(1:N_elements,1:3)=0d0
delta(1:N_elements)=0d0
eps_r(1:N_elements)=0d0

! do this twice, the first time to count the numbers of entries and to allocate the memory, the second to fill the memory
do loop=1,2

  n_entry=0
  n_entry_K_rhs=0

! loop over elements
  do i=1,N_elements
  
! get the node numbers for the 3 nodes on this element
    n1=Element_data(i,2)
    n2=Element_data(i,3)
    n3=Element_data(i,4)
    
! get the coordinates of the nodes
    x1=Node_Coordinates(n1,2)
    y1=Node_Coordinates(n1,3)
    x2=Node_Coordinates(n2,2)
    y2=Node_Coordinates(n2,3)
    x3=Node_Coordinates(n3,2)
    y3=Node_Coordinates(n3,3)
    
! Calculate b and c coefficients, delta (element area) (equation 4.24, J-M.Jin)
    b(i,1)=y2-y3
    b(i,2)=y3-y1
    b(i,3)=y1-y2
    c(i,1)=x3-x2
    c(i,2)=x1-x3
    c(i,3)=x2-x1
    
    delta(i)=0.5*(b(i,1)*c(i,2)-b(i,2)*c(i,1))
    
    Mat_Type=Element_data(i,5)     ! material number for this element
        
    eps_r(i)=Mat_Prop(Mat_Type,2)+j*Mat_Prop(Mat_Type,3)   ! relative permittivity in this material region
   
! STAGE 10a: Determine K matrix elements related to the unknowns
! STAGE 10b: Determine K_rhs matrix that will be used to compute the rhs vector from the knowns.
! See section 4.3.3.2 for a description of the assembly of the system of equations (K matrix elements)
! and section 4.3.3.4 for a description of the assembly of the RHS terms.

    if (Node_Type(n1).EQ.0) then        ! node n1 is not on a conducting boundary
        n_entry=n_entry+1
        if (loop.EQ.2) then
          i_K(n_entry)=Node_to_Known_Unknown(n1)
          j_K(n_entry)=Node_to_Known_Unknown(n1)
          s_K(n_entry)=(eps_r(i)/(4.0*delta(i)))*(b(i,1)*b(i,1)+c(i,1)*c(i,1))       ! K11, equation 4.33 with alpha_x=alpha_y=epsr, beta=0, i=j (diagonal element)
        end if
        if (Node_Type(n2).EQ.0) then          ! neither node 1 nor node 2  are on conductors
            n_entry=n_entry+1
            if (loop.EQ.2) then
              i_K(n_entry)=Node_to_Known_Unknown(n1)
              j_K(n_entry)=Node_to_Known_Unknown(n2)
              s_K(n_entry)=(eps_r(i)/(4.0*delta(i)))*(b(i,1)*b(i,2)+c(i,1)*c(i,2))   ! K12, equation 4.33 with alpha_x=alpha_y=epsr, beta=0
            end if
            n_entry=n_entry+1
            if (loop.EQ.2) then
              i_K(n_entry)=Node_to_Known_Unknown(n2)
              j_K(n_entry)=Node_to_Known_Unknown(n1)   
              s_K(n_entry)=s_K(n_entry-1)                                            ! K21=K12
            end if
        else                                  ! node 2 is on a conductor so therefore has a known value so the contribution goes to the right hand side
            n_entry_K_rhs=n_entry_K_rhs+1
            if (loop.EQ.2) then
              i_K_rhs(n_entry_K_rhs)=Node_to_Known_Unknown(n1)
              j_K_rhs(n_entry_K_rhs)=Node_to_Known_Unknown(n2)
              s_K_rhs(n_entry_K_rhs)=(eps_r(i)/(4.0*delta(i)))*(b(i,1)*b(i,2)+c(i,1)*c(i,2))   ! equation 4.33 with alpha_x=alpha_y=epsr, beta=0
            end if
        end if
        if (Node_Type(n3).EQ.0) then          ! neither node 1 nor node 3  are on conductors
            n_entry=n_entry+1
            if (loop.EQ.2) then
              i_K(n_entry)=Node_to_Known_Unknown(n1)
              j_K(n_entry)=Node_to_Known_Unknown(n3)
              s_K(n_entry)=(eps_r(i)/(4.0*delta(i)))*(b(i,1)*b(i,3)+c(i,1)*c(i,3))   ! K13, equation 4.33 with alpha_x=alpha_y=epsr, beta=0
            end if
            n_entry=n_entry+1
            if (loop.EQ.2) then
              i_K(n_entry)=Node_to_Known_Unknown(n3)
              j_K(n_entry)=Node_to_Known_Unknown(n1)
              s_K(n_entry)=s_K(n_entry-1)                                            ! K31=K13
            end if
        else     ! node 3 is on a conductor so therefore has a known value so the contribution goes to the right hand side
            n_entry_K_rhs=n_entry_K_rhs+1
            if (loop.EQ.2) then
              i_K_rhs(n_entry_K_rhs)=Node_to_Known_Unknown(n1)
              j_K_rhs(n_entry_K_rhs)=Node_to_Known_Unknown(n3)
              s_K_rhs(n_entry_K_rhs)=(eps_r(i)/(4.0*delta(i)))*(b(i,1)*b(i,3)+c(i,1)*c(i,3))   ! equation 4.33 with alpha_x=alpha_y=epsr, beta=0
            end if
        end if
    end if
    
    if (Node_Type(n2).EQ.0) then            ! node n2 is not on a conducting boundary
        if (Node_Type(n1).NE.0) then        ! node n1 is on a conductor so therefore has a known value so the contribution goes to the right hand side
            n_entry_K_rhs=n_entry_K_rhs+1
            if (loop.EQ.2) then
              i_K_rhs(n_entry_K_rhs)=Node_to_Known_Unknown(n2)
              j_K_rhs(n_entry_K_rhs)=Node_to_Known_Unknown(n1)
              s_K_rhs(n_entry_K_rhs)=(eps_r(i)/(4.0*delta(i)))*(b(i,1)*b(i,2)+c(i,1)*c(i,2))   ! equation 4.33 with alpha_x=alpha_y=epsr, beta=0
            end if
        end if
        n_entry=n_entry+1
        if (loop.EQ.2) then
          i_K(n_entry)=Node_to_Known_Unknown(n2)
          j_K(n_entry)=Node_to_Known_Unknown(n2)
          s_K(n_entry)=(eps_r(i)/(4.0*delta(i)))*(b(i,2)*b(i,2)+c(i,2)*c(i,2))   ! equation 4.33 with alpha_x=alpha_y=epsr, beta=0, i=j (diagonal element)
        end if
        if (Node_Type(n3).EQ.0) then                  ! node n3 is not on a conducting boundary
            n_entry=n_entry+1
            if (loop.EQ.2) then
              i_K(n_entry)=Node_to_Known_Unknown(n2)
              j_K(n_entry)=Node_to_Known_Unknown(n3)
              s_K(n_entry)=(eps_r(i)/(4.0*delta(i)))*(b(i,2)*b(i,3)+c(i,2)*c(i,3))   ! K23, equation 4.33 with alpha_x=alpha_y=epsr, beta=0
            end if
            n_entry=n_entry+1
            if (loop.EQ.2) then
              i_K(n_entry)=Node_to_Known_Unknown(n3)
              j_K(n_entry)=Node_to_Known_Unknown(n2)
              s_K(n_entry)=s_K(n_entry-1)                                            ! K32=K23
            end if
        else        ! node n3 is on a conductor so therefore has a known value so the contribution goes to the right hand side
            n_entry_K_rhs=n_entry_K_rhs+1
            if (loop.EQ.2) then
              i_K_rhs(n_entry_K_rhs)=Node_to_Known_Unknown(n2)
              j_K_rhs(n_entry_K_rhs)=Node_to_Known_Unknown(n3)
              s_K_rhs(n_entry_K_rhs)=(eps_r(i)/(4.0*delta(i)))*(b(i,2)*b(i,3)+c(i,2)*c(i,3))   ! equation 4.33 with alpha_x=alpha_y=epsr, beta=0
            end if
        end if
     end if
     if (Node_Type(n3).EQ.0) then            ! node n3 is not on a conducting boundary
         if (Node_Type(n1).NE.0) then        ! node n1 is on a conductor so therefore has a known value so the contribution goes to the right hand side
             n_entry_K_rhs=n_entry_K_rhs+1
             if (loop.EQ.2) then
               i_K_rhs(n_entry_K_rhs)=Node_to_Known_Unknown(n3)
               j_K_rhs(n_entry_K_rhs)=Node_to_Known_Unknown(n1)
               s_K_rhs(n_entry_K_rhs)=(eps_r(i)/(4.0*delta(i)))*(b(i,1)*b(i,3)+c(i,1)*c(i,3))   ! equation 4.33 with alpha_x=alpha_y=epsr, beta=0
             end if
         end if
         if (Node_Type(n2).NE.0) then       ! node n2 is on a conductor so therefore has a known value so the contribution goes to the right hand side
             n_entry_K_rhs=n_entry_K_rhs+1
             if (loop.EQ.2) then
               i_K_rhs(n_entry_K_rhs)=Node_to_Known_Unknown(n3)
               j_K_rhs(n_entry_K_rhs)=Node_to_Known_Unknown(n2)
               s_K_rhs(n_entry_K_rhs)=(eps_r(i)/(4.0*delta(i)))*(b(i,2)*b(i,3)+c(i,2)*c(i,3))   ! equation 4.33 with alpha_x=alpha_y=epsr, beta=0
             end if
         end if
         n_entry=n_entry+1
         if (loop.EQ.2) then
           i_K(n_entry)=Node_to_Known_Unknown(n3)
           j_K(n_entry)=Node_to_Known_Unknown(n3)
           s_K(n_entry)=(eps_r(i)/(4.0*delta(i)))*(b(i,3)*b(i,3)+c(i,3)*c(i,3))      ! K33, equation 4.33 with alpha_x=alpha_y=epsr, beta=0, i=j (diagonal element)
         end if
     end if
     
  end do ! next element

  if ( (loop.eq.1).AND.(verbose) ) then
    write(*,*)' Determine contribution to the K matrix due to '
    write(*,*)' the unknowns from the asymptotic boundary condition on the open boundary'
  end if
  
! STAGE 10c: Determine contribution to the K matrix do the unknowns
! from the asymptotic boundary condition on the open boundary

  do i=1,N_boundary   ! loop over the boundaries in the
  
      n_el_bnd=N_elements_boundary(i)                 ! number of elements on this boundary
      n_con=boundary_info(i)%boundary_elements(1,2)   ! condcutor number on this boundary
      
      if (n_con.EQ.0) then     ! boundary condition set to zero indicates an open boundary so work out the open boundary contribution here
      
          do jj=1,n_el_bnd     ! loop over the elements on the boundary
          
              el=boundary_info(i)%boundary_elements(jj,1)        ! get the element number
              
              n1_el=Element_data(el,2)                           ! get the node numbers on this element
              n2_el=Element_data(el,3)
              n3_el=Element_data(el,4)
              
! get the two nodes on the boundary and call them n1 and n2  
              if (ismember(n1_el,boundary_info(i)%boundary_nodes,boundary_info(i)%N_nodes_boundary,1).EQ.1) then
                 n1=n1_el 
                 if (ismember(n2_el,boundary_info(i)%boundary_nodes,boundary_info(i)%N_nodes_boundary,1).EQ.1) then
                    n2=n2_el
                 else   
                    n2=n3_el
                 end if
              else
                  n1=n2_el
                  n2=n3_el
              end if
              
              x1=Node_Coordinates(n1,2)    ! get the coordinats of the boundary nodes
              y1=Node_Coordinates(n1,3)
              x2=Node_Coordinates(n2,2)
              y2=Node_Coordinates(n2,3)
              
              x_centre=(x1+x2)/2           ! get the coordinates of the centre point of the boundary element edge
              y_centre=(y1+y2)/2
              
              rho=sqrt(x_centre**2+y_centre**2)   ! get the distance from the centre of the problem space (assumed to be 0,0) to the boundary element edge centre
              
              ls=sqrt((x2-x1)**2+(y2-y1)**2)      ! l2= boundary element edge length
              
              if (use_ABC) then
! This implements an asymptotic boundary condition. This apears to cause convergence
! issues for the Capacitance matrix calculation so should be used with caution.
! The default is the Neumann boundary condition (gamma=0)
                gamma=-eps_r(el)/(log(rho)*rho)     ! gamma (see equation 4.3 with equation 4.93)
              else
! Neumann boundary condition on the outer boundary. This effectively sets the charge to zero on the outer
! boundary and the convergence of the capacitance matrix is improved compared with the ABC
                gamma=0d0   
              end if
              
              ! K11 contributions according to equation 4.51 (note delta_ij=1 if i=j, 0 otherwise)
              n_entry=n_entry+1
              if (loop.EQ.2) then
                i_K(n_entry)=Node_to_Known_Unknown(n1)
                j_K(n_entry)=Node_to_Known_Unknown(n1)
                s_K(n_entry)=gamma*ls/3.0                    ! i=j=n1
              end if
              ! K12
              n_entry=n_entry+1
              if (loop.EQ.2) then
                i_K(n_entry)=Node_to_Known_Unknown(n1)
                j_K(n_entry)=Node_to_Known_Unknown(n2)
                s_K(n_entry)=gamma*ls/6.0                    ! i=1, j=n2
              end if
              ! K21
              n_entry=n_entry+1
              if (loop.EQ.2) then
                i_K(n_entry)=Node_to_Known_Unknown(n2)
                j_K(n_entry)=Node_to_Known_Unknown(n1)
                s_K(n_entry)=gamma*ls/6.0                    ! i=2, j=n1
              end if
              ! K22
              n_entry=n_entry+1
             if (loop.EQ.2) then
                i_K(n_entry)=Node_to_Known_Unknown(n2)
                j_K(n_entry)=Node_to_Known_Unknown(n2)
                s_K(n_entry)=gamma*ls/3.0                    ! i=j=n2
             end if
          end do    ! next element on the boundary
      end if
  end do ! next boundary
  
  if (loop.eq.1) then
! Allocate memory
     
     ALLOCATE( i_K(1:n_entry) )
     ALLOCATE( j_K(1:n_entry) )
     ALLOCATE( s_K(1:n_entry) )
     
     ALLOCATE( i_K_rhs(1:n_entry_K_rhs) )
     ALLOCATE( j_K_rhs(1:n_entry_K_rhs) )
     ALLOCATE( s_K_rhs(1:n_entry_K_rhs) )
     
  end if
  
end do ! next loop i.e. after counting and allocating the memory required(loop=1) go back and fill the memory with the appropriate values (loop=2)

! STAGE 11: Matrix solution of the finite element equations
! the equation to solve is [s_K](x)=(s_K_rhs)

! allocate memory for the solution vectors
ALLOCATE ( x(1:jmax-1,1:jmax-1,1:N_unknown) )
ALLOCATE ( x_tmp(1:N_unknown) )
ALLOCATE ( b_tmp(1:N_unknown) )
ALLOCATE ( v_tmp(1:N_known) )

! solution based on a full matrix inverse 

ALLOCATE ( K_rhs(1:N_unknown,1:N_known) )

if(verbose) then
  write(*,*)'Number of entries in K    ',n_entry
  write(*,*)'Number of entries in K_rhs',n_entry_K_rhs
end if

! STAGE 11b: fill the K_rhs matrix
K_rhs(1:N_unknown,1:N_known)=0d0
do i=1,n_entry_K_rhs
  K_rhs(i_K_rhs(i),j_K_rhs(i))=K_rhs(i_K_rhs(i),j_K_rhs(i))+s_K_rhs(i)
end do

if(verbose) then
  write(*,*)'Dimension of K     is',N_unknown,N_unknown
  write(*,*)'Dimension of K_rhs is',N_unknown,N_known
else
  write(*,*)'Dimension of K in Laplace is',N_unknown,N_unknown
end if

if (direct_solver) then
  ALLOCATE ( K(1:N_unknown,1:N_unknown) )
  ALLOCATE ( KI(1:N_unknown,1:N_unknown) )

! STAGE 11a: fill the K matrix
  K(1:N_unknown,1:N_unknown)=0d0
  do i=1,n_entry
    K(i_K(i),j_K(i))=K(i_K(i),j_K(i))+s_K(i)
  end do

! STAGE 11c: Invert the K matrix
  if(verbose) write(*,*)'Invert the K matrix'
  ierr=0   ! set ierr=0 to cause an error within cinvert_Gauss_Jordan if there is a problem calculating the inverse
  CALL cinvert_Gauss_Jordan(K,N_unknown,KI,N_unknown,ierr) 

! STAGE 11d loop over all the RHS vectors solving the matrix equation 
  do j1=1,jmax-1
      do j2=j1,jmax-1
          v_tmp(1:n_known)=V(j1,j2,1:n_known)
          b_tmp(1:N_unknown)=-matmul(K_rhs,v_tmp)
          x_tmp=matmul(KI,b_tmp)
          x(j1,j2,1:N_unknown)=x_tmp(1:N_unknown)
      end do
  end do
  
else if(.NOT.lossy_dielectric) then
    
  itnlim = N_unknown * 2  ! maximum number of iterations set to twice the number of unknowns
  rtol   = cg_tol
     
  allocate(xt(1:N_unknown))
  allocate(bt(1:N_unknown))
  
  ALLOCATE( s_K_re(1:n_entry) )
  s_K_re(1:n_entry)=dble(s_K(1:n_entry))
  
! Iterative solution
   do j1=1,jmax-1
      do j2=j1,jmax-1
      
        v_tmp(1:n_known)=V(j1,j2,1:n_known)
        b_tmp(1:N_unknown)=-matmul(K_rhs,v_tmp)
        bt(1:N_unknown)=b_tmp(1:N_unknown)
        xt(1:N_unknown)=0d0   ! initial guess for xt
      
! conjugate gradient solution
        CALL solve_real_symm(N_unknown, bt, xt, rtol, itnlim)

        x(j1,j2,1:N_unknown)=xt(1:N_unknown)
     
      end do
  end do
      
  deallocate(xt)
  deallocate(bt)
  
else if (lossy_dielectric) then

  itnlim = N_unknown * 2  ! maximum number of iterations set to twice the number of unknowns
  rtol   = cg_tol
        
! Iterative solution
   do j1=1,jmax-1
      do j2=j1,jmax-1
      
        v_tmp(1:n_known)=V(j1,j2,1:n_known)
        b_tmp(1:N_unknown)=-matmul(K_rhs,v_tmp)
        x_tmp(1:N_unknown)=(0d0,0d0)   ! initial guess for x_tmp
              
! conjugate gradient solution
        CALL solve_complex_symm(N_unknown, b_tmp, x_tmp, rtol,itnlim )
 
        x(j1,j2,1:N_unknown)=x_tmp(1:N_unknown)
          
      end do
  end do
         
end if

! STAGE 12 Determine the voltage phi in each node of the mesh

ALLOCATE ( phi(1:jmax-1,1:jmax-1,1:N_nodes) )
phi(1:jmax-1,1:jmax-1,1:N_nodes)=0d0

do j1=1,jmax-1
    do j2=j1,jmax-1
        do i=1,N_nodes
            if (Node_Type(i).EQ.0) then
               phi(j1,j2,i)=x(j1,j2,Node_to_Known_Unknown(i))
            else
               phi(j1,j2,i)=V(j1,j2,Node_to_Known_Unknown(i))
            end if
        end do
    end do
end do
   
if(verbose) write(*,*)' Capacitance computation from electric energy'
! STAGE 13: Capacitance computation from electric energy

! CJS comment: Should there be a contribution due to the asymptotic boundary? The boundary is not a 0V equipotential...
! The conclusion is that this contribution should be small and the solution as implemented here converges to
! the correct solution as the outer boundary distance is increased

ALLOCATE ( energy(1:jmax-1,1:jmax-1) )

energy(1:jmax-1,1:jmax-1)=0d0

do j1=1,jmax-1
    do j2=j1,jmax-1    
          do i=1,N_elements
              n1_el=Element_data(i,2)
              n2_el=Element_data(i,3)
              n3_el=Element_data(i,4)
              do nd1=1,3
                  do nd2=1,3 
                      bc_comp=(b(i,nd1)*b(i,nd2)+c(i,nd1)*c(i,nd2))
                      phi_comp=phi(j1,j2,Element_data(i,nd1+1))*(phi(j1,j2,Element_data(i,nd2+1)))
                      delta_energy=eps0*eps_r(i)*bc_comp*phi_comp/(8.0*delta(i))  
                      energy(j1,j2)=energy(j1,j2)+delta_energy
                  end do
              end do
          end do
    end do
end do

ALLOCATE ( Capacitance_energy(1:jmax-1,1:jmax-1) )
Capacitance_energy(1:jmax-1,1:jmax-1)=0d0
ALLOCATE ( Conductance_energy(1:jmax-1,1:jmax-1) )
Conductance_energy(1:jmax-1,1:jmax-1)=0d0

do j1=1,jmax-1

! diagonal elements

    Capacitance_energy(j1,j1)=2.0*dble(energy(j1,j1))
    Conductance_energy(j1,j1)=2.0*(-2d0*pi*frequency*aimag(energy(j1,j1)))
    
! off diagonal elements
    do j2=j1+1,jmax-1

! Theory_Manual_Eqn     4.28
       Capacitance_energy(j1,j2)=dble((energy(j1,j2)-energy(j1,j1)-energy(j2,j2)))
       Capacitance_energy(j2,j1)=Capacitance_energy(j1,j2)  
            
! Theory_Manual_Eqn     4.29
       Conductance_energy(j1,j2)=-2d0*pi*frequency*aimag((energy(j1,j2)-energy(j1,j1)-energy(j2,j2)))
       Conductance_energy(j2,j1)=Conductance_energy(j1,j2)
       
    end do
end do

if(verbose) then
  write(*,*)' '
  write(*,*)'Capacitance matrix from energy'
  do j1=1,jmax-1
    write(*,8000)( Capacitance_energy(j1,j2),j2=1,jmax-1 )
  end do

  write(*,*)' '
  write(*,*)'Conductance matrix from energy'
  do j1=1,jmax-1
    write(*,8000)( Conductance_energy(j1,j2),j2=1,jmax-1 )
  end do
end if

!  STAGE 14: Inductance matrix computation from inverse of capacitance matrix

ALLOCATE ( Inductance_energy(1:jmax-1,1:jmax-1) )

ierr=0   ! set ierr=0 to cause an error if there is a problem calculating the inverse
CALL dinvert_gauss_jordan(Capacitance_energy,jmax-1,Inductance_energy,jmax-1,ierr) 

! Include the relative permittivity of the background medium
Mat_Type=1    
Inductance_energy(:,:)=Inductance_energy(:,:)*mu0*eps0*(Mat_Prop(Mat_Type,2)+j*Mat_Prop(Mat_Type,3))

if (verbose) then
  write(*,*)' '
  write(*,*)'Inductance matrix from energy'
  do j1=1,jmax-1
    write(*,8000)( Inductance_energy(j1,j2),j2=1,jmax-1 )
  end do
end if

8000 format(100E12.4)

! STAGE 15: Copy the inductance, capacitance and conductance matrices to the output matrices
! Making the matrices symmetric explicitly (may not be symmetric to machine precision from the inverse calculation for L)
if (verbose) write(*,*)' Copy L, C, G matrices'

! Make the matrices symmetric explicitly (may not be symmetric to machine precision from the inverse calculation)

do j1=1,jmax-1

  Lmat(j1,j1)=Inductance_energy(j1,j1)
  Cmat(j1,j1)=Capacitance_energy(j1,j1)
  Gmat(j1,j1)=Conductance_energy(j1,j1)

  do j2=j1+1,jmax-1
  
    Lmat(j1,j2)=Inductance_energy(j1,j2)
    Cmat(j1,j2)=Capacitance_energy(j1,j2)
    Gmat(j1,j2)=Conductance_energy(j1,j2)
  
    Lmat(j2,j1)=Inductance_energy(j1,j2)
    Cmat(j2,j1)=Capacitance_energy(j1,j2)
    Gmat(j2,j1)=Conductance_energy(j1,j2)
    
  end do
end do

! STAGE 16: plot potentials to vtk file for visualisation if required
if (plot_potential) then
! We write out the potentials in vtk format here....

! calculate a scale for the problem
  scale=0d0
  do i=1,N_nodes
    scale=max( scale,sqrt(node_coordinates(i,2)**2+node_coordinates(i,3)**2) )
  end do

! write potential as a vtk file

  do j1=1,jmax-1
   do j2=1,jmax-1
  
      write(filename,'(A,A,I1,I1,A4)')trim(mesh_filename),'_V',j1,j2,'.vtk'
      open(unit=10,file=trim(filename))
     
! write header information    
! write vtk header      
      write(10,'(A)')'# vtk DataFile Version 2.0'
      write(10,'(A)')'V.vtk'
      write(10,'(A)')'ASCII'
      write(10,'(A)')'DATASET POLYDATA'
      write(10,'(A,I10,A)')'POINTS',n_nodes,' float'

! write point data 
      do i=1,N_nodes
        write(10,8005)node_coordinates(i,2)+ox,node_coordinates(i,3)+oy,real(phi(j1,j2,i)*scale)
      end do
      
! write element data (note node numbering starts from 0 in vtk format)
      write(10,'(A,2I10)')'POLYGONS',n_elements,n_elements*4
      do i=1,N_elements
        write(10,8010)3,Element_data(i,2)-1,Element_data(i,3)-1,Element_data(i,4)-1
      end do
  
! STAGE 6. write data associated with points

! write point based data
      write(10,'(A,I10)')'POINT_DATA ',n_nodes
      write(10,'(A)')'SCALARS Field_on_cells float 1'
      write(10,'(A)')'LOOKUP_TABLE field_on_cells_table'
    
      do i=1,N_nodes
        write(10,8020)real(phi(j1,j2,i))
      end do
  
    close(unit=10)
  
   end do
 
  end do

end if ! plot_potential

! STAGE 17: plot mesh to vtk file for visualisation if required

if (plot_mesh) then
! We write out the mesh in vtk format here....

! calculate a scale for the problem
  scale=0d0
  do i=1,N_nodes
    scale=max( scale,sqrt(node_coordinates(i,2)**2+node_coordinates(i,3)**2) )
  end do

! write potential as a vtk file
  
  filename=trim(mesh_filename)//'.vtk'
  open(unit=10,file=trim(filename))
     
! write header information    
! write vtk header      
  write(10,'(A)')'# vtk DataFile Version 2.0'
  write(10,'(A)')'V.vtk'
  write(10,'(A)')'ASCII'
  write(10,'(A)')'DATASET POLYDATA'
  write(10,'(A,I10,A)')'POINTS',n_nodes,' float'

! write point data 
  do i=1,N_nodes
    write(10,8005)node_coordinates(i,2)+ox,node_coordinates(i,3)+oy,0d0
  end do
        
! write element data (note node numbering starts from 0 in vtk format)
  write(10,'(A,2I10)')'POLYGONS',n_elements,n_elements*4
  do i=1,N_elements
    write(10,8010)3,Element_data(i,2)-1,Element_data(i,3)-1,Element_data(i,4)-1
  end do
  
! STAGE 6. write data associated with points

! write point based data
  write(10,'(A,I10)')'POINT_DATA ',n_nodes
  write(10,'(A)')'SCALARS Field_on_cells float 1'
  write(10,'(A)')'LOOKUP_TABLE field_on_cells_table'
  
  do i=1,N_nodes
    write(10,8020)real(0d0)
  end do
  
  close(unit=10)
  
end if ! plot_mesh

! format specifications for potential and mesh outputs
8005  format(3E14.5)
8010  format(I3,4I12)
8020  format(E14.5) 

! STAGE 18: deallocate memory 

if (verbose) write(*,*)' Deallocate memory'

DEALLOCATE ( node_coordinates )
DEALLOCATE ( Element_data )
DEALLOCATE ( N_elements_boundary )
DEALLOCATE ( N_nodes_boundary )

do i=1,N_boundaries_max
  if (allocated( boundary_info(i)%boundary_elements )) DEALLOCATE ( boundary_info(i)%boundary_elements )
  if (allocated( boundary_info(i)%boundary_nodes ))    DEALLOCATE ( boundary_info(i)%boundary_nodes)
end do
DEALLOCATE ( boundary_info )

DEALLOCATE( boundary_segment_to_boundary_number_list )

DEALLOCATE ( Mat_Prop )
DEALLOCATE ( Node_Type )

DEALLOCATE ( Node_to_Known_Unknown )
DEALLOCATE ( Vector_of_Knowns )

DEALLOCATE ( V )

DEALLOCATE( b )
DEALLOCATE( c )
DEALLOCATE( delta )
DEALLOCATE( eps_r )
     
DEALLOCATE( i_K )
DEALLOCATE( j_K )
DEALLOCATE( s_K )
if ( ALLOCATED(s_K_re) ) DEALLOCATE( s_K_re )

DEALLOCATE( i_K_rhs )
DEALLOCATE( j_K_rhs )
DEALLOCATE( s_K_rhs )

DEALLOCATE ( x )
DEALLOCATE ( x_tmp )
DEALLOCATE ( b_tmp )
DEALLOCATE ( v_tmp )
if ( ALLOCATED(K) ) DEALLOCATE ( K )
if ( ALLOCATED(KI) ) DEALLOCATE ( KI )
DEALLOCATE ( K_rhs )

DEALLOCATE( phi )

DEALLOCATE ( energy )
DEALLOCATE ( Capacitance_energy )
DEALLOCATE ( Conductance_energy )
DEALLOCATE ( Inductance_energy )

if (verbose) write(*,*)'FINISHED Laplace'

RETURN

9000 write(run_status,*)'ERROR in Laplace opening the mesh file:',trim(mesh_filename)
CALL write_program_status()
STOP 1

9010 write(run_status,*)'ERROR in Laplace reading the mesh file:',trim(mesh_filename)
CALL write_program_status()
STOP 1

9020 write(run_status,*)'ERROR in Laplace opening the boundary file:',boundary_file_name
CALL write_program_status()
STOP 1

9030 write(run_status,*)'ERROR in Laplace reading the boundary file:',boundary_file_name
CALL write_program_status()
STOP 1

END SUBROUTINE Laplace
!
!_________________________________________________________________________________
!
!
FUNCTION ismember(number,array,size,element2) RESULT(res)

! return 1 if number is found in the array, 0 otherwise

integer,intent(IN)     :: number,size,element2
integer,intent(IN)     :: array(1:size,1:2)
integer     :: res

! local variables

integer     :: i

! start

res=0

do i=1,size
  if (number.EQ.array(i,element2)) then
    res=1
    RETURN
  end if
end do

RETURN

END FUNCTION ismember
