mesh.f90 Source File

This File Depends On

sourcefile~~mesh.f90~~EfferentGraph sourcefile~mesh.f90 mesh.f90 sourcefile~kinds.f90 kinds.f90 sourcefile~kinds.f90->sourcefile~mesh.f90
Help

Files Dependent On This One

sourcefile~~mesh.f90~~AfferentGraph sourcefile~mesh.f90 mesh.f90 sourcefile~testmesh.f90 testMesh.f90 sourcefile~mesh.f90->sourcefile~testmesh.f90
Help

Source Code


Source Code

module mesh_mod
	!! Module for 2D simplex meshes
	use kinds_mod
	implicit none
	private
	
	!==============!
	!= Parameters =!
	!==============!
	
	integer,parameter::GT_POINT_1 = 15
	integer,parameter::VT_POINT_1 = 1
	integer,parameter::ET_POINT_1 = 11
		!! Element type for a single point
	
	integer,parameter::GT_EDGE_1  = 1
	integer,parameter::VT_EDGE_1  = 3
	integer,parameter::ET_EDGE_1  = 21
		!! Element type for a 2-node edge
	
	integer,parameter::GT_EDGE_2  = 8
	integer,parameter::VT_EDGE_2  = 21
	integer,parameter::ET_EDGE_2  = 22
		!! Element type for a 3-node edge
	
	integer,parameter::GT_EDGE_3  = 26
	integer,parameter::VT_EDGE_3  = -1 ! Unsupported by VTK
	integer,parameter::ET_EDGE_3  = 23
		!! Element type for a 4-node edge
	
	integer,parameter::GT_TRIANGLE_1 = 2
	integer,parameter::VT_TRIANGLE_1 = 5
	integer,parameter::ET_TRIANGLE_1 = 31
		!! Element type for a 3-node triangle
	
	integer,parameter::GT_TRIANGLE_2 = 9
	integer,parameter::VT_TRIANGLE_2 = 22
	integer,parameter::ET_TRIANGLE_2 = 32
		!! Element type for a 6-node triangle
	
	integer,parameter::GT_TRIANGLE_3 = 21
	integer,parameter::VT_TRIANGLE_3 = -1 ! Unsupported by VTK
	integer,parameter::ET_TRIANGLE_3 = 33
		!! Element type for a 10-node triangle
	
	!=========!
	!= Types =!
	!=========!
	
	type::node_t
		!! One node in the mesh
		integer::nidx
			!! Node index
		real(wp),dimension(2)::x
			!! Node location
		integer::ngroup
			!! Node group
			!!
			!! Groups are derived from elements the node belongs to.
			!! The group with lowest dimenionality takes precidence.
		integer,dimension(:),allocatable::nodes
			!! Indicies of nodes connected to this one
		integer,dimension(:),allocatable::elements
			!! Indicies of elements this node occures in
	end type
	
	type::element_t
		!! One element in the mesh
		integer::eidx
			!! Element index
		integer::etype
			!! Element type
		integer::egroup
			!! Element group
		integer::ecolor
			!! Element color
			!!
			!! The element color can be used for parallel caculations
			!! without race conditions, comparable to red-black versions
			!! of the Gauss-Seidel algorithm.
		integer,dimension(:),allocatable::nodes
			!! Nodes of the element
		integer,dimension(:),allocatable::elements
			!! Elements connected to this one by at least one node
	end type
	
	type::group_t
		!! Physical group of elements
		integer::gidx
			!! Group index
		integer::gdim
			!! Group dimensionality \(d\)
			!!
			!! \(d \in {1,2}\)
		character(128)::gname
			!! Group name
		real(wp),dimension(:),allocatable::gdata
			!! Data for group
			!!
			!! This data can be used to store material properties
			!! or source term constants.
	end type
	
	type::mesh_t
		!! A 2D mesh of elements connecting nodes
		type(node_t),dimension(:),allocatable::nodes
			!! All nodes in the mesh
		type(element_t),dimension(:),allocatable::elements
			!! All elements in the mesh
		type(group_t),dimension(:),allocatable::groups
			!! All groups in the mesh
	contains
		procedure::readGmsh
		procedure::writeVTK
		procedure::appendScalarVTK
		procedure::appendVectorVTK
		procedure::connect
	end type
	
	!===========!
	!= Exports =!
	!===========!
	
	public::ET_POINT_1
	public::ET_EDGE_1
	public::ET_EDGE_2
	public::ET_EDGE_3
	public::ET_TRIANGLE_1
	public::ET_TRIANGLE_2
	public::ET_TRIANGLE_3
	
	public::node_t
	public::element_t
	public::group_t
	public::mesh_t

