Merge pull request #8 from aselimov/development

Development
master
aselimov 5 years ago committed by GitHub
commit 840c8aa352
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -112,3 +112,45 @@ This mode merges multiple data files and creates one big simulation cell. The pa
`N` - The number of files which are being read
`dim` - the dimension they are to be stacked along, can be either `x`, `y`, or `z`. If the argument `none` is passed then the cells are just overlaid. Future options will include a delete overlap command.
## Options
Options are additional portions of code which have additional functionality. Options are performed in the order that they appear in the argument list and can be added to any mode. If wanting to use strictly options use `--convert` to specify input and output files.
### Option dislgen
```
-dislgen [ijk] [hkl] x y z char_angle poisson
```
This options adds an arbitrarily oriented dislocation into your model based on user inputs using the volterra displacement fields. The options are below
`[ijk]` - The vector for the line direction
`[hkl]` - The vector for the slip plane
`x y z` - The position of the dislocation centroid
`char_angle` - Character angle of the dislocation (0 is screw and 90 is edge)
`poisson` - Poisson's ratio used for the displacement field.
### Option disloop
````
-disloop loop_normal radius x y z bx by bz poisson
````
This option deletes vacancies on a plane which when minimized should result in a dislocation loop structure. The arguments are below:
`dim` - The box dimension which defines the normal to the loop plane. As of now this dimension must be a closed back direction, meaning that for fcc a box dimension has to be of the (111) family of planes. Either `x`, `y`, or `z`.
`n` - The number of atoms to delete on the loop plane
`x y z` - The centroid of the loop.
`bx by bz` - The burgers vector for the dislocation
`poisson` - Poisson ratio for continuum solution

@ -1,14 +1,15 @@
FC=ifort
FFLAGS=-mcmodel=large -g -O0 -stand f08 -fpe0 -traceback -check bounds,uninit -warn all -implicitnone -no-wrap-margin
#FFLAGS=-c -mcmodel=large -Ofast
#FFLAGS=-mcmodel=large -Ofast -no-wrap-margin
MODES=mode_create.o mode_merge.o mode_convert.o
OBJECTS=main.o elements.o io.o subroutines.o functions.o atoms.o call_mode.o box.o $(MODES)
OPTIONS=opt_disl.o
OBJECTS=main.o elements.o io.o subroutines.o functions.o atoms.o call_mode.o box.o call_option.o $(MODES) $(OPTIONS)
.SUFFIXES:
.SUFFIXES: .c .f .f90 .F90 .o
cacmb: $(OBJECTS)
$(FC) $(FFLAGS) $(OBJECTS) -o $@
$(FC) $(FFLAGS) $(OBJECTS) parameters.o -o $@
.f90.o:
$(FC) $(FFLAGS) -c $<
@ -18,16 +19,25 @@ clean:
$(RM) cacmb *.o
testfuncs: testfuncs.o functions.o subroutines.o
$(FC) testfuncs.o functions.o subroutines.o elements.o -o $@
$(FC) testfuncs.o functions.o subroutines.o box.o elements.o -o $@
.PHONY: cleantest
cleantest:
$(RM) testfuncs testfuncs.o
.PHONY: test
test: testfuncs
./testfuncs
.PHONY: install
install: cacmb
cp ./cacmb /usr/local/bin
$(OBJECTS) : parameters.o
atoms.o subroutines.o testfuncs.o : functions.o
main.o io.o build_subroutines.o: elements.o
atoms.o subroutines.o testfuncs.o box.o : functions.o
main.o io.o $(MODES) $(OPTIONS) : elements.o
call_mode.o : $(MODES)
call_option.o : $(OPTIONS)
$(MODES) io.o: atoms.o box.o
$(MODES) main.o : io.o
testfuncs.o elements.o mode_create.o: subroutines.o
testfuncs.o elements.o mode_create.o opt_disl.o: subroutines.o

@ -1,11 +1,11 @@
module box
!This module contains information on the properties of the current box.
use parameters
use functions
implicit none
real(kind=dp) :: box_bd(6) !Global box boundaries
character(len=3) :: box_bc !Box boundary conditions (periodic or shrinkwrapped)
!The subbox variables contain values for each subbox, being the boxes read in through some
!command. Currently only mode_merge will require sub_boxes, for mode_create it will always
!allocate to only 1 sub_box
@ -14,12 +14,19 @@ module box
real(kind=dp), allocatable :: sub_box_ori(:,:,:)!Orientations for each of the subboxes
real(kind=dp), allocatable :: sub_box_bd(:,:)!Boundaries for each of the sub_boxes
!Below are some simulation parameters which may be adjusted if reading in restart files
integer :: timestep=0
real(kind=dp) :: total_time=0.0_dp
public
contains
subroutine box_init
!Initialize some box functions
box_bd(:) = 0.0_dp
box_bc = 'ppp'
end subroutine box_init
subroutine alloc_sub_box(n)
@ -27,8 +34,14 @@ module box
integer, intent(in) :: n
allocate(sub_box_ori(3,3,n), sub_box_bd(6,n), sub_box_array_bd(2,2,n))
integer :: i
allocate(sub_box_ori(3,3,n), sub_box_bd(6,n), sub_box_array_bd(2,2,n))
do i = 1, n
sub_box_ori(:,:,i) = identity_mat(3)
sub_box_bd(:,i) = 0.0_dp
sub_box_array_bd(:,:,i) = 1
end do
end subroutine alloc_sub_box
subroutine grow_sub_box(n)
@ -51,7 +64,7 @@ module box
call move_alloc(temp_bd, sub_box_bd)
temp_array_bd(:,:,1:sub_box_num) = sub_box_array_bd
temp_array_bd(:,:,sub_box_num+1:) = 0.0_dp
temp_array_bd(:,:,sub_box_num+1:) = 1
call move_alloc(temp_array_bd, sub_box_array_bd)
return
@ -72,4 +85,21 @@ module box
return
end subroutine grow_box
subroutine in_sub_box(r, which_sub_box)
!This returns which sub_box a point is in. It returns the first sub_box with boundaries which
!contain the point.
real(kind=dp), dimension(3), intent(in) :: r
integer, intent(out) :: which_sub_box
integer :: i
do i = 1, sub_box_num
if( in_block_bd(r, sub_box_bd(:,i))) then
which_sub_box = i
exit
end if
end do
return
end subroutine in_sub_box
end module box

@ -1,4 +1,4 @@
subroutine call_mode(arg_num,mode)
subroutine call_mode(arg_pos,mode)
!This code is used to parse the command line argument for the mode information and calls the required
!mode module.
@ -9,16 +9,16 @@ subroutine call_mode(arg_num,mode)
implicit none
integer, intent(in) :: arg_num
integer, intent(out) :: arg_pos
character(len=100), intent(in) :: mode
select case(mode)
case('--create')
call create
call create(arg_pos)
case('--convert')
call convert
call convert(arg_pos)
case('--merge')
call merge
call merge(arg_pos)
case default
print *, "Mode ", trim(adjustl(mode)), " currently not accepted. Please check documentation for ", &
"accepted modes and rerun."

