MPI-SCATCI  2.0
An MPI version of SCATCI
Symbolic_Module.f90
Go to the documentation of this file.
1 ! Copyright 2019
2 !
3 ! For a comprehensive list of the developers that contributed to these codes
4 ! see the UK-AMOR website.
5 !
6 ! This file is part of UKRmol-in (UKRmol+ suite).
7 !
8 ! UKRmol-in is free software: you can redistribute it and/or modify
9 ! it under the terms of the GNU General Public License as published by
10 ! the Free Software Foundation, either version 3 of the License, or
11 ! (at your option) any later version.
12 !
13 ! UKRmol-in is distributed in the hope that it will be useful,
14 ! but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ! GNU General Public License for more details.
17 !
18 ! You should have received a copy of the GNU General Public License
19 ! along with UKRmol-in (in source/COPYING). Alternatively, you can also visit
20 ! <https://www.gnu.org/licenses/>.
21 
34 
35  use const_gbl, only: stdout
37  use containers, only: bstree
38  use integer_packing, only: pack8ints, unpack8ints
39  use iso_c_binding, only: c_loc, c_ptr, c_f_pointer
40  use mpi_gbl, only: mpi_reduceall_max, mpi_mod_rotate_arrays_around_ring, mpi_xermsg
41  use precisn, only: longint, wp
43  use parallelization_module, only: grid => process_grid
44 
45  implicit none
46 
48 
49  private
50 
57  type, extends(bstree) :: symbolicelementvector
58  private
59  integer(longint), allocatable :: electron_integral(:,:)
60  real(wp), allocatable :: coeffs(:)
61  integer :: n = 0
62  logical :: constructed = .false.
63  real(wp) :: threshold = 0.0
64  integer :: max_capacity = 0
65  integer :: expand_size = 10
66  contains
67  ! type-bound procedures used by the bstree interface
68  procedure, public :: compare => bstree_compare
69 
70  ! own type-bound procedures
71  procedure, public :: construct
72  procedure, public :: insert_ijklm_symbol
73  procedure, public :: insert_symbol
74  procedure, public :: remove_symbol_at
75  procedure, public :: is_empty
76  procedure, public :: clear
77  procedure, public :: check_same_integral
78  procedure, public :: get_integral_label
79  procedure, public :: synchronize_symbols
80  procedure, public :: modify_coeff
81  procedure, public :: get_coefficient
82  procedure, public :: get_coeff_and_integral
83  procedure, public :: get_size
84  procedure, public :: add_symbols
85  procedure, public :: print => print_symbols
86  procedure, private :: expand_array
87  procedure, private :: check_bounds
88  procedure, public :: destroy
89  procedure, private :: check_constructed
90  end type symbolicelementvector
91 
92 contains
93 
111  integer function bstree_compare (this, i, j, data) result (verdict)
112 
113  class(symbolicelementvector), intent(in) :: this
114  integer, intent(in) :: i, j
115  type(c_ptr), optional, intent(in) :: data
116 
117  integer(longint) :: ii(2), jj(2)
118  integer(longint), pointer :: kk(:)
119 
120  ! get the default index set to compare with (if provided)
121  if (present(data)) call c_f_pointer(data, kk, (/ 2 /))
122 
123  ! obtain the index sets to compare
124  if (i <= 0) then; ii = kk; else; ii = this % electron_integral(:,i); end if
125  if (j <= 0) then; jj = kk; else; jj = this % electron_integral(:,j); end if
126 
127  ! compare the index sets using lexicographical comparison
128  if (ii(1) < jj(1)) then
129  verdict = -1
130  else if (ii(1) > jj(1)) then
131  verdict = +1
132  else if (ii(2) < jj(2)) then
133  verdict = -1
134  else if (ii(2) > jj(2)) then
135  verdict = +1
136  else
137  verdict = 0
138  end if
139 
140  end function bstree_compare
141 
142 
147  subroutine check_constructed (this)
148  class(symbolicelementvector) :: this
149  if (.not. this % constructed) then
150  write (stdout, "('Vector::constructed - Vector is not constructed')")
151  stop "Vector::constructed - Vector is not constructed"
152  end if
153  end subroutine check_constructed
154 
155 
166  subroutine construct (this, threshold, initial_size)
167  class(symbolicelementvector) :: this
168  real(wp), optional, intent(in) :: threshold
169  integer, optional, intent(in) :: initial_size
170  integer :: err
171 
172  if (present(threshold)) then
173  this % threshold = threshold
174  else
175  this % threshold = default_integral_threshold
176  end if
177 
178  this % expand_size = 100
179  this % max_capacity = this % expand_size
180 
181  !Allocate the vectors
182  allocate(this % electron_integral(2, this % max_capacity), this % coeffs(this % max_capacity), stat = err)
183  call master_memory % track_memory(kind(this % electron_integral), size(this % electron_integral), &
184  err, 'SYMBOLIC::ELECTRONINT')
185  call master_memory % track_memory(kind(this % coeffs), size(this % coeffs), err, 'SYMBOLIC::ELECTRONCOEFF')
186  if (err /= 0) then
187  write (stdout, "('SymbolicVector::construct- arrays not allocated')")
188  stop "SymbolicVector arrays not allocated"
189  end if
190 
191  !Do an intial clear
192  call this % clear
193 
194  !This has been succesfully constructed
195  this % constructed = .true.
196  end subroutine construct
197 
198 
209  logical function check_same_integral (this, integral, idx) result (found)
210 
211  class(symbolicelementvector), intent(in) :: this
212  integer(longint), target, intent(in) :: integral(2)
213  integer, intent(out) :: idx
214 
215  call this % check_constructed
216 
217  idx = this % locate(-1, c_loc(integral))
218  found = idx > 0
219 
220  end function check_same_integral
221 
222 
235  subroutine insert_ijklm_symbol (this, i, j, k, l, m, coeff, check_same_)
236  class(symbolicelementvector) :: this
237  integer, intent(in) :: i, j, k, l, m
238  real(wp), intent(in) :: coeff
239  logical, optional, intent(in) :: check_same_
240  integer(longint) :: integral_label(2)
241  logical :: check_same
242 
243  if (present(check_same_)) then
244  check_same = check_same_
245  else
246  check_same = .true.
247  end if
248 
249  call pack8ints(i, j, k, l, m, 0, 0, 0, integral_label)
250  call this % insert_symbol(integral_label, coeff, check_same)
251  end subroutine insert_ijklm_symbol
252 
253 
267  subroutine insert_symbol (this, integral_label, coeff, check_same_)
268  class(symbolicelementvector) :: this
269  integer(longint), intent(in) :: integral_label(2)
270  real(wp), intent(in) :: coeff
271  logical, optional, intent(in) :: check_same_
272  integer :: idx = 0, check_idx
273  logical :: check_same
274 
275  if (present(check_same_)) then
276  check_same = check_same_
277  else
278  check_same = .true.
279  end if
280 
281  ! Filter small coefficients
282  if (abs(coeff) < this % threshold) return
283 
284  if (.not.check_same) then
285 
286  this % n = this % n + 1 ! update number of elements
287  idx = this % n ! the idx is the last element
288 
289  if (this % n > this % max_capacity) then ! if we've exceeded capacity then expand
290  call this % expand_array()
291  end if
292 
293  this % electron_integral(:,idx) = integral_label(:) ! place into the array
294  this % coeffs(idx) = 0.0_wp ! zero the coefficient since we can guareentee the compiler will do so
295  call this % insert(idx) ! insert idx into the binary tree
296 
297  !Check if we have the same integral, if we do get the index
298  else if (.not. this % check_same_integral(integral_label, idx)) then
299 
300  this % n = this % n + 1 ! update number of elements
301  idx = this % n ! the idx is the last element
302 
303  if (this % n > this % max_capacity) then ! if we've exceeded capacity then expand
304  call this % expand_array()
305  end if
306 
307  this % electron_integral(:,idx) = integral_label(:) ! place into the array
308  this % coeffs(idx) = 0.0_wp ! zero the coefficient since we can guareentee the compiler will do so
309  call this % insert(idx) ! insert index into the binary tree
310  end if
311 
312  !Now we have an index, lets add in the coeffcient
313  this % coeffs(idx) = this % coeffs(idx) + coeff
314  !write(111,"('COEFF :',2es16.8)") coeff,this%coeffs(idx)
315 
316  end subroutine insert_symbol
317 
318 
326  subroutine expand_array(this)
327  class(symbolicelementvector) :: this
328  integer(longint), allocatable :: temp_integral(:,:)
329  real(wp), allocatable :: temp_coeffs(:)
330  integer :: temp_capacity,ido,err
331 
332  !Of course lets check if we are constructed
333  call this % check_constructed
334 
335  !Now the tempcapacity is the current max capacity
336  temp_capacity = this % max_capacity
337 
338  allocate(temp_integral(2, this % max_capacity), temp_coeffs(this % max_capacity))
339 
340  !Copy over the elements into the temporary array
341  do ido = 1, temp_capacity
342  temp_integral(1,ido) = this % electron_integral(1,ido)
343  temp_integral(2,ido) = this % electron_integral(2,ido)
344  end do
345 
346  temp_coeffs(1:temp_capacity) = this % coeffs(1:temp_capacity)
347 
348  call master_memory % free_memory(kind(this % electron_integral),size(this % electron_integral))
349  call master_memory % free_memory(kind(this % coeffs), size(this % coeffs))
350  !deallocate our old values
351  deallocate(this % electron_integral)
352  deallocate(this % coeffs)
353 
354  !Increase the max capacity
355  this % max_capacity = this % max_capacity + this % expand_size
356  !Allocate our new array with the new max capacity
357  allocate(this % electron_integral(2, this % max_capacity), this % coeffs(this % max_capacity), stat = err)
358  call master_memory % track_memory(kind(this % electron_integral), size(this % electron_integral), &
359  err, 'SYMBOLIC::EXP::ELECTRONINT')
360  call master_memory % track_memory(kind(this % coeffs), size(this % coeffs), err, 'SYMBOLIC::EXP::ELECTRONCOEFF')
361 
362  !Zero the elements
363  this % electron_integral(:,:) = -1
364  this % coeffs(:) = 0.0_wp
365 
366  do ido = 1, temp_capacity
367  !Copy our old values back
368  this % electron_integral(1,ido) = temp_integral(1,ido)
369  this % electron_integral(2,ido) = temp_integral(2,ido)
370  end do
371  this % coeffs(1:temp_capacity) = temp_coeffs(1:temp_capacity)
372  deallocate(temp_integral, temp_coeffs)
373 
374  end subroutine expand_array
375 
376 
385  subroutine add_symbols (this, rhs, alpha_)
386  class(symbolicelementvector) :: this
387  class(symbolicelementvector), intent(in) :: rhs
388  real(wp), optional, intent(in) :: alpha_
389  real(wp) :: alpha
390  integer :: ido
391 
392  if (present(alpha_)) then
393  alpha = alpha_
394  else
395  alpha = 1.0_wp
396  end if
397 
398  !Loop through each rhs symbolic and add it to me scaled by alpha
399  do ido = 1, rhs % n
400  call this % insert_symbol(rhs % electron_integral(1:2,ido), rhs % coeffs(ido) * alpha)
401  end do
402  end subroutine add_symbols
403 
404 
405  subroutine synchronize_symbols (this)
406  class(symbolicelementvector) :: this
407  real(wp), allocatable :: coeffs(:)
408  integer(longint), allocatable, target :: labels(:,:)
409  integer(longint), pointer :: label_ptr(:)
410  integer(longint) :: my_num_symbols, largest_num_symbols, procs_num_of_symbols
411  integer :: ido, proc_id, ierr
412 
413  if (grid % gprocs <= 1) then
414  return
415  end if
416 
417  my_num_symbols = this % get_size()
418 
419  call mpi_reduceall_max(my_num_symbols, largest_num_symbols, grid % gcomm)
420 
421  if (largest_num_symbols == 0) return
422 
423  call master_memory % track_memory(kind(labels), int(largest_num_symbols * 2), 0, 'SYMBOLIC::MPISYNCH::LABELS')
424  call master_memory % track_memory(kind(coeffs), int(largest_num_symbols), 0, 'SYMBOLIC::MPISYNCH::COEFFS')
425  allocate(labels(2,largest_num_symbols), coeffs(largest_num_symbols), stat = ierr)
426 
427  label_ptr(1:largest_num_symbols*2) => labels(:,:)
428  labels = 0
429  coeffs = 0
430 
431  if (my_num_symbols > 0) then
432  labels(1,1:my_num_symbols) = this % electron_integral(1,1:my_num_symbols)
433  labels(2,1:my_num_symbols) = this % electron_integral(2,1:my_num_symbols)
434  coeffs(1:my_num_symbols) = this % coeffs(1:my_num_symbols)
435  end if
436 
437  procs_num_of_symbols = my_num_symbols
438 
439  do proc_id = 1, grid % gprocs - 1
440  call mpi_mod_rotate_arrays_around_ring(procs_num_of_symbols, label_ptr, coeffs, largest_num_symbols, grid % gcomm)
441  do ido = 1, procs_num_of_symbols
442  call this % insert_symbol(labels(1:2,ido), coeffs(ido))
443  end do
444  end do
445 
446  call master_memory % free_memory(kind(labels), size(labels))
447  call master_memory % free_memory(kind(coeffs), size(coeffs))
448  deallocate(labels, coeffs)
449 
450  end subroutine synchronize_symbols
451 
452 
462  logical function check_bounds (this, i)
463  class(symbolicelementvector) :: this
464  integer, intent(in) :: i
465 
466  if (i <= 0 .or. i > this % n) then
467  write (stdout, "('SymbolicVector::check_bounds - Out of Bounds access')")
468  stop "SymbolicVector::check_bounds - Out of Bounds access"
469  check_bounds = .false.
470  else
471  check_bounds = .true.
472  end if
473 
474  end function check_bounds
475 
476 
483  subroutine remove_symbol_at (this, idx)
484  class(symbolicelementvector) :: this
485  integer, intent(in) :: idx
486  integer :: ido
487 
488  if (this % check_bounds(idx)) then
489  do ido = idx + 1, this % n
490  this % electron_integral(1,ido-1) = this % electron_integral(1,ido)
491  this % electron_integral(2,ido-1) = this % electron_integral(2,ido)
492  this % coeffs(ido-1) = this % coeffs(ido)
493  end do
494  this % electron_integral(:, this % n) = 0
495  this % coeffs(this % n) = 0
496  this % n = this % n - 1
497  end if
498 
499  end subroutine remove_symbol_at
500 
501 
511  logical function is_empty (this)
512  class(symbolicelementvector) :: this
513 
514  is_empty = (this % n == 0)
515 
516  end function is_empty
517 
518 
526  subroutine clear (this)
527  class(symbolicelementvector) :: this
528 
529  this % n = 0
530  call this % bstree % destroy
531 
532  end subroutine clear
533 
534 
541  subroutine modify_coeff (this, idx, coeff)
542  class(symbolicelementvector) :: this
543  integer, intent(in) :: idx
544  real(wp) :: coeff
545 
546  this % coeffs(idx) = this % coeffs(idx) + coeff
547 
548  end subroutine modify_coeff
549 
550 
556  function get_integral_label (this, idx)
557  class(symbolicelementvector) :: this
558  integer, intent(in) :: idx
559  integer(longint), dimension(2) :: get_integral_label
560 
561  if (this % check_bounds(idx)) get_integral_label(:) = this % electron_integral(:,idx)
562 
563  end function get_integral_label
564 
565 
571  real(wp) function get_coefficient (this, idx)
572  class(symbolicelementvector) :: this
573  integer, intent(in) :: idx
574 
575  if (this % check_bounds(idx)) get_coefficient = this % coeffs(idx)
576 
577  end function get_coefficient
578 
579 
585  subroutine get_coeff_and_integral (this, idx, coeff, label)
586  class(symbolicelementvector), intent(in) :: this
587  integer, intent(in) :: idx
588  real(wp), intent(out) :: coeff
589  integer(longint), intent(out) :: label(2)
590 
591  if (this % check_bounds(idx)) then
592  label(1:2) = this % electron_integral(1:2,idx)
593  coeff = this % coeffs(idx)
594  end if
595 
596  end subroutine get_coeff_and_integral
597 
598 
603  integer function get_size(this)
604  class(symbolicelementvector) :: this
605  get_size = this % n
606  end function get_size
607 
608 
614  subroutine destroy (this)
615  class(symbolicelementvector) :: this
616 
617  if (allocated(this % electron_integral)) then
618  call master_memory % free_memory(kind(this % electron_integral), size(this % electron_integral))
619  deallocate(this % electron_integral)
620  end if
621  if (allocated(this % coeffs)) then
622  call master_memory % free_memory(kind(this % coeffs), size(this % coeffs))
623  deallocate(this % coeffs)
624  end if
625 
626  call this % bstree % destroy
627 
628  this % constructed = .false.
629 
630  end subroutine destroy
631 
632 
637  subroutine print_symbols (this)
638  class(symbolicelementvector) :: this
639  integer :: labels(8), ido
640  real(wp) :: coeff
641 
642  if (.not. this % is_empty()) write (stdout, "('Outputting symbolic elements....')")
643  do ido = 1, this % n
644  call unpack8ints(this % electron_integral(1,ido), labels)
645  write (stdout, "(5i4,' -- ',es14.3)") labels(1:5),this % coeffs(ido)
646  end do
647 
648  end subroutine print_symbols
649 
650 end module symbolic_module
memorymanager_module
Memory manager module.
Definition: MemoryManager_module.f90:31
symbolic_module::add_symbols
subroutine add_symbols(this, rhs, alpha_)
This inserts one symbolic vector into another scaled by a coefficient.
Definition: Symbolic_Module.f90:386
symbolic_module::construct
subroutine construct(this, threshold, initial_size)
Constructs the class.
Definition: Symbolic_Module.f90:167
symbolic_module::modify_coeff
subroutine modify_coeff(this, idx, coeff)
Update coeff with contribution.
Definition: Symbolic_Module.f90:542
symbolic_module::get_coeff_and_integral
subroutine get_coeff_and_integral(this, idx, coeff, label)
Get both label and coeffcient at specific index.
Definition: Symbolic_Module.f90:586
symbolic_module::check_same_integral
logical function check_same_integral(this, integral, idx)
A function to check whether the same integral label exists.
Definition: Symbolic_Module.f90:210
symbolic_module::check_constructed
subroutine check_constructed(this)
A simple class to check if this has been properly constructed.
Definition: Symbolic_Module.f90:148
symbolic_module::get_size
integer function get_size(this)
Returns the number of symbolic elements stored.
Definition: Symbolic_Module.f90:604
symbolic_module::is_empty
logical function is_empty(this)
Emptiness check.
Definition: Symbolic_Module.f90:512
symbolic_module::destroy
subroutine destroy(this)
Cleans up the class by deallocating arrays.
Definition: Symbolic_Module.f90:615
symbolic_module
Symbolic module.
Definition: Symbolic_Module.f90:33
parallelization_module::process_grid
type(processgrid) process_grid
Definition: Parallelization_module.F90:80
consts_mpi_ci
MPI SCATCI Constants module.
Definition: consts_mpi_ci.f90:31
symbolic_module::symbolicelementvector
This class handles the storage symbolic elements.
Definition: Symbolic_Module.f90:57
symbolic_module::get_integral_label
integer(longint) function, dimension(2) get_integral_label(this, idx)
Get integral label at specific index.
Definition: Symbolic_Module.f90:557
symbolic_module::check_bounds
logical function check_bounds(this, i)
Range check.
Definition: Symbolic_Module.f90:463
parallelization_module
Distribution of processes into a grid.
Definition: Parallelization_module.F90:29
symbolic_module::bstree_compare
integer function bstree_compare(this, i, j, data)
Compare two integral index sets.
Definition: Symbolic_Module.f90:112
consts_mpi_ci::default_integral_threshold
real(wp), parameter default_integral_threshold
Default threshold to store integrals.
Definition: consts_mpi_ci.f90:142
symbolic_module::expand_array
subroutine expand_array(this)
Array expansion subroutine.
Definition: Symbolic_Module.f90:327
symbolic_module::insert_ijklm_symbol
subroutine insert_ijklm_symbol(this, i, j, k, l, m, coeff, check_same_)
Insert unpacked integral labels.
Definition: Symbolic_Module.f90:236
symbolic_module::insert_symbol
subroutine insert_symbol(this, integral_label, coeff, check_same_)
Insert a packed integral symbol into the class.
Definition: Symbolic_Module.f90:268
symbolic_module::remove_symbol_at
subroutine remove_symbol_at(this, idx)
Removes an integral and coefficient.
Definition: Symbolic_Module.f90:484
symbolic_module::get_coefficient
real(wp) function get_coefficient(this, idx)
Get coefficient at specific index.
Definition: Symbolic_Module.f90:572
symbolic_module::print_symbols
subroutine print_symbols(this)
Print currently stored symbols.
Definition: Symbolic_Module.f90:638
symbolic_module::clear
subroutine clear(this)
Clear array.
Definition: Symbolic_Module.f90:527
memorymanager_module::master_memory
type(memorymanager), public master_memory
This is the global memory manager.
Definition: MemoryManager_module.f90:69
symbolic_module::synchronize_symbols
subroutine synchronize_symbols(this)
Definition: Symbolic_Module.f90:406