contains

	subroutine readGmsh(self,fn)
		!! Read a gmsh .msh file into memory
		class(mesh_t),intent(inout)::self
			!! Self mesh_t object
		character(*),intent(in)::fn
			!! Filename
		
		character(128)::buf
		integer::iou,ier
		
		open(file=fn,newunit=iou)
		read(iou,'(1A)',iostat=ier) buf
		do while(ier==0)
			select case(buf)
			case('$MeshFormat')
			case('$PhysicalNames')
				call readPhysical
			case('$Nodes')
				call readNodes
			case('$Elements')
				call readElements
			end select
			call complete(trim(adjustl(buf)))
			read(iou,*,iostat=ier) buf
		end do
		close(iou)
		
		call nodalGroups(self)
		call self%connect()
		
	contains
		
		subroutine complete(tag)
			!! Read a file until the end for tag is found
			character(*),intent(in)::tag
			character(128)::buf
			integer::ier
			
			read(iou,'(1A)',iostat=ier) buf
			do while(buf/='$End'//tag(2:) .and. ier==0)
				read(iou,'(1A)',iostat=ier) buf
			end do
		end subroutine complete
		
		subroutine readNodes
			!! Read the nodes from a msh file
			integer::N,i,k
			real(wp)::x,y,z
			
			read(iou,*) N
			if(allocated(self%nodes)) deallocate(self%nodes)
			allocate(self%nodes(N))
			do k=1,N
				read(iou,*) i,x,y,z
				self%nodes(i)%nidx = i
				self%nodes(i)%x = [x,y]
			end do
		end subroutine readNodes
		
		subroutine readElements
			!! Read the elements from a msh file
			integer::N,k,Nt,i,t,p
			integer,dimension(10)::nodes
			integer,dimension(10)::tags
			
			read(iou,*) N
			if(allocated(self%elements)) deallocate(self%elements)
			allocate(self%elements(N))
			do k=1,N
				read(iou,*) i,t,Nt,p,tags(1:Nt-1),nodes(1:elementNodeCount(t))
				self%elements(i)%eidx = i
				self%elements(i)%etype = elementGmshToInt(t)
				self%elements(i)%egroup = p
				self%elements(i)%nodes = nodes(1:elementNodeCount(t))
			end do
		end subroutine readElements
		
		function elementGmshToInt(gmsh_type) result(internalType)
			!! Convert gmsh element types to internal types
			integer,intent(in)::gmsh_type
			integer::internalType
			
			select case(gmsh_type)
			case(GT_POINT_1)
				internalType = ET_POINT_1
			case(GT_EDGE_1)
				internalType = ET_EDGE_1
			case(GT_EDGE_2)
				internalType = ET_EDGE_2
			case(GT_EDGE_3)
				internalType = ET_EDGE_3
			case(GT_TRIANGLE_1)
				internalType = ET_TRIANGLE_1
			case(GT_TRIANGLE_2)
				internalType = ET_TRIANGLE_2
			case(GT_TRIANGLE_3)
				internalType = ET_TRIANGLE_3
			case default
				internalType = -1
			end select
		end function elementGmshToInt
		
		function elementNodeCount(gmshType) result(nodeCount)
			!! Output the number of nodes for each gmsh element type
			integer,intent(in)::gmshType
			integer::nodeCount
			
			select case(gmshType)
			case(GT_POINT_1)
				nodeCount = 1
			case(GT_EDGE_1)
				nodeCount = 2
			case(GT_EDGE_2)
				nodeCount = 3
			case(GT_EDGE_3)
				nodeCount = 4
			case(GT_TRIANGLE_1)
				nodeCount = 3
			case(GT_TRIANGLE_2)
				nodeCount = 6
			case(GT_TRIANGLE_3)
				nodeCount = 10
			case default
				nodeCount = -1
			end select
		end function elementNodeCount
		
		subroutine readPhysical
			!! Read physical names from msh file
			integer::N,k,i,d
			
			read(iou,*) N
			if(allocated(self%groups)) deallocate(self%groups)
			allocate(self%groups(N))
			do k=1,N
				read(iou,*) d,i,self%groups(i)%gname
				self%groups(i)%gdim = d
			end do
		end subroutine readPhysical
		
		subroutine nodalGroups(self)
			!! Assign groups to nodes
			class(mesh_t),intent(inout)::self
				!! Self mesh_t object
			
			integer::i,k,l,g
			
			self%nodes%ngroup = -1
			
			do k=1,size(self%elements)
				g = self%elements(k)%egroup
				do l=1,size(self%elements(k)%nodes)
					i = self%elements(k)%nodes(l)
					
					if(self%nodes(i)%ngroup == -1) self%nodes(i)%ngroup = g
					if(self%groups(g)%gdim>self%groups(self%nodes(i)%ngroup)%gdim) cycle
					self%nodes(i)%ngroup = g
				end do
			end do
		end subroutine nodalGroups
		
	end subroutine readGmsh

	subroutine writeVTK(self,fn)
		!! Write the mesh as a VTK file
		class(mesh_t),intent(in)::self
			!! Self mesh_t object
		character(*),intent(in)::fn
			!! Filename
		
		integer::iou
		integer::k,l
		
		open(file=fn,newunit=iou)
		write(iou,'(1A)') '# vtk DataFile Version 2.0'
		write(iou,'(1A256)') '[Arbitrary data description]'
		write(iou,'(1A)') 'ASCII'
		write(iou,'(1A)') 'DATASET UNSTRUCTURED_GRID'
		write(iou,'(1A,1X,I10,1X,1A)') 'POINTS',size(self%nodes),'double'
		do k=1,size(self%nodes)
			write(iou,'(3E25.15)') real(self%nodes(k)%x,dp),0.0_dp
		end do
		write(iou,'(1A,2I10)') 'CELLS',size(self%elements), &
		& sum([(1+size(self%elements(l)%nodes),l=1,size(self%elements))])
		do k=1,size(self%elements)
			write(iou,'(15I10)') size(self%elements(k)%nodes),self%elements(k)%nodes-1
		end do
		write(iou,'(1A,1I10)') 'CELL_TYPES',size(self%elements)
		do k=1,size(self%elements)
			write(iou,'(1I10)') elementIntToVTK(self%elements(k)%etype)
		end do
		write(iou,'(1A,1I10)') 'CELL_DATA',size(self%elements)
		write(iou,'(1A,1X,1A,1X,1A,1I10)') 'SCALARS','element_idx','int',1
		write(iou,'(1A,1X,1A)') 'LOOKUP_TABLE','default'
		do k=1,size(self%elements)
			write(iou,'(1I10)') self%elements(k)%eidx
		end do
		write(iou,'(1A,1X,1A,1X,1A,1I10)') 'SCALARS','element_group','int',1
		write(iou,'(1A,1X,1A)') 'LOOKUP_TABLE','default'
		do k=1,size(self%elements)
			write(iou,'(1I10)') self%elements(k)%egroup
		end do
		write(iou,'(1A,1X,1A,1X,1A,1I10)') 'SCALARS','element_color','int',1
		write(iou,'(1A,1X,1A)') 'LOOKUP_TABLE','default'
		do k=1,size(self%elements)
			write(iou,'(1I10)') self%elements(k)%ecolor
		end do
		write(iou,'(1A,1I10)') 'POINT_DATA',size(self%nodes)
		write(iou,'(1A,1X,1A,1X,1A,1I10)') 'SCALARS','node_idx','int',1
		write(iou,'(1A,1X,1A)') 'LOOKUP_TABLE','default'
		do k=1,size(self%nodes)
			write(iou,'(1I10)') self%nodes(k)%nidx
		end do
		write(iou,'(1A,1X,1A,1X,1A,1I10)') 'SCALARS','node_group','int',1
		write(iou,'(1A,1X,1A)') 'LOOKUP_TABLE','default'
		do k=1,size(self%nodes)
			write(iou,'(1I10)') self%nodes(k)%ngroup
		end do
		close(iou)
	
	contains
	
		function elementIntToVTK(internal_type) result(vtk_type)
			!! Convert internal element types to VTK types
			integer,intent(in)::internal_type
			integer::vtk_type
			
			select case(internal_type)
			case(ET_POINT_1)
				vtk_type = VT_POINT_1
			case(ET_EDGE_1)
				vtk_type = VT_EDGE_1
			case(ET_EDGE_2)
				vtk_type = VT_EDGE_2
			case(ET_EDGE_3)
				vtk_type = VT_EDGE_3
			case(ET_TRIANGLE_1)
				vtk_type = VT_TRIANGLE_1
			case(ET_TRIANGLE_2)
				vtk_type = VT_TRIANGLE_2
			case(ET_TRIANGLE_3)
				vtk_type = VT_TRIANGLE_3
			end select
		end function elementIntToVTK
	
	end subroutine writeVTK

	subroutine appendScalarVTK(self,fn,v,vn)
		!! Append scalar point data to a file
		class(mesh_t),intent(in)::self
			!! Self mesh_t object
		character(*),intent(in)::fn
			!! Filename
		real(wp),dimension(:),intent(in)::v
			!! Variable data
		character(*),intent(in)::vn
			!! Variable name
		
		integer::N,k,iou
		
		open(file=fn,newunit=iou,access='append',status='old')
		
		N = size(v)
		
		write(iou,'(1A,1X,1A,1X,1A,1I10)') 'SCALARS',vn,'double',1
		write(iou,'(1A,1X,1A)') 'LOOKUP_TABLE','default'
		do k=1,N
			write(iou,'(1E30.15)') v(k)
		end do
		
		close(iou)
	end subroutine appendScalarVTK


	subroutine appendVectorVTK(self,fn,v,vn)
		!! Append vector point data to a file
		class(mesh_t),intent(in)::self
			!! Self mesh_t object
		character(*),intent(in)::fn
			!! Filename
		real(wp),dimension(:,:),intent(in)::v
			!! Variable data
		character(*),intent(in)::vn
			!! Variable name
		
		integer::N,k,iou
		integer,dimension(2)::S
		
		open(file=fn,newunit=iou,access='append',status='old')
		
		S = shape(v)
		N = maxval(S)
		
		write(iou,'(1A,1X,1A,1X,1A,1I10)') 'VECTORS',vn,'double'
		if( S(1)>S(2) ) then
			do k=1,N
				write(iou,'(3E30.15)') v(k,1:3)
			end do
		else if( S(1)<S(2) ) then
			do k=1,N
				write(iou,'(3E30.15)') v(1:3,k)
			end do
		end if
		
		close(iou)
	end subroutine appendVectorVTK

	subroutine connect(self)
		!! Build connectivity in the mesh
		class(mesh_t),intent(inout)::self
			!! Mesh to operate on
		
		integer,dimension(:),allocatable::c,b
		type(element_t)::e
		integer::k,l,m,n
		
		! Create nodal element lists
		c = [(0,k=1,size(self%nodes))]
		do k=1,size(self%elements)
			e = self%elements(k)
			do l=1,size(e%nodes)
				c(e%nodes(l)) = c(e%nodes(l))+1
			end do
		end do
		do k=1,size(c)
			self%nodes(k)%elements = [(0,l=1,c(k))]
		end do
		c = 0
		do k=1,size(self%elements)
			e = self%elements(k)
			do l=1,size(e%nodes)
				n = e%nodes(l)
				c(n) = c(n)+1
				self%nodes(n)%elements(c(n)) = k
			end do
		end do
		
		! Create nodal node lists
		do k=1,size(self%nodes)
			b = self%nodes(k)%elements
			self%nodes(k)%nodes = deDup([(self%elements(b(l))%nodes,l=1,size(b))],k)
		end do
		
		! Create elemental element lists
		do k=1,size(self%elements)
			b = self%elements(k)%nodes
			self%elements(k)%elements = deDup([(self%nodes(b(l))%elements,l=1,size(b))],k)
		end do
		
		! Assign element colors
		c = [(0,k=1,size(self%elements))]
		self%elements%ecolor = 0
		do m=1,size(self%elements)
			n = 0
			where(self%elements%ecolor==-1) self%elements%ecolor = 0
			do k=1,size(self%elements)
				e = self%elements(k)
				if(e%ecolor/=0) cycle
				
				self%elements(k)%ecolor = m
				do l=1,size(e%elements)
					if(self%elements(e%elements(l))%ecolor>0) cycle
					self%elements(e%elements(l))%ecolor = -1
					n = n+1
				end do
			end do
			if(n==0) exit
		end do
		
	contains
	
		function deDup(l,n) result(o)
			!! Remove duplicates from a list
			integer,dimension(:),intent(in)::l
			integer,intent(in)::n
			integer,dimension(:),allocatable::o
			
			integer::k,b
			
			o = l
			where(o==n) o = -1
			do k=1,size(l)
				if(o(k)==-1) cycle
				b = o(k)
				where(o==b) o = -1
				o(k) = b
			end do
			
			o = pack(o,o>=0)
		end function deDup
	
	end subroutine connect

end module mesh_mod