@ -3,16 +3,17 @@ module elements
!This module contains the elements data structures, structures needed for building regions
!and operations that are done on elements
use parameters
use functions
use subroutines
implicit none
!Data structures used to represent the CAC elements. Each index represents an element
character(len=100), allocatable :: type_ele(:) !Element type
integer, allocatable :: size_ele(:), lat_ele(:) !Element siz
integer, allocatable :: size_ele(:), lat_ele(:), sbox_ele(:) !Element size
real(kind=dp), allocatable :: r_node(:,:,:,:) !Nodal position array
integer :: ele_num=0 !Number of elements
integer :: node_num=0 !Total number of nodes
integer, save :: ele_num !Number of elements
integer, save :: node_num !Total number of nodes
!Data structure used to represent atoms
integer, allocatable :: type_atom(:)!atom type
@ -38,6 +39,7 @@ module elements
!This can be easily increased with no change to efficiency
integer :: max_basisnum, basisnum(10) !Max basis atom number, number of basis atoms in each lattice type
integer :: basis_type(10,10)
real(kind=dp) :: lapa(10)
public
contains
@ -85,6 +87,9 @@ module elements
max_basisnum = 0
basisnum(:) = 0
ng_node(:) = 0
node_num = 0
ele_num = 0
atom_num = 0
end subroutine lattice_init
subroutine cell_init(lapa,esize,ele_type, orient_mat, cell_mat)
@ -98,10 +103,28 @@ module elements
real(kind=dp), dimension(3,max_ng_node), intent(out) :: cell_mat
integer :: inod, i
real(kind=dp), dimension(3,max_ng_node) :: adjustVar
adjustVar(:,:) = 0.0_dp
select case(trim(ele_type))
case('fcc')
cell_mat(:,1:8) = lapa * ((esize-1)*matmul(orient_mat, fcc_cell))
if(lmpcac) then
do inod = 1, 8
do i = 1,3
if(is_equal(cubic_cell(i, inod),0.0_dp)) then
adjustVar(i,inod) = -0.5_dp
else
adjustVar(i, inod) = 0.5_dp
end if
end do
end do
adjustVar(:,1:8) = matmul(fcc_mat, adjustVar(:,1:8))
end if
cell_mat(:, 1:8) = fcc_cell + adjustVar(:,1:8)
cell_mat(:,1:8) = lapa * ((esize-1)*matmul(orient_mat, cell_mat(:,1:8)))
case default
print *, "Element type ", trim(ele_type), " currently not accepted"
stop
@ -118,7 +141,7 @@ module elements
!Allocate element arrays
if(n > 0) then
allocate(type_ele(n), size_ele(n), lat_ele(n), r_node(3,max_basisnum, max_ng_node,n), &
allocate(type_ele(n), size_ele(n), lat_ele(n), sbox_ele(n), r_node(3,max_basisnum, max_ng_node,n), &
stat=allostat)
if(allostat > 0) then
print *, "Error allocating element arrays in elements.f90 because of: ", allostat
@ -157,12 +180,17 @@ module elements
allocate(temp_int(n+ele_num+buffer_size))
temp_int(1:ele_size) = lat_ele
temp_int(ele_size+1:) = 0
call move_alloc(temp_int(1:ele_size), lat_ele)
call move_alloc(temp_int, lat_ele)
allocate(temp_int(n+ele_num+buffer_size))
temp_int(1:ele_size) = size_ele
temp_int(ele_size+1:) = 0
call move_alloc(temp_int(1:ele_size), size_ele)
call move_alloc(temp_int, size_ele)
allocate(temp_int(n+ele_num+buffer_size))
temp_int(1:ele_size) = lat_ele
temp_int(ele_size+1:) = 0
call move_alloc(temp_int, sbox_ele)
allocate(char_temp(n+ele_num+buffer_size))
char_temp(1:ele_size) = type_ele
@ -188,9 +216,9 @@ module elements
end if
end subroutine
subroutine add_element(type, size, lat, r)
subroutine add_element(type, size, lat, sbox, r)
!Subroutine which adds an element to the element arrays
integer, intent(in) :: size, lat
integer, intent(in) :: size, lat, sbox
character(len=100), intent(in) :: type
real(kind=dp), intent(in) :: r(3, max_basisnum, max_ng_node)
@ -198,6 +226,7 @@ module elements
type_ele(ele_num) = type
size_ele(ele_num) = size
lat_ele(ele_num) = lat
sbox_ele(ele_num) = sbox
r_node(:,:,:,ele_num) = r(:,:,:)
node_num = node_num + ng_node(lat)
@ -225,8 +254,11 @@ module elements
exists = .false.
do i=1, 10
if(type == type_to_name(i)) exists = .true.
if(type == type_to_name(i)) then
exists = .true.
inttype = i
exit
end if
end do
if (exists.eqv..false.) then
@ -349,4 +381,26 @@ module elements
return
end subroutine rhombshape
subroutine delete_atoms(num, index)
!This subroutine deletes atoms from the atom arrays
integer, intent(in) :: num
integer, intent(inout), dimension(num) :: index
integer :: i, j
call heapsort(index)
!We go from largest index to smallest index just to make sure that we don't miss
!accidentally overwrite values which need to be deleted
do i = num, 1, -1
if(index(i) == atom_num) then
r_atom(:,index(i)) = 0.0_dp
type_atom(index(i)) = 0
else
r_atom(:,index(i)) = r_atom(:, atom_num)
type_atom(index(i)) = type_atom(atom_num)
end if
atom_num = atom_num - 1
end do
end subroutine
end module elements

@ -232,4 +232,22 @@ END FUNCTION StrDnCase
end if
return
end function is_equal
pure function unitvec(n,vec)
integer, intent(in) :: n
real(kind=dp), intent(in) :: vec(n)
real(kind=dp) :: unitvec(n)
unitvec = vec/norm2(vec)
return
end function unitvec
pure function norm_dis(rl,rk)
!This just returns the magnitude of the vector between two points
real(kind=dp), dimension(3), intent(in) :: rl, rk
real(kind=dp) :: norm_dis(4)
norm_dis(1:3) = (rk - rl)
norm_dis(4) = norm2(rk-rl)
end function
end module functions

@ -10,6 +10,7 @@ module io
integer :: outfilenum = 0, infilenum = 0
character(len=100) :: outfiles(10), infiles(10)
public
contains
@ -58,13 +59,18 @@ module io
cycle
end if
select case(temp_outfile(scan(temp_outfile,'.',.true.)+1:))
case('xyz', 'lmp', 'vtk', 'mb')
case('xyz', 'lmp', 'vtk', 'mb', 'restart')
outfilenum=outfilenum+1
outfiles(outfilenum) = temp_outfile
exit
case('cac')
lmpcac = .true.
outfilenum=outfilenum+1
outfiles(outfilenum) = temp_outfile
exit
case default
print *, "File type: ", trim(temp_outfile(scan(temp_outfile,'.',.true.):)), " not currently accepted. ", &
"please input a filename with extension from following list: xyz, lmp, vtk."
"please input a filename with extension from following list: xyz, lmp, vtk, cac."
read(*,*) temp_outfile
end select
@ -72,7 +78,6 @@ module io
end subroutine get_out_file
subroutine write_out
!This subroutine loops over alll of the outfile types defined and calls the correct writing subroutine
@ -92,6 +97,10 @@ module io
call write_vtk(outfiles(i))
case('mb')
call write_mb(outfiles(i))
case('restart')
call write_pycac(outfiles(i))
case('cac')
call write_lmpcac(outfiles(i))
case default
print *, "The extension ", trim(adjustl(outfiles(i)(scan(outfiles(i),'.',.true.)+1:))), &
" is not accepted for writing. Please select from: xyz and try again"
@ -107,16 +116,10 @@ module io
!This is the simplest visualization subroutine, it writes out all nodal positions and atom positions to an xyz file
character(len=100), intent(in) :: file
integer :: node_num, i, inod, ibasis
integer :: i, inod, ibasis
open(unit=11, file=trim(adjustl(file)), action='write', status='replace',position='rewind')
!Calculate total node number
node_num=0
do i = 1, ele_num
node_num = node_num + basisnum(lat_ele(i))*ng_node(lat_ele(i))
end do
!Write total number of atoms + elements
write(11, '(i16)') node_num+atom_num
@ -197,6 +200,87 @@ module io
end do
end subroutine write_lmp
subroutine write_lmpcac(file)
!This subroutine writes out a .lmp style dump file
character(len=100), intent(in) :: file
integer :: write_num, i, inod, ibasis
real(kind=dp) :: mass, fcc_adjust(3,8), local_adjust(3,8), rout(3)
1 format(i16, ' Eight_Node', 4i16)
2 format(i16, ' Atom', 4i16)
3 format(3i16,3f23.15)
open(unit=11, file=trim(adjustl(file)), action='write', status='replace',position='rewind')
!Comment line
write(11, '(a)') '# CAC input file made with cacmb'
write(11, '(a)')
!Calculate total atom number
write_num = atom_num + ele_num
!Write total number of atoms + elements
write(11, '(i16, a)') write_num, ' cac elements'
!Write number of atom types
write(11, '(i16, a)') atom_types, ' atom types'
write(11,'(a)') ' '
!Write box bd
write(11, '(2f23.15, a)') box_bd(1:2), ' xlo xhi'
write(11, '(2f23.15, a)') box_bd(3:4), ' ylo yhi'
write(11, '(2f23.15, a)') box_bd(5:6), ' zlo zhi'
!Masses
write(11, '(a)') 'Masses'
write(11, '(a)') ' '
do i =1, atom_types
call atommass(type_to_name(i),mass)
write(11, '(i16, f23.15, 2a)') i, mass, ' # ', type_to_name(i)
end do
write(11, '(a)') ' '
write(11, '(a)') 'CAC Elements'
write(11, '(a)') ' '
!Set up the nodal adjustment variables for all the different element types. This adjusts the node centers
!from the center of the unit cell (as formulated in this code) to the corners of the unit cells
do inod = 1, 8
do i = 1,3
if(is_equal(cubic_cell(i, inod),0.0_dp)) then
fcc_adjust(i,inod) = -0.5_dp
else
fcc_adjust(i, inod) = 0.5_dp
end if
end do
end do
fcc_adjust = matmul(fcc_mat, fcc_adjust)
!Write element nodal positions
do i = 1, ele_num
select case(trim(adjustl(type_ele(i))))
case('fcc')
!Now orient the current adjustment vector to the correct orientation
local_adjust = matmul(sub_box_ori(:,:,sbox_ele(i)), fcc_adjust) * lapa(lat_ele(i))
!The first entry is the element specifier
write(11,1) i, basisnum(lat_ele(i)), size_ele(i), size_ele(i), size_ele(i)
do ibasis = 1, basisnum(lat_ele(i))
do inod = 1, 8
!Nodal information for every node
rout = r_node(:,ibasis,inod,i) + local_adjust(:,inod)
write(11,3) inod, ibasis, basis_type(ibasis,lat_ele(i)), rout
end do
end do
end select
end do
do i = 1, atom_num
!Element specifier dictating that it is an atom
write(11,2) ele_num+i, 1, 1, 1, 1
!Write the atomic information
write(11,3) 1, 1, type_atom(i), r_atom(:,i)
end do
end subroutine write_lmpcac
subroutine write_vtk(file)
!This subroutine writes out a vtk style dump file
integer :: i, j, inod, ibasis
@ -253,7 +337,7 @@ module io
do i = 1, ele_num
do inod=1, ng_node(lat_ele(i))
do ibasis = 1, basisnum(lat_ele(i))
write(11, '(3f23.1)') sum(r_node(:,:,inod,i),2)/basisnum(lat_ele(i))
write(11, '(3f23.15)') sum(r_node(:,:,inod,i),2)/basisnum(lat_ele(i))
end do
end do
end do
@ -273,6 +357,123 @@ module io
close(11)
end subroutine
subroutine write_pycac(file)
!This subroutine writes restart files meant to be used with the McDowell Group CAC code.
!NOTE: This code doesn't work for arbitrary number of basis atoms per node. It assumes that the
!each element has only 1 atom type at the node.
character(len=100), intent(in) :: file
integer :: interp_max, i, j, lat_size, inod, ibasis, ip
real(kind=dp) :: box_vec(3)
1 format('time' / i16, f23.15)
2 format('number of elements' / i16)
3 format('number of nodes' / i16)
4 format('element types' / i16)
5 format('number of atoms' / i16)
6 format('number of grains' / i16)
7 format('boundary ' / 3a1)
8 format('box bound' / 6f23.15)
9 format('box length' / 3f23.15)
10 format('box matrix')
11 format(3f23.15)
12 format('coarse-grained domain')
13 format('ie ele_type grain_ele lat_type_ele'/ 'ip ibasis type x y z')
14 format('atomistic domain' / 'ia grain_atom type_atom x y z')
15 format('maximum lattice periodicity length' / 3f23.15)
16 format('Number of lattice types and atom types '/ 2i16)
17 format('lattice type IDs')
18 format('lattice types for grains')
19 format('max nodes per element' / i16)
20 format('max interpo per element' / i16)
21 format('atom types to elements')
open(unit=11, file=trim(adjustl(file)), action='write', status='replace',position='rewind')
write(11,1) timestep, total_time
write(11,2) ele_num
!Below writes the header information for the restart file
!Calculate the max number of atoms per element
select case(max_ng_node)
case(8)
interp_max = (max_esize)**3
end select
write(11,20) interp_max
write(11,3) node_num
write(11,19) max_ng_node
write(11,4) lattice_types
write(11,5) atom_num
write(11,6) 1 !Grain_num is ignored
write(11,16) lattice_types, atom_types
write(11,21)
do i = 1, atom_types
write(11,*) i, type_to_name(i)
end do
write(11,7) box_bc(1:1), box_bc(2:2), box_bc(3:3)
write(11,18)
write(11,'(2i16)') 1,1 !This is another throwaway line that is meaningless
write(11,17)
!This may have to be updated in the future but currently the only 8 node element is fcc
do i = 1, lattice_types
select case(ng_node(i))
case(8)
write(11, *) i, 'fcc'
end select
end do
write(11,15) 1.0_dp, 1.0_dp, 1.0_dp !Another throwaway line that isn't needed
write(11,8) box_bd
write(11,9) box_bd(2)-box_bd(1), box_bd(4) - box_bd(3), box_bd(6)-box_bd(5)
write(11,10)
!Current boxes are limited to being rectangular
do i = 1,3
box_vec(:) = 0.0_dp
box_vec(i) = box_bd(2*i) - box_bd(2*i-1)
write(11,11) box_vec
end do
!We write this as box_mat ori and box_mat current
do i = 1,3
box_vec(:) = 0.0_dp
box_vec(i) = box_bd(2*i) - box_bd(2*i-1)
write(11,11) box_vec
end do
!write the element information
if(ele_num > 0) then
write(11,12)
do i = 1, lattice_types
do j = 1, ele_num
if (lat_ele(j) == i) then
lat_size = size_ele(j)-1
exit
end if
end do
write(11,'(3i16)') i, lat_size, basis_type(1,i)
end do
ip = 0
write(11,13)
do i = 1, ele_num
write(11, '(4i16)') i, lat_ele(i), 1, basis_type(1,lat_ele(i))
do inod = 1, ng_node(lat_ele(i))
do ibasis = 1, basisnum(lat_ele(i))
ip = ip + 1
write(11, '(2i16, 3f23.15)') ip, ibasis, r_node(:, ibasis, inod, i)
end do
end do
end do
end if
!Now write the atomic information
if(atom_num /= 0) then
write(11,14)
do i = 1, atom_num
write(11, '(3i16, 3f23.15)') i, 1, type_atom(i), r_atom(:,i)
end do
end if
close(11)
end subroutine write_pycac
subroutine write_mb(file)
!This subroutine writes the cacmb formatted file which provides necessary information for building models
@ -296,11 +497,13 @@ module io
end do
!Write the number of atom types in the current model and all of their names
write(11,*) atom_types, (type_to_name(i), i=1, atom_types)
write(11,*) atom_types, (type_to_name(i)//' ', i=1, atom_types)
!Write the number of lattice_types, basisnum and number of nodes for each lattice type
write(11,*) lattice_types, (basisnum(i), i = 1, lattice_types), (ng_node(i), i = 1, lattice_types)
!Now for every lattice type write the basis atom types
write(11,*) ((basis_type(i,j), i = 1, basisnum(j)), j = 1, lattice_types)
!Now for every lattice type write the lattice parameters
write(11,*) (lapa(i), i = 1, lattice_types)
!Now write the numbers of elements and atoms
write(11,*) atom_num, ele_num
@ -313,7 +516,7 @@ module io
!Write out the elements, this is written in two stages, one line for the element and then 1 line for
!every basis at every node
do i = 1, ele_num
write(11, *) i, lat_ele(i), size_ele(i), type_ele(i)
write(11, *) i, lat_ele(i), size_ele(i), sbox_ele(i), type_ele(i)
do inod = 1, ng_node(lat_ele(i))
do ibasis =1, basisnum(lat_ele(i))
write(11,*) inod, ibasis, r_node(:, ibasis, inod, i)
@ -321,8 +524,10 @@ module io
end do
end do
close(11)
end subroutine write_mb
!!!!!!!!!!!!! Below are subroutines for reading files !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
subroutine get_in_file(filename)
@ -394,10 +599,12 @@ module io
real(kind=dp), dimension(3), intent(in) :: displace
real(kind = dp), dimension(6), intent(out) :: temp_box_bd
integer :: i, j, k, n, inod, ibasis, type, size, in_atoms, in_eles
integer :: i, j, k, n, inod, ibasis, type, size, in_atoms, in_eles, new_atom_types, &
new_type_to_type(10), new_lattice_types, sbox
character(len=100) :: etype
real(kind=dp) :: r(3), newdisplace(3)
real(kind=dp), allocatable :: r_innode(:,:,:)
character(len = 2) :: new_type_to_name(10)
!First open the file
open(unit=11, file=trim(adjustl(file)), action='read',position='rewind')
@ -427,21 +634,36 @@ module io
read(11,*) sub_box_bd(:,sub_box_num+i)
sub_box_bd(:,sub_box_num+i) = sub_box_bd(:, sub_box_num+i) + displace(:)
!Read in sub_box_array_bd
read(11,*) ((sub_box_ori(j, k, sub_box_num+i), j = 1, 2), k = 1, 2)
read(11,*) ((sub_box_array_bd(j, k, sub_box_num+i), j = 1, 2), k = 1, 2)
end do
sub_box_num = sub_box_num + n
!Add the existing element boundaries
sub_box_array_bd(:,1,sub_box_num+1:) = sub_box_array_bd(:,1,sub_box_num+1:) + atom_num
sub_box_array_bd(:,2,sub_box_num+1:) = sub_box_array_bd(:,2,sub_box_num+1:) + ele_num
!Read in the number of atom types and all their names
read(11, *) atom_types, (type_to_name(i), i = 1, atom_types)
read(11, *) new_atom_types, (new_type_to_name(i), i = 1, new_atom_types)
!Now fit these into the global list of atom types, after this new_type_to_type is the actual global
!type of the atoms within this file
do i = 1, new_atom_types
call add_atom_type(new_type_to_name(i), new_type_to_type(i))
end do
!Read the number of lattice types, basisnum, and number of nodes for each lattice type
read(11,*) lattice_types, (basisnum(i), i = 1, lattice_types), (ng_node(i), i = 1, lattice_types)
read(11,*) new_lattice_types, (basisnum(i), i = lattice_types+1, lattice_types+new_lattice_types), &
(ng_node(i), i = lattice_types+1, lattice_types+new_lattice_types)
!Define max_ng_node and max_basis_num
max_basisnum = maxval(basisnum)
max_ng_node = maxval(ng_node)
!Read the basis atom types for every lattice
read(11,*) ((basis_type(i,j), i = 1, basisnum(j)), j = 1, lattice_types)
read(11,*) ((basis_type(i,j), i = 1, basisnum(j)), j = lattice_types+1, lattice_types+new_lattice_types)
!Convert the basis_atom types
do j = lattice_types+1, lattice_types+new_lattice_types
do i = 1, basisnum(j)
basis_type(i,j) = new_type_to_type(basis_type(i,j))
end do
end do
!Read the lattice parameters for every lattice type
read(11,*) (lapa(i), i = lattice_types+1, lattice_types+new_lattice_types)
!Read number of elements and atoms and allocate arrays
read(11, *) in_atoms, in_eles
call grow_ele_arrays(in_eles, in_atoms)
@ -450,23 +672,31 @@ module io
!Read the atoms
do i = 1, in_atoms
read(11,*) j, type, r(:)
call add_atom(type, r+newdisplace)
call add_atom(new_type_to_type(type), r+newdisplace)
end do
!Read the elements
do i = 1, in_eles
read(11, *) n, type, size, etype
read(11, *) n, type, size, sbox, etype
do inod = 1, ng_node(type)
do ibasis =1, basisnum(type)
read(11,*) j, k, r_innode(:, ibasis, inod)
r_innode(:,ibasis,inod) = r_innode(:, ibasis, inod) + newdisplace
end do
end do
call add_element(etype, size, type, r_innode)
type = type + lattice_types
call add_element(etype, size, type, sbox+n, r_innode)
end do
!Close the file being read
close(11)
!Only increment the lattice types if there are elements, if there are no elements then we
!just overwrite the arrays
if(in_eles > 0) lattice_types = lattice_types + new_lattice_types
sub_box_num = sub_box_num + n
end subroutine read_mb
end module io

@ -17,25 +17,59 @@ program main
use io
integer :: arg_num
character(len=100) :: mode
integer :: i, end_mode_arg, arg_num, arg_pos
character(len=100) :: argument
!Call initialization functions
call lattice_init
call box_init
end_mode_arg = 0
! Command line parsing
arg_num = command_argument_count()
!Determine if a mode is being used and what it is. The first argument has to be the mode
!if a mode is being used
call get_command_argument(1, mode)
call get_command_argument(1, argument)
mode = trim(adjustl(mode))
if (mode(1:2) == '--') then
call call_mode(arg_num, mode)
argument = trim(adjustl(argument))
if (argument(1:2) == '--') then
call call_mode(end_mode_arg, argument)
end if
!Finish by writing the files
!Check to make sure a mode was called
if (end_mode_arg==0) then
stop "Nothing to do, please run cacmb using an available mode"
end if
!Now we loop through all of the arguments and check for passed options or for a filename to be written out
arg_pos = end_mode_arg
do while(.true.)
!Exit the loop if we are done reading
if(arg_pos > arg_num) exit
call get_command_argument(arg_pos, argument)
!Check to see if a filename was passed
if(scan(argument,'.',.true.) > 0) then
call get_out_file(argument)
arg_pos = arg_pos + 1
!Check to see if an option has been passed
else if(argument(1:1) == '-') then
call call_option(argument, arg_pos)
!Otherwise print that the argument is not accepted and move on
else
print *, trim(adjustl(argument)), " is not accepted. Skipping to next argument"
arg_pos = arg_pos + 1
end if
end do
!Check to make sure a file was passed to be written out and then write out
! Before building do a check on the file
if (outfilenum == 0) then
argument = 'none'
call get_out_file(argument)
end if
call write_out
end program main

@ -8,8 +8,9 @@ module mode_convert
public
contains
subroutine convert
subroutine convert(arg_pos)
!This subroutine converts a single input file from one format to another
integer, intent(out) :: arg_pos
character(len=100) :: infile, outfile
real(kind = dp) :: temp_box_bd(6)
!We have to allocate the element and atom arrays with a size of 1 for the read in code to work
@ -19,10 +20,7 @@ module mode_convert
call get_in_file(infile)
call read_in(1, (/0.0_dp,0.0_dp,0.0_dp/), temp_box_bd)
call grow_box(temp_box_bd)
!Now get the outfile, writing is done after all the codes complete
call get_command_argument(3, outfile)
call get_out_file(outfile)
arg_pos = 3
end subroutine convert
end module mode_convert

@ -12,8 +12,8 @@ module mode_create
character(len=100) :: name, element_type
real(kind = dp) :: lattice_parameter, orient(3,3), cell_mat(3,8), box_len(3), basis(3,3), origin(3), maxlen(3), &
orient_inv(3,3), box_vert(3,8), maxbd(3), lattice_space(3)
integer :: esize, duplicate(3), ix, iy, iz, box_lat_vert(3,8), lat_ele_num, lat_atom_num, bd_in_lat(6), &
orient_inv(3,3), box_vert(3,8), maxbd(3), lattice_space(3), duplicate(3)
integer :: esize, ix, iy, iz, box_lat_vert(3,8), lat_ele_num, lat_atom_num, bd_in_lat(6), &
basis_pos(3,10)
logical :: dup_flag, dim_flag
@ -21,10 +21,11 @@ module mode_create
public
contains
subroutine create()
subroutine create(arg_pos)
! Main subroutine which controls execution
character(len=100) :: textholder
integer, intent(out) :: arg_pos
integer :: i, ibasis, inod
real(kind=dp), allocatable :: r_node_temp(:,:,:)
@ -45,29 +46,22 @@ module mode_create
lat_atom_num = 0
!First we parse the command
call parse_command()
! Before building do a check on the file
if (outfilenum == 0) then
textholder = 'none'
call get_out_file(textholder)
end if
call parse_command(arg_pos)
!Now we setup the unit element and call other init subroutines
call def_ng_node(1, element_type)
allocate(r_node_temp(3,max_basisnum,max_ng_node))
if(dup_flag) then
!We initialize the cell with a lattice_parameter of 1 because we will add the lattice parameter later
call cell_init(1.0_dp, esize, element_type, orient, cell_mat)
!Get the inverse orientation matrix we will need later
call matrix_inverse(orient,3,orient_inv)
if(dup_flag) then
!Define box vertices
do i = 1, 8
box_vert(:,i) = duplicate(:)*esize*lattice_space(:)*cubic_cell(:,i) + origin(:)
box_vert(:,i) = duplicate(:)*esize*lattice_space(:)*cubic_cell(:,i) + (origin(:)/lattice_parameter)
end do
call matrix_inverse(orient,3,orient_inv)
!Now get the rotated box vertex positions in lattice space. Should be integer units
box_lat_vert = int(matmul(fcc_inv, matmul(orient_inv, box_vert)))+1
!Find the new maxlen
@ -76,21 +70,25 @@ module mode_create
box_bd(2*i) = maxval(box_vert(i,:)) - 0.25_dp*lattice_space(i)
box_bd(2*i-1) = origin(i)-0.25_dp*lattice_space(i)
end do
!and then call the build function with the correct transformation matrix
select case(trim(adjustl(element_type)))
case('fcc')
call build_with_rhomb(box_lat_vert, fcc_mat)
case default
print *, "Element type ", trim(adjustl(element_type)), " not accepted in mode create, please specify a supported ", &
"element type"
stop 3
end select
!Now that it is multiply by the lattice parameter
box_bd = box_bd*lattice_parameter
else if(dim_flag) then
continue
!As a note everything is defined so that the lattice parameter is multiplied in at the end
!so we have to divide all the real Angstroms units by the lattice parameter
!Define box_vertices
do i = 1, 8
box_vert(:,i) = (cubic_cell(:,i)*box_len(:) + origin(:))/lattice_parameter
end do
!Now get the rotated box vertex positions in lattice space. Should be integer units
box_lat_vert = int(matmul(fcc_inv, matmul(orient_inv, box_vert)))+1
!Now get the box_bd in lattice units
do i = 1, 3
box_bd(2*i) = (box_len(i)+origin(i))/lattice_parameter
box_bd(2*i-1) = origin(i)/lattice_parameter
end do
else
call cell_init(lattice_parameter, esize, element_type, orient, cell_mat)
@ -107,11 +105,24 @@ module mode_create
box_bd(2*i) = maxval(r_node_temp(i,:,:))
box_bd(2*i-1) = origin(i)
end do
call add_element(element_type, esize, 1, r_node_temp)
call add_element(element_type, esize, 1, 1, r_node_temp)
end if
!If we passed the dup_flag or dim_flag then we have to convert the lattice points and add them to the atom/element arrays
if(dup_flag.or.dim_flag) then
!Call the build function with the correct transformation matrix
select case(trim(adjustl(element_type)))
case('fcc')
call build_with_rhomb(box_lat_vert, fcc_mat)
case default
print *, "Element type ", trim(adjustl(element_type)), " not accepted in mode create, please specify a supported ", &
"element type"
stop 3
end select
!Now that it is built multiply by the lattice parameter
box_bd = box_bd*lattice_parameter
!Allocate variables
call alloc_ele_arrays(lat_ele_num, lat_atom_num*basisnum(1))
if(lat_atom_num > 0) then
@ -130,7 +141,7 @@ module mode_create
r_node_temp(:,ibasis,inod) = (r_lat(:,inod,i)*lattice_parameter)+basis_pos(:,ibasis)
end do
end do
call add_element(element_type, esize, 1, r_node_temp)
call add_element(element_type, esize, 1, 1, r_node_temp)
end do
end if
end if
@ -145,10 +156,10 @@ module mode_create
sub_box_array_bd(2,2,1) = ele_num
end subroutine create
!This subroutine parses the command and pulls out information needed for mode_create
subroutine parse_command()
subroutine parse_command(arg_pos)
integer :: arg_pos, ori_pos, i, j, arglen, stat
integer, intent(out) :: arg_pos
integer :: ori_pos, i, j, arglen, stat
character(len=100) :: textholder
character(len=8) :: orient_string
@ -208,33 +219,32 @@ module mode_create
!If the duplicate command is passed then we extract the information on the new bounds.
case('duplicate')
if(dim_flag) STOP "Both duplicate and dim options cannot be used in mode_create"
dup_flag = .true.
do i = 1, 3
call get_command_argument(arg_pos, textholder)
read(textholder, *) duplicate(i)
arg_pos = arg_pos + 1
end do
case('dim')
if(dup_flag) STOP "Both duplicate and dim options cannot be used in mode_create"
dim_flag = .true.
do i = 1, 3
call get_command_argument(arg_pos, textholder)
read(textholder, *) box_len(i)
arg_pos = arg_pos + 1
end do
case('origin')
do i = 1, 3
call get_command_argument(arg_pos, textholder)
read(textholder, *) origin(i)
arg_pos = arg_pos + 1
end do
!If a filetype is passed then we add name.ext to the outfiles list
case('xyz')
textholder = trim(adjustl(name)) //'.xyz'
call get_out_file(textholder)
case default
!Check to see if it is an option command, if so then mode_create must be finished
if(textholder(1:1) == '-') then
!If it isn't an option then you have to exit
arg_pos = arg_pos -1
exit
!Check to see if a filename was passed
elseif(scan(textholder,'.',.true.) > 0) then
call get_out_file(textholder)
end if
end select
end do
@ -268,8 +278,9 @@ module mode_create
!Now normalize the orientation matrix
orient = matrix_normal(orient,3)
!Set lattice_num to 1
!Set lattice_num to 1 and add the lattice_parameter to the elements module lattice paramter variable
lattice_types = 1
lapa(1) = lattice_parameter
!If we haven't defined a basis then define the basis and add the default basis atom type and position
if (basisnum(1) == 0) then

@ -12,13 +12,14 @@ module mode_merge
public
contains
subroutine merge
subroutine merge(arg_pos)
integer, intent(out) :: arg_pos
integer :: i
real(kind=dp) :: displace(3), temp_box_bd(6)
!First we parse the merge command
call parse_command
call parse_command(arg_pos)
!Now loop over all files and stack them
do i = 1, in_num
@ -44,10 +45,12 @@ module mode_merge
return
end subroutine merge
subroutine parse_command
subroutine parse_command(arg_pos)
integer, intent(out) :: arg_pos
character(len=100) :: textholder
integer :: i, stat, arglen, arg_pos
integer :: i, stat, arglen
!Get dimension to concatenate along
call get_command_argument(2, dim, arglen)
@ -74,27 +77,22 @@ module mode_merge
end do
!Set argpos accordingly
arg_pos = 3+in_num+1
arg_pos = 3+in_num
!Now options loop
!Check for optional keywords
do while(.true.)
if(arg_pos > command_argument_count()) exit
!Pull out the next argument which should either be a keyword or an option
arg_pos=arg_pos+1
call get_command_argument(arg_pos, textholder)
textholder=adjustl(textholder)
arg_pos=arg_pos+1
!Choose what to based on what the option string is
select case(trim(textholder))
case default
!Check to see if it is an option command, if so then mode_create must be finished
if(textholder(1:1) == '-') then
!If it isn't an available option to mode merge then we just exit
exit
!Check to see if a filename was passed
elseif(scan(textholder,'.',.true.) > 0) then
call get_out_file(textholder)
end if
end select
end do

@ -0,0 +1,520 @@
module opt_disl
!This module contains all code associated with dislocations
use parameters
use elements
use subroutines
use box
implicit none
real(kind=dp), dimension(3) :: line, slip_plane, centroid!dislocation line, slip plane vectors, centroid,
real(kind=dp) :: burgers(3) !burgers vector of loop
real(kind=dp) :: poisson, char_angle, lattice_parameter!Poisson ratio and character angle, lattice_parameter for burgers vector
character(len=10) :: lattice
character(len=1) :: loop_normal
real(kind=dp) :: loop_radius !Dislocation loop radius
real(kind=dp) :: b !burgers vector
public
contains
subroutine dislocation(option, arg_pos)
!Main calling function for all codes related to dislocations
character(len=100), intent(in) :: option
integer, intent(inout) :: arg_pos
select case(trim(adjustl(option)))
case('-dislgen')
call parse_dislgen(arg_pos)
call dislgen
case('-disloop')
call parse_disloop(arg_pos)
call disloop
end select
end subroutine dislocation
subroutine parse_dislgen(arg_pos)
!Parse dislgen command
integer, intent(inout) :: arg_pos
integer :: i,arglen
character(len=8) :: ori_string
character(len=100) :: textholder
!Parse all of the commands
arg_pos = arg_pos + 1
line(:) = 0.0_dp
call get_command_argument(arg_pos, ori_string, arglen)
if (arglen==0) STOP "Missing line vector in dislgen command"
call parse_ori_vec(ori_string, line)
arg_pos=arg_pos + 1
slip_plane(:) = 0.0_dp
call get_command_argument(arg_pos, ori_string, arglen)
if (arglen==0) STOP "Missing plane vector in dislgen command"
call parse_ori_vec(ori_string, slip_plane)
do i = 1, 3
arg_pos = arg_pos + 1
call get_command_argument(arg_pos, textholder, arglen)
if (arglen==0) STOP "Missing centroid in dislgen command"
call parse_pos(i, textholder, centroid(i))
end do
print *, centroid
arg_pos = arg_pos + 1
call get_command_argument(arg_pos, textholder, arglen)
if (arglen==0) STOP "Missing character angle in dislgen command"
read(textholder, *) char_angle
arg_pos = arg_pos + 1
call get_command_argument(arg_pos, textholder, arglen)
if (arglen==0) STOP "Missing poisson in dislgen command"
read(textholder, *) poisson
arg_pos = arg_pos + 1
call get_command_argument(arg_pos, lattice, arglen)
if (arglen==0) STOP "Missing lattice in dislgen command"
arg_pos = arg_pos + 1
call get_command_argument(arg_pos, textholder, arglen)
if (arglen==0) STOP "Missing lattice parameter in dislgen command"
read(textholder, *) lattice_parameter
arg_pos = arg_pos + 1
!Now set the vurgers vector based on the lattice paarameter and lattice type
select case(lattice)
case('fcc')
b = lattice_parameter / sqrt(2.0_dp)
! case('bcc')
! b = lattice_parameter * sqrt(3.0_dp) / 2.0_dp
case default
print *, 'Error: Lattice structure', lattice, ' is not accepted for dislgen option'
STOP
end select
end subroutine parse_dislgen
subroutine dislgen
!This subroutine creates the actual dislocation
integer :: i, sub_box, inod, ibasis
real(kind=dp) :: ss_ori(3,3), ss_inv(3,3), be, bs, slipx(3), disp_transform(3,3), inv_transform(3,3), &
actan, r(3), disp(3)
!Calculate screw and edge burgers vectors
be = sin(char_angle*pi/180.0_dp)*b
bs = cos(char_angle*pi/180.0_dp)*b
!Figure out which sub box you are in so you can access it's orientation, this code will not work
!with overlapping sub_boxes
do i = 1, sub_box_num
if(in_block_bd(centroid, sub_box_bd(:,i))) then
sub_box = i
exit
end if
end do
!Construct the slip system orientation matrix in an unrotated system
slipx = cross_product(slip_plane, line)
ss_ori(1,:) = unitvec(3,slipx)
ss_ori(2,:) = unitvec(3,slip_plane)
ss_ori(3,:) = unitvec(3,line)
call matrix_inverse(ss_ori, 3, ss_inv)
!Apply the rotation
disp_transform = matmul(sub_box_ori(:,:,i), ss_inv)
call matrix_inverse(disp_transform,3,inv_transform)
if(atom_num > 0) then
do i = 1, atom_num
r=r_atom(:,i) - centroid
r=matmul(inv_transform, r)
if (r(1) == 0) then
actan=pi/2
else
actan = datan2(r(2),r(1))
end if
if ((r(1)**2 + r(2)**2) == 0) cycle
!This is the elastic displacement field for dislocation according to Hirth and Lowe
disp(1) = be/(2.0_dp*pi) * (actan + (r(1)*r(2))/(2.0_dp*(1.0_dp-poisson)*(r(1)**2.0_dp + r(2)**2.0_dp)))
disp(2) = -be/(2.0_dp*pi)*((1.0_dp-2.0_dp*poisson)/(4.0_dp-4.0_dp*poisson) * &
log(r(1)**2.0_dp + r(2)**2.0_dp) &
+ (r(1)**2.0_dp - r(2)**2.0_dp)/(4.0_dp*(1.0_dp-poisson)&
*(r(1)**2.0_dp+r(2)**2.0_dp)))
disp(3) = bs/(2.0_dp*pi) * actan
!This transforms the displacement to the correct orientation
disp = matmul(disp_transform, disp)
r_atom(:,i) = r_atom(:,i) + disp
end do
end if
if(ele_num > 0) then
do i = 1, ele_num
do inod=1, ng_node(lat_ele(i))
do ibasis = 1, basisnum(lat_ele(i))
r = r_node(:,ibasis,inod,i)
r = matmul(inv_transform, r)
if (r(1) == 0) then
actan = pi/2
else
actan = datan2(r(2),r(1))
end if
if ((r(1)**2 + r(2)**2) == 0) cycle
!This is the elastic displacement field for dislocation according to Hirth and Lowe
disp(1) = be/(2.0_dp*pi) * (actan + (r(1)*r(2))/(2.0_dp*(1.0_dp-poisson)*(r(1)**2.0_dp + r(2)**2.0_dp)))
disp(2) = -be/(2.0_dp*pi)*((1.0_dp-2.0_dp*poisson)/(4.0_dp-4.0_dp*poisson) * &
log(r(1)**2.0_dp + r(2)**2.0_dp) &
+ (r(1)**2.0_dp - r(2)**2.0_dp)/(4.0_dp*(1.0_dp-poisson)&
*(r(1)**2.0_dp+r(2)**2.0_dp)))
disp(3) = bs/(2.0_dp*pi) * actan
disp = matmul(disp_transform, disp)
r_node(:,ibasis,inod,i) = r_node(:,ibasis,inod,i) + disp
end do
end do
end do
end if
end subroutine dislgen
subroutine parse_disloop(arg_pos)
!This subroutine parses the disloop command
integer, intent(inout) :: arg_pos
integer :: i,arglen, sbox
character(len=8) :: ori_string
character(len=100) :: textholder
!Parse all of the commands
arg_pos = arg_pos + 1
loop_normal = ' '
call get_command_argument(arg_pos, loop_normal, arglen)
if (arglen==0) STOP "Missing loop_normal in disloop command"
!Convert the loop_normal to the dimension
select case(loop_normal)
case('x','X', 'y', 'Y', 'z', 'Z')
continue
case default
print *, "Dimension argument must either be x, y, or z not", loop_normal
stop 3
end select
arg_pos = arg_pos + 1
loop_radius = 0
call get_command_argument(arg_pos, textholder, arglen)
if (arglen==0) STOP "Missing loop_size in disloop command"
read(textholder, *) loop_radius
do i = 1, 3
arg_pos = arg_pos + 1
call get_command_argument(arg_pos, textholder, arglen)
if (arglen==0) STOP "Missing centroid in disloop command"
call parse_pos(i, textholder, centroid(i))
end do
burgers(:) = 0.0_dp
do i = 1, 3
arg_pos = arg_pos + 1
call get_command_argument(arg_pos, textholder, arglen)
if (arglen==0) STOP "Missing burgers vector in disloop command"
read(textholder, *) burgers(i)
end do
arg_pos = arg_pos + 1
call get_command_argument(arg_pos, textholder, arglen)
if (arglen==0) STOP "Missing poisson ratio in disloop command"
read(textholder, *) poisson
arg_pos = arg_pos + 1
!Now check to make sure that the dimension selected is actually a 1 1 1 direction.
! call in_sub_box(centroid, sbox)
! if(.not.((abs(sub_box_ori(loop_normal,1,sbox)) == abs(sub_box_ori(loop_normal,2,sbox))).and. &
! (abs(sub_box_ori(loop_normal,2,sbox)) == abs(sub_box_ori(loop_normal,3,sbox))).and. &
! (abs(sub_box_ori(loop_normal,3,sbox)) == abs(sub_box_ori(loop_normal,1,sbox))))) then
! print *, "The selected dimension ", loop_normal, " for sub_box ", sbox, " is ", &
! sub_box_ori(loop_normal,:,sbox), " which is not in the (111) family of planes"
! STOP 3
! end if
end subroutine
!Code for the creation of dislocation loops is based on functions from atomsk
!which is available from https://atomsk.univ-lille.fr/. They have been adapted to fit cacmb.
subroutine disloop
!This subroutine applies the displacement field for a dislocation loop to all atoms and elements.
integer :: a1, a2, a3 !New directions
integer :: i, j, inod, ibasis, Npoints
real(kind = dp) :: perimeter, angle, theta, omega, xA(3), xB(3), xC(3), u(3)
real(kind=dp), dimension(:,:), allocatable :: xloop !coordinate of points forming loop
if(allocated(xLoop)) deallocate(xLoop)
!Define new directions
select case(loop_normal)
case('x','X')
!Loop in (y,z) plane
a1 = 2
a2 = 3
a3 = 1
case('y','Y')
!Loop in (x,z) plane
a1 = 3
a2 = 1
a3 = 2
case default
!Loop in (x,y) plane
a1 = 1
a2 = 2
a3 = 3
end select
!Calculate loop perimeter
perimeter = 2.0_dp*pi*loop_radius
!Define the number of points forming the loop
! The following criteria are used as a trade-off between
! good accuracy and computational efficiency:
! - each dislocation segment should have a length of 5 angströms;
! - the loop should contain at least 3 points
! (for very small loops, this will result in segments shorter than 5 A);
! - there should not be more than 100 points
! (for very large loops, this will result in segments longer than 5 A).
Npoints = MAX( 3 , MIN( NINT(perimeter/5.d0) , 100 ) )
!angle between two consecutive points
theta = 2.0_dp*pi / dble(Npoints)
!allocate xLoop
allocate(xLoop(Npoints,3))
xLoop(:,:) = 0.0_dp
!Calculate the position of each point in the loop
angle = 0.0_dp
do i = 1, size(xLoop,1)
xLoop(i,a1) = centroid(a1) + loop_radius*dcos(angle)
xLoop(i,a2) = centroid(a2) + loop_radius*dsin(angle)
xLoop(i,a3) = centroid(a3)
! Increment angle for next point
angle = angle + theta
end do
!Now actually calculate the displacement created by a loop for every atom
do i = 1, atom_num
u = 0.0_dp
omega = 0.0_dp
xC(:) = centroid - r_atom(:,i)
!Loop over all dislocation segments
do j = 1, size(xLoop,1)
!Coordinates of point A
if (j ==1) then
xA(:) = xLoop(size(xLoop,1),:) - r_atom(:,i)
else
xA(:) = xLoop(j-1, :) - r_atom(:,i)
end if
!Coordinates of point B
xB(:) = xLoop(j,:) - r_atom(:,i)
!Displacement due to solid angle
omega = omega + SolidAngle(xA, xB, xC)
!Displacement due to elasticity
u(:) = u(:) + DisloSeg_displacement_iso(xA, xB, burgers(:), poisson)
end do
!Total displacement
u=u(:) + burgers(:) * omega
!Apply displacement to atom
r_atom(:,i) = r_atom(:,i) + u(:)
end do
!Repeat for element nodes
do i = 1, ele_num
do inod =1, ng_node(lat_ele(i))
do ibasis = 1, basisnum(lat_ele(i))
u = 0.0_dp
omega = 0.0_dp
xC(:) = centroid - r_node(:,ibasis,inod,i)
!Loop over all dislocation segments
do j = 1, size(xLoop,1)
!Coordinates of point A
if (j ==1) then
xA(:) = xLoop(size(xLoop,1),:) - r_node(:,ibasis,inod,i)
else
xA(:) = xLoop(j-1, :) - r_node(:,ibasis,inod,i)
end if
!Coordinates of point B
xB(:) = xLoop(j,:) - r_node(:,ibasis,inod,i)
!Displacement due to solid angle
omega = omega + SolidAngle(xA, xB, xC)
!Displacement due to elasticity
u(:) = u(:) + DisloSeg_displacement_iso(xA, xB, burgers(:), poisson)
end do
!Total displacement
u=u(:) + burgers(:) * omega
!Apply displacement to atom
r_node(:,ibasis,inod,i) = r_node(:,ibasis,inod,i) + u(:)
end do
end do
end do
return
end subroutine
!********************************************************
! SOLIDANGLE
! Calculate solid angle (normalized by 4*pi) used to obtain the
! displacement created by a dislocation triangle loop ABC at the origin
! (field point = origin)
! Ref.: Van Oosterom, A. and Strackee, J.,
! The Solid Angle of a Plane Triangle,
! IEEE Transactions on Biomedical Engineering BME-30, 125 (1983).
!********************************************************
FUNCTION SolidAngle(xA, xB, xC) RESULT(Omega)
!
IMPLICIT NONE
!
! Extremities of the triangle loop
REAL(dp),DIMENSION(3),INTENT(IN) :: xA, xB, xC
!
! Solid angle (normalized by 4*pi)
REAL(dp):: omega
REAL(dp),PARAMETER:: factor=1.d0/(2.d0*pi)
!
REAL(dp) :: rA, rB, rC, numerator, denominator
!
rA = norm2(xA)
rB = norm2(xB)
rC = norm2(xC)
!
numerator = TRIPLE_PRODUCT( xA, xB, xC )
denominator = rA*rB*rC + DOT_PRODUCT( xA, xB )*rC &
& + DOT_PRODUCT( xB, xC )*rA + DOT_PRODUCT( xC, xA )*rB
!
omega = factor*ATAN2( numerator, denominator )
!
END FUNCTION SolidAngle
!********************************************************
! DISLOSEG_DISPLACEMENT_ISO
! Calculate displacement created by dislocation segment AB
! once the solid angle part has been removed
! Isotropic elastic calculation with nu Poisson coef.
! Ref.: Eq. (1) in Barnett, D. M.
! The Displacement Field of a Triangular Dislocation Loop
! Philos. Mag. A, 1985, 51, 383-387
!********************************************************
FUNCTION DisloSeg_displacement_iso(xA, xB, b, nu) RESULT(u)
!
IMPLICIT NONE
REAL(dp),DIMENSION(3),INTENT(IN):: xA, xB ! Extremities of the segment
REAL(dp),DIMENSION(3),INTENT(IN):: b ! Burgers vector
REAL(dp),INTENT(IN):: nu ! Poisson coefficient
REAL(dp),DIMENSION(3):: u ! Displacement
REAL(dp):: rA, rB
REAL(dp),DIMENSION(1:3):: tAB, nAB
!
rA = norm2(xA)
rB = norm2(xB)
!
! Tangent vector
tAB(:) = xB(:) - xA(:)
tAB(:) = tAB(:)/norm2(tAB)
!
! Normal vector
nAB(:) = CROSS_PRODUCT(xA,xB)
nAB(:) = nAB(:)/norm2(nAB)
!
u(:) = ( -(1.d0-2.d0*nu)*CROSS_PRODUCT(b, tAB)* &
& LOG( (rB + DOT_PRODUCT(xB,tAB))/(rA + DOT_PRODUCT(xA,tAB)) ) &
& + DOT_PRODUCT(b,nAB)*CROSS_PRODUCT(xB/rB-xA/rA,nAB) ) &
& /(8.d0*pi*(1.d0-nu))
!
END FUNCTION DisloSeg_displacement_iso
!
!This code simply creates a planar vacancy cluster and does not apply the dislocation loop displacement field.
! subroutine disloop
! !This subroutine actually creates the dislocation loop.
! real(kind=dp) :: neighbor_dis(loop_size), temp_box(6), dis
! integer :: i, j, index(loop_size)
! neighbor_dis(:) = HUGE(1.0_dp)
! index(:) = 0
! !First find the nearest atom to the centroid
! do i = 1, atom_num
! if(norm2(r_atom(:,i) - centroid) < neighbor_dis(1)) then
! neighbor_dis(1) = norm2(r_atom(:,i) - centroid)
! index(1) = i
! end if
! end do
! !Define a new box, this box tries to isolate all atoms on the plane of the atom
! !closest to the user defined centroid.
! temp_box(:) = box_bd(:)
! temp_box(2*loop_normal) = r_atom(loop_normal,index(1)) + 10.0_dp**(-2.0_dp)
! temp_box(2*loop_normal-1) = r_atom(loop_normal,index(1)) - 10.0_dp**(-2.0_dp)
! !Now reset the list for the scanning algorithm
! index(1) = 0
! neighbor_dis(1) = HUGE(1.0_dp)
! !Now scan over all atoms again and find the closest loop_size number of atoms to the initial atom
! !that reside on the same plane.
! do i = 1, atom_num
! !Check to see if it is on the same plane
! if (in_block_bd(r_atom(:,i), temp_box)) then
! dis = norm2(r_atom(:,i) - centroid)
! do j = 1, loop_size
! !Check to see if it is closer than other atoms
! if (dis < neighbor_dis(j)) then
! !Move values in the neighbor array and add the new neighbor
! if(j < loop_size) then
! neighbor_dis(j+1:loop_size) = neighbor_dis(j:loop_size-1)
! index(j+1:loop_size) = index(j:loop_size-1)
! end if
! neighbor_dis(j) = dis
! index(j) = i
! exit
! end if
! end do
! end if
! end do
! !Now delete the atoms
! call delete_atoms(loop_size, index)
! return
! end subroutine disloop
end module opt_disl

@ -2,7 +2,14 @@ module parameters
implicit none
!Default precision
integer, parameter :: dp= selected_real_kind(15,307)
!Parameters for floating point tolerance
real(kind=dp), parameter :: lim_zero = epsilon(1.0_dp), &
lim_large = huge(1.0_dp)
logical, save :: lmpcac
!Numeric constants
real(kind=dp), parameter :: pi = 3.14159265358979323846_dp
end module parameters

@ -1,6 +1,7 @@
module subroutines
use parameters
use functions
use box
implicit none
integer :: allostat, deallostat
@ -145,4 +146,107 @@ module subroutines
return
end subroutine matrix_inverse
subroutine parse_ori_vec(ori_string, ori_vec)
!This subroutine parses a string to vector in the format [ijk]
character(len=8), intent(in) :: ori_string
real(kind=dp), dimension(3), intent(out) :: ori_vec
integer :: i, ori_pos, stat
ori_pos=2
do i = 1,3
if (ori_string(ori_pos:ori_pos) == '-') then
ori_pos = ori_pos + 1
read(ori_string(ori_pos:ori_pos), *, iostat=stat) ori_vec(i)
if (stat>0) STOP "Error reading orientation value"
ori_vec(i) = -ori_vec(i)
ori_pos = ori_pos + 1
else
read(ori_string(ori_pos:ori_pos), *, iostat=stat) ori_vec(i)
if(stat>0) STOP "Error reading orientation value"
ori_pos=ori_pos + 1
end if
end do
return
end subroutine parse_ori_vec
subroutine parse_pos(i, pos_string, pos)
!This subroutine parses the pos command allowing for command which include inf
integer, intent(in) :: i !The dimension of the position
character(len=100), intent(in) :: pos_string !The position string
real(kind=dp), intent(out) :: pos !The output parsed position value
integer :: iospara
if(trim(adjustl(pos_string)) == 'inf') then
pos=box_bd(2*i)
else if(trim(adjustl(pos_string)) == '-inf') then
pos=box_bd(2*i-1)
else if ((index(pos_string,'-') > 0).and.(index(pos_string,'inf')>0)) then
!Now extract the number we are reducing from infinity
read(pos_string(index(pos_string,'-')+1:), *, iostat=iospara) pos
pos = box_bd(2*i) - pos
else if ((index(pos_string,'+') > 0).and.(index(pos_string,'inf')>0)) then
!Now extract the number we are reducing from infinity
read(pos_string(index(pos_string,'+')+1:), *, iostat=iospara) pos
pos = box_bd(2*i-1) + pos
else if ((index(pos_string,'*') > 0).and.(index(pos_string,'inf')>0)) then
!Now extract the number we are reducing from infinity
read(pos_string(index(pos_string,'*')+1:), *, iostat=iospara) pos
pos = (box_bd(2*i)-box_bd(2*i-1))*pos
else
read(pos_string, *, iostat=iospara) pos
end if
if (iospara > 0) then
print *, "Error reading position argument ", trim(adjustl(pos_string)), ". Please reformat and try again."
end if
end subroutine parse_pos
subroutine heapsort(a)
integer, intent(in out) :: a(0:)
integer :: start, n, bottom
real :: temp
n = size(a)
do start = (n - 2) / 2, 0, -1
call siftdown(a, start, n);
end do
do bottom = n - 1, 1, -1
temp = a(0)
a(0) = a(bottom)
a(bottom) = temp;
call siftdown(a, 0, bottom)
end do
end subroutine heapsort
subroutine siftdown(a, start, bottom)
integer, intent(in out) :: a(0:)
integer, intent(in) :: start, bottom
integer :: child, root
real :: temp
root = start
do while(root*2 + 1 < bottom)
child = root * 2 + 1
if (child + 1 < bottom) then
if (a(child) < a(child+1)) child = child + 1
end if
if (a(root) < a(child)) then
temp = a(child)
a(child) = a (root)
a(root) = temp
root = child
else
return
end if
end do
end subroutine siftdown
end module subroutines
Loading…
Cancel
Save