MPI-SCATCI 2.0
An MPI version of SCATCI
Loading...
Searching...
No Matches
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
22!> \brief Symbolic module
23!> \authors A Al-Refaie
24!> \date 2017
25!>
26!> This module handles the storage of symbolic elements produced from slater rules
27!> when integrating configuration state functions
28!>
29!> \note 30/01/2017 - Ahmed Al-Refaie: Initial documentation Version.
30!> \note 16/01/2019 - Jakub Benda: Unifom coding style and expanded documentation.
31!> \note 22/02/2019 - Jakub Benda: Removed dependency on C++ std::map.
32!>
33module symbolic_module
34
35 use const_gbl, only: stdout
37 use containers, only: bstree
38 use iso_c_binding, only: c_loc, c_ptr, c_f_pointer
39 use mpi_gbl, only: mpi_reduceall_max, mpi_mod_rotate_arrays_around_ring, mpi_xermsg
40 use precisn, only: longint, wp
41 use memorymanager_module, only: master_memory
42 use parallelization_module, only: grid => process_grid
43 use utility_module, only: lexicographical_compare, pack_ints, unpack_ints
44
45 implicit none
46
47 public symbolicelementvector
48
49 private
50
51 !> \brief This class handles the storage symbolic elements
52 !>
53 !> This class handles the storage symbolic elements and also expands the vector size if we have reached max capacity
54 !> Additionaly, uses a binary search tree to perform a binary search on integrals labels during insertion to ensure no
55 !> repeating elements this is automatic and removes the compression stage in the original SCATCI code.
56 !>
57 type, extends(bstree) :: symbolicelementvector
58 private
59 integer(longint), allocatable :: electron_integral(:,:) !< The packed integral storage
60 real(wp), allocatable :: coeffs(:) !< The coefficients of the integral
61 integer :: n = 0 !< Number of elements in the array of both the integral and coefficients
62 logical :: constructed = .false. !< Whether this class has been constructed
63 real(wp) :: threshold = 0.0 !< The integral threshold
64 integer :: max_capacity = 0 !< The number of free slots in the array
65 integer :: expand_size = 10 !< How much we have to expand each
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
92contains
93
94 !> \brief Compare two integral index sets
95 !> \authors J Benda
96 !> \date 2019
97 !>
98 !> This is the predicate (order-defining function) used by the binary search tree. Given two pointers into the
99 !> electron integral array, it returns 0 when the corresponding integral index sets are equal, -1 when the
100 !> first one is (lexicographically) less, and +1 when the first one is (lexicographically) greater.
101 !>
102 !> When either of the indices is non-positive, the dummy value pointed to by the optional argument is used instead.
103 !>
104 !> \param[in] this Symbolic vector containing the electron_integral storage.
105 !> \param[in] i Position of the first integral index set.
106 !> \param[in] j Position of the first integral index set.
107 !> \param[in] data Integral index set used for comparing with sets being processed.
108 !>
109 !> \returns -1/0/+1 as a lexicographical spaceship operator applied on the index sets.
110 !>
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(nidx), jj(nidx)
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, (/ nidx /))
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 verdict = lexicographical_compare(ii, jj)
129
130 end function bstree_compare
131
132
133 !> \brief A simple class to check if this has been properly constructed
134 !> \authors A Al-Refaie
135 !> \date 2017
136 !>
137 subroutine check_constructed (this)
138 class(symbolicelementvector) :: this
139 if (.not. this % constructed) then
140 write (stdout, "('Vector::constructed - Vector is not constructed')")
141 stop "Vector::constructed - Vector is not constructed"
142 end if
143 end subroutine check_constructed
144
145
146 !> \brief Constructs the class
147 !> \authors A Al-Refaie
148 !> \date 2017
149 !>
150 !> Constructs the class by allocating space for the electron integrals.
151 !>
152 !> \param[inout] this Vector object to initialize.
153 !> \param[in] threshold The mininum integral coefficient threshold we should store
154 !> \param[in] initial_size Deprecated, doesnt do anything
155 !>
156 subroutine construct (this, threshold, initial_size)
157 class(symbolicelementvector) :: this
158 real(wp), optional, intent(in) :: threshold
159 integer, optional, intent(in) :: initial_size
160 integer :: err
161
162 if (present(threshold)) then
163 this % threshold = threshold
164 else
165 this % threshold = default_integral_threshold
166 end if
167
168 this % expand_size = 100
169 this % max_capacity = this % expand_size
170
171 !Allocate the vectors
172 allocate(this % electron_integral(nidx, this % max_capacity), this % coeffs(this % max_capacity), stat = err)
173 call master_memory % track_memory(storage_size(this % electron_integral)/8, size(this % electron_integral), &
174 err, 'SYMBOLIC::ELECTRONINT')
175 call master_memory % track_memory(storage_size(this % coeffs)/8, size(this % coeffs), err, 'SYMBOLIC::ELECTRONCOEFF')
176 if (err /= 0) then
177 write (stdout, "('SymbolicVector::construct- arrays not allocated')")
178 stop "SymbolicVector arrays not allocated"
179 end if
180
181 !Do an intial clear
182 call this % clear
183
184 !This has been succesfully constructed
185 this % constructed = .true.
186 end subroutine construct
187
188
189 !> \brief A function to check whether the same integral label exists
190 !> \authors A Al-Refaie
191 !> \date 2017
192 !>
193 !> \param[inout] this Vector object to query.
194 !> \param[in] integral Packed integral label
195 !> \param[out] idx The index to the electron integral array, -1 if not found, positive otherwise
196 !>
197 !> \result check_same_integral True: we found one the same one. False: there isn't one
198 !>
199 logical function check_same_integral (this, integral, idx) result (found)
200
201 class(symbolicelementvector), intent(in) :: this
202 integer(longint), target, intent(in) :: integral(nidx)
203 integer, intent(out) :: idx
204
205 call this % check_constructed
206
207 idx = this % locate(-1, c_loc(integral))
208 found = idx > 0
209
210 end function check_same_integral
211
212
213 !> \brief Insert unpacked integral labels
214 !> \authors A Al-Refaie
215 !> \date 2017
216 !>
217 !> This subroutine is used to insert unpacked integral labels. Wraps insert_symbol by automatically packing.
218 !>
219 !> \param[inout] this Vector object to update.
220 !> \param[in] i,j,k,l The integral labels
221 !> \param[in] m Positron label
222 !> \param[in] coeff The integral coefficient
223 !> \param[in] check_same_ Deprecated, orginally used to debug but now does nothing
224 !>
225 subroutine insert_ijklm_symbol (this, i, j, k, l, m, coeff, check_same_)
226 class(symbolicelementvector) :: this
227 integer, intent(in) :: i, j, k, l, m
228 real(wp), intent(in) :: coeff
229 logical, optional, intent(in) :: check_same_
230 integer(longint) :: integral_label(NIDX)
231 logical :: check_same
232
233 if (present(check_same_)) then
234 check_same = check_same_
235 else
236 check_same = .true.
237 end if
238
239 call pack_ints(i, j, k, l, m, 0, 0, 0, integral_label)
240 call this % insert_symbol(integral_label, coeff, check_same)
241 end subroutine insert_ijklm_symbol
242
243
244 !> \brief Insert a packed integral symbol into the class
245 !> \authors A Al-Refaie
246 !> \date 2017
247 !>
248 !> This subroutine is used to insert the integral into the class, it performs a check to see if it exists, if not then
249 !> it will insert (and expand if needed) into the integral array. Otherwise it will simply add the coefficients to found
250 !> integral index.
251 !>
252 !> \param[inout] this Vector object to update.
253 !> \param[in] integral_label Packed integral label
254 !> \param[in] coeff The integral coefficient
255 !> \param[in] check_same_ Deprecated, orginally used to debug but now does nothing
256 !>
257 subroutine insert_symbol (this, integral_label, coeff, check_same_)
258 class(symbolicelementvector) :: this
259 integer(longint), intent(in) :: integral_label(NIDX)
260 real(wp), intent(in) :: coeff
261 logical, optional, intent(in) :: check_same_
262 integer :: idx = 0, check_idx
263 logical :: check_same
264
265 if (present(check_same_)) then
266 check_same = check_same_
267 else
268 check_same = .true.
269 end if
270
271 ! Filter small coefficients
272 if (abs(coeff) < this % threshold) return
273
274 if (.not.check_same) then
275
276 this % n = this % n + 1 ! update number of elements
277 idx = this % n ! the idx is the last element
278
279 if (this % n > this % max_capacity) then ! if we've exceeded capacity then expand
280 call this % expand_array()
281 end if
282
283 this % electron_integral(:,idx) = integral_label(:) ! place into the array
284 this % coeffs(idx) = 0.0_wp ! zero the coefficient since we can guareentee the compiler will do so
285 call this % insert(idx) ! insert idx into the binary tree
286
287 !Check if we have the same integral, if we do get the index
288 else if (.not. this % check_same_integral(integral_label, idx)) then
289
290 this % n = this % n + 1 ! update number of elements
291 idx = this % n ! the idx is the last element
292
293 if (this % n > this % max_capacity) then ! if we've exceeded capacity then expand
294 call this % expand_array()
295 end if
296
297 this % electron_integral(:,idx) = integral_label(:) ! place into the array
298 this % coeffs(idx) = 0.0_wp ! zero the coefficient since we can guareentee the compiler will do so
299 call this % insert(idx) ! insert index into the binary tree
300 end if
301
302 !Now we have an index, lets add in the coeffcient
303 this % coeffs(idx) = this % coeffs(idx) + coeff
304 !write(111,"('COEFF :',2es16.8)") coeff,this%coeffs(idx)
305
306 end subroutine insert_symbol
307
308
309 !> \brief Array expansion subroutine
310 !> \authors A Al-Refaie
311 !> \date 2017
312 !>
313 !> It simply copies the coeffcieint and integral array into a temporary one, reallocates a new array increased by expand_size
314 !> and then recopies the elements and updates max capacity.
315 !>
316 subroutine expand_array(this)
317 class(symbolicelementvector) :: this
318 integer(longint), allocatable :: temp_integral(:,:)
319 real(wp), allocatable :: temp_coeffs(:)
320 integer :: temp_capacity,ido,err
321
322 !Of course lets check if we are constructed
323 call this % check_constructed
324
325 !Now the tempcapacity is the current max capacity
326 temp_capacity = this % max_capacity
327
328 allocate(temp_integral(nidx, this % max_capacity), temp_coeffs(this % max_capacity))
329
330 !Copy over the elements into the temporary array
331 do ido = 1, temp_capacity
332 temp_integral(:,ido) = this % electron_integral(:,ido)
333 end do
334
335 temp_coeffs(1:temp_capacity) = this % coeffs(1:temp_capacity)
336
337 call master_memory % free_memory(storage_size(this % electron_integral)/8, size(this % electron_integral))
338 call master_memory % free_memory(storage_size(this % coeffs)/8, size(this % coeffs))
339 !deallocate our old values
340 deallocate(this % electron_integral)
341 deallocate(this % coeffs)
342
343 !Increase the max capacity
344 this % max_capacity = this % max_capacity + this % expand_size
345 !Allocate our new array with the new max capacity
346 allocate(this % electron_integral(nidx, this % max_capacity), this % coeffs(this % max_capacity), stat = err)
347 call master_memory % track_memory(storage_size(this % electron_integral)/8, size(this % electron_integral), &
348 err, 'SYMBOLIC::EXP::ELECTRONINT')
349 call master_memory % track_memory(storage_size(this % coeffs)/8, size(this % coeffs), err, 'SYMBOLIC::EXP::ELECTRONCOEFF')
350
351 !Zero the elements
352 this % electron_integral(:,:) = -1
353 this % coeffs(:) = 0.0_wp
354
355 do ido = 1, temp_capacity
356 !Copy our old values back
357 this % electron_integral(:,ido) = temp_integral(:,ido)
358 end do
359 this % coeffs(1:temp_capacity) = temp_coeffs(1:temp_capacity)
360 deallocate(temp_integral, temp_coeffs)
361
362 end subroutine expand_array
363
364
365 !> \brief This inserts one symbolic vector into another scaled by a coefficient
366 !> \authors A Al-Refaie
367 !> \date 2017
368 !>
369 !> \param[inout] this The symbolic vector to modify.
370 !> \param[in] rhs The symbolic vector to add into this class.
371 !> \param[in] alpha_ The scaling value to be applied to each coefficient.
372 !>
373 subroutine add_symbols (this, rhs, alpha_)
374 class(symbolicelementvector) :: this
375 class(symbolicelementvector), intent(in) :: rhs
376 real(wp), optional, intent(in) :: alpha_
377 real(wp) :: alpha
378 integer :: ido
379
380 if (present(alpha_)) then
381 alpha = alpha_
382 else
383 alpha = 1.0_wp
384 end if
385
386 !Loop through each rhs symbolic and add it to me scaled by alpha
387 do ido = 1, rhs % n
388 call this % insert_symbol(rhs % electron_integral(:,ido), rhs % coeffs(ido) * alpha)
389 end do
390 end subroutine add_symbols
391
392
393 subroutine synchronize_symbols (this)
394 class(symbolicelementvector) :: this
395 real(wp), allocatable :: coeffs(:)
396 integer(longint), allocatable, target :: labels(:,:)
397 integer(longint), pointer :: label_ptr(:)
398 integer(longint) :: my_num_symbols, largest_num_symbols, procs_num_of_symbols
399 integer :: ido, proc_id, ierr
400
401 if (grid % gprocs <= 1) then
402 return
403 end if
404
405 my_num_symbols = this % get_size()
406
407 call mpi_reduceall_max(my_num_symbols, largest_num_symbols, grid % gcomm)
408
409 if (largest_num_symbols == 0) return
410
411 call master_memory % track_memory(storage_size(labels)/8, int(largest_num_symbols * nidx), 0, 'SYMBOLIC::MPISYNCH::LABELS')
412 call master_memory % track_memory(storage_size(coeffs)/8, int(largest_num_symbols), 0, 'SYMBOLIC::MPISYNCH::COEFFS')
413 allocate(labels(nidx,largest_num_symbols), coeffs(largest_num_symbols), stat = ierr)
414
415 label_ptr(1:largest_num_symbols*nidx) => labels(:,:)
416 labels = 0
417 coeffs = 0
418
419 if (my_num_symbols > 0) then
420 labels(:,1:my_num_symbols) = this % electron_integral(:,1:my_num_symbols)
421 coeffs(1:my_num_symbols) = this % coeffs(1:my_num_symbols)
422 end if
423
424 procs_num_of_symbols = my_num_symbols
425
426 do proc_id = 1, grid % gprocs - 1
427 call mpi_mod_rotate_arrays_around_ring(procs_num_of_symbols, label_ptr, coeffs, largest_num_symbols, grid % gcomm)
428 do ido = 1, procs_num_of_symbols
429 call this % insert_symbol(labels(:,ido), coeffs(ido))
430 end do
431 end do
432
433 call master_memory % free_memory(storage_size(labels)/8, size(labels))
434 call master_memory % free_memory(storage_size(coeffs)/8, size(coeffs))
435 deallocate(labels, coeffs)
436
437 end subroutine synchronize_symbols
438
439
440 !> \brief Range check
441 !> \authors A Al-Refaie
442 !> \date 2017
443 !>
444 !> Simply checks the index and wheter it exists within the array.
445 !>
446 !> \param[inout] this Vector object to query.
447 !> \param[in] i The index we wish to access
448 !>
449 logical function check_bounds (this, i)
450 class(symbolicelementvector) :: this
451 integer, intent(in) :: i
452
453 if (i <= 0 .or. i > this % n) then
454 write (stdout, "('SymbolicVector::check_bounds - Out of Bounds access')")
455 stop "SymbolicVector::check_bounds - Out of Bounds access"
456 check_bounds = .false.
457 else
458 check_bounds = .true.
459 end if
460
461 end function check_bounds
462
463
464 !> \brief Removes an integral and coefficient
465 !> \author A Al-Refaie
466 !> \date 2017
467 !>
468 !> Never been used and pretty much useless.
469 !>
470 subroutine remove_symbol_at (this, idx)
471 class(symbolicelementvector) :: this
472 integer, intent(in) :: idx
473 integer :: ido
474
475 if (this % check_bounds(idx)) then
476 do ido = idx + 1, this % n
477 this % electron_integral(:,ido-1) = this % electron_integral(:,ido)
478 this % coeffs(ido-1) = this % coeffs(ido)
479 end do
480 this % electron_integral(:, this % n) = 0
481 this % coeffs(this % n) = 0
482 this % n = this % n - 1
483 end if
484
485 end subroutine remove_symbol_at
486
487
488 !> \brief Emptiness check
489 !> \authors A Al-Refaie
490 !> \date 2017
491 !> \public
492 !>
493 !> Simply returns whether we are storing integrals and coeffs or not.
494 !>
495 !> \result True if empty or False if we have any symbols.
496 !>
497 logical function is_empty (this)
498 class(symbolicelementvector) :: this
499
500 is_empty = (this % n == 0)
501
502 end function is_empty
503
504
505 !> \brief Clear array
506 !> \authors A Al-Refaie
507 !> \date 2017
508 !> \public
509 !>
510 !> Clears our array (not really but it essential resets the symbol counter to zero, which is way quicker).
511 !>
512 subroutine clear (this)
513 class(symbolicelementvector) :: this
514
515 this % n = 0
516 call this % bstree % destroy
517
518 end subroutine clear
519
520
521 !> \brief Update coeff with contribution
522 !> \authors A Al-Refaie
523 !> \date 2017
524 !>
525 !> Add contribution to coeff at given index.
526 !>
527 subroutine modify_coeff (this, idx, coeff)
528 class(symbolicelementvector) :: this
529 integer, intent(in) :: idx
530 real(wp) :: coeff
531
532 this % coeffs(idx) = this % coeffs(idx) + coeff
533
534 end subroutine modify_coeff
535
536
537 !> \brief Get integral label at specific index
538 !> \authors A Al-Refaie
539 !> \date 2017
540 !> \public
541 !>
542 function get_integral_label (this, idx)
543 class(symbolicelementvector) :: this
544 integer, intent(in) :: idx
545 integer(longint) :: get_integral_label(nidx)
546
547 if (this % check_bounds(idx)) get_integral_label(:) = this % electron_integral(:,idx)
548
549 end function get_integral_label
550
551
552 !> \brief Get coefficient at specific index
553 !> \authors A Al-Refaie
554 !> \date 2017
555 !> \public
556 !>
557 real(wp) function get_coefficient (this, idx)
558 class(symbolicelementvector) :: this
559 integer, intent(in) :: idx
560
561 if (this % check_bounds(idx)) get_coefficient = this % coeffs(idx)
562
563 end function get_coefficient
564
565
566 !> \brief Get both label and coeffcient at specific index
567 !> \authors A Al-Refaie
568 !> \date 2017
569 !> \public
570 !>
571 subroutine get_coeff_and_integral (this, idx, coeff, label)
572 class(symbolicelementvector), intent(in) :: this
573 integer, intent(in) :: idx
574 real(wp), intent(out) :: coeff
575 integer(longint), intent(out) :: label(NIDX)
576
577 if (this % check_bounds(idx)) then
578 label = this % electron_integral(:,idx)
579 coeff = this % coeffs(idx)
580 end if
581
582 end subroutine get_coeff_and_integral
583
584
585 !> \brief Returns the number of symbolic elements stored
586 !> \authors A Al-Refaie
587 !> \date 2017
588 !> \public
589 integer function get_size(this)
590 class(symbolicelementvector) :: this
591 get_size = this % n
592 end function get_size
593
594
595 !> \brief Cleans up the class by deallocating arrays
596 !> \authors A Al-Refaie
597 !> \date 2017
598 !> \public
599 !>
600 subroutine destroy (this)
601 class(symbolicelementvector) :: this
602
603 if (allocated(this % electron_integral)) then
604 call master_memory % free_memory(storage_size(this % electron_integral)/8, size(this % electron_integral))
605 deallocate(this % electron_integral)
606 end if
607 if (allocated(this % coeffs)) then
608 call master_memory % free_memory(storage_size(this % coeffs)/8, size(this % coeffs))
609 deallocate(this % coeffs)
610 end if
611
612 call this % bstree % destroy
613
614 this % constructed = .false.
615
616 end subroutine destroy
617
618
619 !> \brief Print currently stored symbols
620 !> \authors A Al-Refaie
621 !> \date 2017
622 !> \public
623 subroutine print_symbols (this)
624 class(symbolicelementvector) :: this
625 integer :: labels(8), ido
626 real(wp) :: coeff
627
628 if (.not. this % is_empty()) write (stdout, "('Outputting symbolic elements....')")
629 do ido = 1, this % n
630 call unpack_ints(this % electron_integral(:,ido), labels)
631 write (stdout, "(5i4,' -- ',es14.3)") labels(1:5),this % coeffs(ido)
632 end do
633
634 end subroutine print_symbols
635
636end module symbolic_module
MPI SCATCI Constants module.
integer, parameter nidx
Number of long integers used to store up to 8 (packed) integral indices.
real(wp), parameter default_integral_threshold
Default threshold to store integrals.