Skip to content

Commit 15ecc63

Browse files
committed
Add extended basis type
1 parent 8ee57ae commit 15ecc63

File tree

7 files changed

+246
-17
lines changed

7 files changed

+246
-17
lines changed

CMakeLists.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,12 @@ set(FORTRAN_SRC_DIR ${SRC_DIR}/fortran)
7272
set(LIB_DIR ${FORTRAN_SRC_DIR}/lib)
7373

7474
set(LIB_FILES
75-
mod_constants.f90
76-
mod_misc.f90
75+
mod_constants.f90
76+
mod_misc.f90
7777
mod_misc_maths.f90
7878
mod_misc_linalg.f90
7979
mod_edit_geom.f90
80+
mod_extended_geom.f90
8081
mod_elements.f90
8182
mod_evaluator.f90
8283
mod_atom_adder.f90

src/fortran/lib/mod_atom_adder.f90

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module add_atom
99
use constants, only: real12
1010
use misc_linalg, only: modu
1111
use rw_geom, only: basis_type
12+
use extended_geom, only: extended_basis_type
1213
use edit_geom, only: get_min_dist, get_min_dist_between_point_and_atom
1314
use evaluator, only: evaluate_point
1415
use evolver, only: gvector_container_type
@@ -40,7 +41,7 @@ function add_atom_min(gridpoints, gvector_container, &
4041
! Arguments
4142
type(gvector_container_type), intent(in) :: gvector_container
4243
!! Distribution function (gvector) container.
43-
type(basis_type), intent(inout) :: basis
44+
type(extended_basis_type), intent(inout) :: basis
4445
!! Structure to add atom to.
4546
logical, intent(out) :: viable
4647
!! Boolean to indicate if point is viable.
@@ -98,7 +99,7 @@ function add_atom_void(bin_size, basis, atom_ignore_list, viable) &
9899
implicit none
99100

100101
! Arguments
101-
type(basis_type), intent(inout) :: basis
102+
type(extended_basis_type), intent(inout) :: basis
102103
!! Structure to add atom to.
103104
integer, dimension(3), intent(in) :: bin_size
104105
!! Number of gridpoints in each direction.
@@ -166,7 +167,7 @@ function add_atom_walk ( gvector_container, &
166167
! Arguments
167168
type(gvector_container_type), intent(in) :: gvector_container
168169
!! Distribution function (gvector) container.
169-
type(basis_type), intent(inout) :: basis
170+
type(extended_basis_type), intent(inout) :: basis
170171
!! Structure to add atom to.
171172
logical, intent(out) :: viable
172173
!! Boolean to indicate if point is viable.
@@ -293,7 +294,7 @@ function get_viable_gridpoints(bin_size, basis, &
293294
implicit none
294295

295296
! Arguments
296-
type(basis_type), intent(in) :: basis
297+
type(extended_basis_type), intent(in) :: basis
297298
!! Structure to add atom to.
298299
integer, dimension(3), intent(in) :: bin_size
299300
!! Number of gridpoints in each direction.
@@ -365,7 +366,7 @@ subroutine update_viable_gridpoints(points, basis, atom, radius)
365366
implicit none
366367

367368
! Arguments
368-
type(basis_type), intent(in) :: basis
369+
type(extended_basis_type), intent(in) :: basis
369370
!! Structure to add atom to.
370371
integer, dimension(2) :: atom
371372
!! Index of atom to add.

src/fortran/lib/mod_edit_geom.f90

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ function get_min_dist(bas,loc,lignore_close,axis,labove,lreal,tol, &
3131
! Arguments
3232
logical, intent(in) :: lignore_close
3333
!! If true, ignore atoms that are really close to the point.
34-
type(basis_type), intent(in) :: bas
34+
class(basis_type), intent(in) :: bas
3535
!! The basis of the cell.
3636
real(real12), dimension(3), intent(in) :: loc
3737
!! The location of the point (in crystal coordinates).
@@ -123,7 +123,7 @@ pure function get_min_dist_between_point_and_atom(bas,loc,atom) &
123123
implicit none
124124

125125
! Arguments
126-
type(basis_type), intent(in) :: bas
126+
class(basis_type), intent(in) :: bas
127127
!! The basis of the cell.
128128
integer, dimension(2), intent(in) :: atom
129129
!! The index of the atom in the cell (species, atom).
@@ -155,7 +155,7 @@ function bas_merge(bas1,bas2,length,map1,map2) result(mergbas)
155155
! Arguments
156156
type(basis_type) :: mergbas
157157
!! Output merged basis.
158-
type(basis_type), intent(in) :: bas1, bas2
158+
class(basis_type), intent(in) :: bas1, bas2
159159
!! Input bases to merge.
160160
integer, intent(in), optional :: length
161161
!! Number of dimensions for atomic positions (default 3).

src/fortran/lib/mod_evaluator.f90

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ module evaluator
88
use constants, only: real12
99
use misc_linalg, only: get_distance, get_angle, get_dihedral_angle
1010
use rw_geom, only: basis_type
11+
use extended_geom, only: extended_basis_type
1112
use edit_geom, only: get_min_dist_between_point_and_atom
1213
use evolver, only: gvector_container_type
1314
implicit none
@@ -32,7 +33,7 @@ pure function evaluate_point(gvector_container, &
3233
!! Distribution function (gvector) container.
3334
real(real12), intent(in) :: uptol, lowtol
3435
!! Upper and lower tolerance for bond lengths and angles.
35-
type(basis_type), intent(in) :: basis
36+
type(extended_basis_type), intent(in) :: basis
3637
!! Basis of the system.
3738
real(real12), dimension(3), intent(in) :: position
3839
!! Position of the test point.
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
module extended_geom
2+
use constants, only: real12
3+
use misc_linalg, only: modu, LUinv, cross
4+
use rw_geom, only: basis_type, species_type
5+
implicit none
6+
7+
8+
private
9+
10+
public :: extended_basis_type
11+
12+
13+
type, extends(basis_type) :: extended_basis_type
14+
real(real12) :: max_extension
15+
integer :: nspec_images
16+
integer :: num_images
17+
type(species_type), dimension(:), allocatable :: image_species
18+
contains
19+
procedure, pass(this) :: create_images
20+
end type extended_basis_type
21+
22+
contains
23+
24+
subroutine create_images(this, max_bondlength)
25+
class(extended_basis_type), intent(inout) :: this
26+
real(real12), intent(in) :: max_bondlength
27+
28+
type(species_type), dimension(this%nspec) :: image_species
29+
30+
integer :: is, ia, js, i, j, k
31+
integer :: amax, bmax, cmax
32+
real(real12), dimension(3) :: vtmp1
33+
34+
35+
!---------------------------------------------------------------------------
36+
! get the maximum number of lattice vectors to consider
37+
! NOTE: this is not perfect
38+
! won't work for extremely acute/obtuse angle cells
39+
! (due to diagonal path being shorter than individual lattice vectors)
40+
!---------------------------------------------------------------------------
41+
amax = ceiling(max_bondlength/modu(this%lat(1,:)))
42+
bmax = ceiling(max_bondlength/modu(this%lat(2,:)))
43+
cmax = ceiling(max_bondlength/modu(this%lat(3,:)))
44+
45+
46+
!!! WARNING, NEED IGNORE LIST IN HERE TO ONLY APPLY TO ATOMS WE WANT TO EXTEND !!!
47+
!!! needs and update_images subroutine !!!
48+
spec_loop1: do is=1,this%nspec
49+
allocate( image_species(is)%atom( &
50+
this%spec(is)%num*(2*amax+1)*(2*bmax+1)*(2*cmax+1), &
51+
size(this%spec(is)%atom,2) ) &
52+
)
53+
image_species(is)%num = 0
54+
image_species(is)%mass = this%spec(is)%mass
55+
image_species(is)%charge = this%spec(is)%charge
56+
image_species(is)%radius = this%spec(is)%radius
57+
image_species(is)%name = this%spec(is)%name
58+
atom_loop1: do ia=1,this%spec(is)%num
59+
do i=-amax,amax+1,1
60+
vtmp1(1) = this%spec(is)%atom(ia,1) + real(i, real12)
61+
do j=-bmax,bmax+1,1
62+
vtmp1(2) = this%spec(is)%atom(ia,2) + real(j, real12)
63+
do k=-cmax,cmax+1,1
64+
vtmp1(3) = this%spec(is)%atom(ia,3) + real(k, real12)
65+
if( get_distance_from_unit_cell(vtmp1, this%lat) .le. max_bondlength ) then
66+
! add the image to the list
67+
image_species(is)%num = this%spec(is)%num + 1
68+
this%image_species(is)%atom(image_species(is)%num,:3) = vtmp1
69+
end if
70+
end do
71+
end do
72+
end do
73+
end do atom_loop1
74+
end do spec_loop1
75+
76+
77+
this%nspec_images = count(image_species%num.gt.0)
78+
allocate(this%image_species(this%nspec_images))
79+
js = 0
80+
do is = 1, this%nspec
81+
js = js + 1
82+
this%image_species(js)%num = image_species(is)%num
83+
this%image_species(js)%mass = image_species(is)%mass
84+
this%image_species(js)%charge = image_species(is)%charge
85+
this%image_species(js)%radius = image_species(is)%radius
86+
this%image_species(js)%name = image_species(is)%name
87+
allocate(this%image_species(js)%atom(image_species(is)%num,size(image_species(is)%atom,2)))
88+
this%image_species(js)%atom(:,:) = image_species(is)%atom(:this%nspec_images,:)
89+
end do
90+
91+
end subroutine create_images
92+
93+
94+
95+
96+
97+
function get_distance_from_unit_cell(point, lattice, closest_point, is_cartesian) result(distance)
98+
implicit none
99+
! Input
100+
real(real12), intent(in) :: point(3) ! Point in 3D space
101+
real(real12), intent(in) :: lattice(3,3) ! 3x3 array representing lattice vectors as columns
102+
! Output
103+
real(real12), intent(out), optional :: closest_point(3) ! Closest point on the unit cell surface
104+
logical, optional, intent(in) :: is_cartesian ! Flag indicating whether the point is in Cartesian coordinates
105+
real(real12) :: distance ! Shortest distance to the unit cell surface
106+
! Local variables
107+
real(real12), dimension(3) :: point_
108+
real(real12), dimension(3,3) :: inverse_lattice
109+
real(real12), dimension(3) :: normal
110+
real(real12), dimension(3) :: plane_point
111+
real(real12), dimension(3) :: projection, closest_point_
112+
real(real12), dimension(3) :: inverse_projection
113+
real(real12) :: min_distance
114+
logical :: is_outside = .false.
115+
integer :: i, j, k
116+
integer, dimension(3) :: index_list = [1, 2, 3]
117+
logical :: is_cartesian_ = .false.
118+
119+
120+
121+
if(present(is_cartesian)) is_cartesian_ = is_cartesian
122+
123+
inverse_lattice = LUinv(lattice)
124+
if(is_cartesian_) then
125+
! Convert point to fractional coordinates
126+
! point_ = matmul(LUinv(lattice), point)
127+
point_ = point
128+
else
129+
point_ = matmul(lattice, point)
130+
end if
131+
132+
min_distance = huge(1._real12)
133+
134+
! get projection of point onto each face of the lattice
135+
! get the length of the projection vector
136+
! if negative, then the point is inside the unit cell
137+
! if positive, then the point is outside the unit cell
138+
! if the projection falls outside of the cell edges, use edge or corner distances
139+
140+
do i = 1, 3
141+
index_list = cshift(index_list, 1)
142+
plane_point = 0._real12
143+
do j = 1, 2
144+
normal = (-1._real12)**j * cross(lattice(index_list(2),:), lattice(index_list(3),:))
145+
projection = project_point_onto_plane(point_, plane_point, normal)
146+
147+
! check if point minus projection is negative
148+
! if so, it is on the wrong side of the plane and should be ignored
149+
if( dot_product(point_ - projection, normal) .lt. 0._real12 ) cycle
150+
is_outside = .true.
151+
152+
! check if projection is outside the surface
153+
154+
inverse_projection = matmul(inverse_lattice, projection)
155+
if( any( inverse_projection .lt. 0._real12 ) .or. &
156+
any( inverse_projection .gt. 1._real12 ) ) then
157+
! projection is outside the surface
158+
! check if the projection is outside the edges
159+
! if it is, then the closest point is the edge or corner
160+
! if it is not, then the closest point is the projection
161+
do k = 1, 3
162+
if( inverse_projection(k) .lt. 0._real12 ) then
163+
inverse_projection(k) = 0._real12
164+
else if( inverse_projection(k) .gt. 1._real12 ) then
165+
inverse_projection(k) = 1._real12
166+
end if
167+
end do
168+
projection = matmul(lattice, inverse_projection)
169+
end if
170+
distance = norm2(point_ - projection)
171+
if( distance .lt. min_distance ) then
172+
min_distance = distance
173+
closest_point_ = projection
174+
end if
175+
176+
!! makes it apply to the next iteration
177+
plane_point = plane_point + lattice(index_list(1),:)
178+
end do
179+
end do
180+
181+
if( is_outside ) then
182+
distance = min_distance
183+
else
184+
distance = 0._real12
185+
end if
186+
187+
if( present(closest_point) ) then
188+
if(is_cartesian_) then
189+
closest_point = closest_point_
190+
else
191+
closest_point = matmul(inverse_lattice, closest_point_)
192+
end if
193+
end if
194+
195+
end function get_distance_from_unit_cell
196+
197+
198+
199+
200+
function project_point_onto_plane(point, plane_point, normal) result(output)
201+
implicit none
202+
real(real12), dimension(3), intent(in) :: point
203+
real(real12), dimension(3), intent(in) :: plane_point
204+
real(real12), dimension(3), intent(in) :: normal
205+
real(real12), dimension(3) :: output
206+
207+
real(real12) :: distance
208+
real(real12), dimension(3) :: vector_to_plane
209+
210+
vector_to_plane = point - plane_point
211+
212+
distance = dot_product(vector_to_plane, normal) / dot_product(normal, normal)
213+
214+
output = point - distance * normal
215+
216+
end function project_point_onto_plane
217+
218+
219+
end module extended_geom

src/fortran/lib/mod_generator.f90

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ module generator
88
use constants, only: real12
99
use misc_raffle, only: strip_null
1010
use rw_geom, only: basis_type
11+
use extended_geom, only: extended_basis_type
1112
use evolver, only: gvector_container_type
1213

1314
use constants, only: verbose_global => verbose
@@ -315,8 +316,12 @@ subroutine generate(this, num_structures, &
315316
structure_loop: do istructure = num_structures_old + 1, num_structures_new
316317

317318
if(verbose_.gt.0) write(*,*) "Generating structure", istructure
318-
this%structures(istructure) = this%generate_structure( basis_template, &
319-
placement_list, method_probab_, verbose_ )
319+
! this%structures(istructure) = this%generate_structure( basis_template, &
320+
! placement_list, method_probab_, verbose_ )
321+
call this%structures(istructure)%copy( basis = &
322+
this%generate_structure( basis_template, &
323+
placement_list, method_probab_, verbose_ ) &
324+
)
320325
this%num_structures = istructure
321326

322327
#ifdef ENABLE_ATHENA
@@ -351,7 +356,7 @@ module function generate_structure( &
351356
!! List of possible placements.
352357
real(real12), dimension(3) :: method_probab
353358
!! Probability of each placement method.
354-
type(basis_type) :: basis
359+
type(extended_basis_type) :: basis
355360
!! Generated basis.
356361
integer, intent(in) :: verbose
357362
!! Verbosity level.
@@ -382,6 +387,7 @@ module function generate_structure( &
382387
! initialise the basis
383388
!---------------------------------------------------------------------------
384389
call basis%copy(basis_initial)
390+
call basis%create_images(max_bondlength=this%distributions%cutoff_max(1))
385391
num_insert_atoms = basis%natom - this%host%natom
386392

387393

@@ -478,6 +484,7 @@ module function generate_structure( &
478484
iplaced = iplaced + 1
479485
basis%spec(placement_list_shuffled(iplaced,1))%atom( &
480486
placement_list_shuffled(iplaced,2),:3) = point(:3)
487+
! basis%update_images(placement_list_shuffled(iplaced,1))
481488
if(verbose.gt.0)then
482489
write(*,'(A)',ADVANCE='NO') achar(13)
483490
write(*,*) "placed", viable

0 commit comments

Comments
 (0)