MPI-SCATCI 2.0
An MPI version of SCATCI
Loading...
Searching...
No Matches
MatrixCache_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 Matrix cache module
23!> \authors A Al-Refaie
24!> \date 2017
25!>
26!> \note 16/01/2019 - Jakub Benda: Unifom coding style and expanded documentation.
27!>
28module matrixcache_module
29
30 use precisn, only: wp
31 use const_gbl, only: stdout
32 use timing_module, only: master_timer
33 use memorymanager_module, only: master_memory
35
36 implicit none
37
38 public matrixcache
39
40 private
41
42 type :: matrixarray
43 integer, pointer :: ij(:,:)
44 real(wp), pointer :: coefficient(:)
45 integer :: num_elems
46 logical :: constructed = .false.
47 contains
48 !procedure, public :: is_valid
49 procedure, public :: destroy => destroy_matrix_array
50 procedure, public :: get => get_from_array
51 procedure, public :: insert => insert_into_array
52 procedure, public :: construct => construct_array
53 procedure, public :: clear => clear_array
54 end type matrixarray
55
56 !> \brief This handles the matrix elements and also expands the vector size if we have reached max capacity
57 !> \authors A Al-Refaie
58 !> \date 2017
59 !>
60 type :: matrixcache
61 type(matrixarray), allocatable :: matrix_arrays(:)
62 integer :: n !< Number of elements in the array of both the integral and coefficients
63 logical :: constructed = .false.
64 integer :: matrix_index
65 integer :: num_matrix_chunks = 1
66 integer :: max_capacity = 0 !< The number of free slots in the array
67 integer :: expand_size = default_expand_size !< How much we have to expand each
68 contains
69 procedure, public :: construct
70 procedure, public :: insert_into_cache
71 procedure, public :: get_from_cache
72 procedure, public :: is_empty
73 procedure, public :: clear
74 procedure, public :: get_size
75 procedure, public :: expand_capacity
76 !procedure, public :: prune_threshold
77 procedure, public :: sort => qsort_matelem
78 procedure, public :: print => print_matelems
79 procedure, public :: get_local_mat
80 procedure, public :: get_chunk_idx
81 procedure, private :: quick_sort_function
82 procedure, private :: partition_function
83 procedure, private :: expand_array
84 procedure, public :: shrink_capacity
85 procedure, public :: clear_and_shrink
86 procedure, private :: check_bounds
87 procedure, public :: destroy => destroy_cache
88 procedure, private :: check_constructed
89 !procedure, public :: count_occurance
90 !procedure, public :: construct
91 end type matrixcache
92
93contains
94
95 subroutine check_constructed (this)
96 class(matrixcache) :: this
97
98 if (.not. this % constructed) then
99 write (stdout, "('Vector::constructed - Vector is not constructed')")
100 stop "Vector::constructed - Vector is not constructed"
101 end if
102
103 end subroutine check_constructed
104
105
106 integer function get_chunk_idx (this, idx)
107 class(matrixcache) :: this
108 integer, intent(in) :: idx
109 integer :: mat_idx, local_idx
110
111 call this % get_local_mat(idx, mat_idx, local_idx)
112
113 get_chunk_idx = mat_idx
114
115 end function get_chunk_idx
116
117
118 subroutine construct (this, expand_size_)
119 class(matrixcache) :: this
120 integer :: err
121 integer, optional, intent(in) :: expand_size_
122
123 if (present(expand_size_)) then
124 this % expand_size = expand_size_
125 else
126 this % expand_size = default_expand_size
127 end if
128
129 this % max_capacity = this % expand_size
130 this % n = 0
131 this % matrix_index = 1
132
133 !Allocate the vectors
134 allocate(this%matrix_arrays(this%matrix_index),stat=err)
135 call master_memory%track_memory(storage_size(this%matrix_arrays)/8, size(this%matrix_arrays), err, &
136 'MATRIXCACHE::MATRIXARRAY')
137
138 call this % matrix_arrays(this % matrix_index) % construct(this % expand_size)
139 !this%matrix_arrays(this%matrix_index)%num_elems = 0
140
141 this % num_matrix_chunks = 1
142
143 call this % clear
144
145 if (err /= 0) then
146 write (stdout, "('Matrix Cache::construct- arrays not allocated')")
147 stop "Matrix Cache:: arrays not allocated"
148 end if
149
150 call this % clear
151 this % constructed = .true.
152
153 end subroutine construct
154
155
156 subroutine insert_into_cache (this, i, j, coefficient)
157 class(matrixcache) :: this
158 integer, intent(in) :: i, j
159 real(wp), intent(in) :: coefficient
160 integer :: mat_id, local_index
161
162 !this % matrix_size = max(this % matrix_size, i, j)
163 this % n = this % n + 1
164
165 if (this % n > this % max_capacity) then
166 call this % expand_array()
167 end if
168
169 call this % get_local_mat(this % n, mat_id, local_index)
170
171 this % num_matrix_chunks = mat_id
172 this % matrix_arrays(mat_id) % ij(1, local_index) = i
173 this % matrix_arrays(mat_id) % ij(2, local_index) = j
174 this % matrix_arrays(mat_id) % coefficient(local_index) = coefficient
175
176 this % matrix_arrays(mat_id) % num_elems = this % matrix_arrays(mat_id) % num_elems + 1
177
178 end subroutine insert_into_cache
179
180
181 subroutine get_local_mat (this, idx, mat_id, local_index)
182 class(matrixcache) :: this
183 integer, intent(in) :: idx
184 integer, intent(out) :: mat_id
185 integer, intent(out) :: local_index
186
187 if (this % check_bounds(idx)) then
188 mat_id = (idx - 1) / this % expand_size + 1
189 !if(mat_id > this%num_matrix_chunks) then
190 !write(stdout,"('Error, referencing a chunk larger than me! [idx,max_Capacity,expand_size,num_chunks,referenced_chunk] = ',5i8)") idx,this%max_capacity,this%expand_size,this%num_matrix_chunks,mat_id
191 !stop "matid error"
192 !endif
193 local_index = idx - (mat_id - 1) * this % expand_size
194 end if
195
196 end subroutine get_local_mat
197
198
199 subroutine get_from_cache (this, idx, i, j, coeff)
200 class(matrixcache) :: this
201 integer, intent(in) :: idx
202 integer, intent(out) :: i, j
203 real(wp), intent(out) :: coeff
204
205 integer :: mat_id, local_index
206
207 if (this % check_bounds(idx)) then
208 call this % get_local_mat(idx, mat_id, local_index)
209
210 i = this % matrix_arrays(mat_id) % ij(1, local_index)
211 j = this % matrix_arrays(mat_id) % ij(2, local_index)
212
213 coeff = this % matrix_arrays(mat_id) % coefficient(local_index)
214 end if
215
216 end subroutine get_from_cache
217
218
219 subroutine expand_array (this)
220 class(matrixcache) :: this
221 type(matrixarray) :: temp_matrix(this % num_matrix_chunks + 1)
222 integer :: new_num_mats, err
223
224 call this % check_constructed
225
226 !call master_timer%start_timer("Expand array")
227 temp_matrix(1:this % num_matrix_chunks) = this % matrix_arrays(1:this % num_matrix_chunks)
228 call master_memory % free_memory(storage_size(this % matrix_arrays)/8, size(this % matrix_arrays))
229 deallocate(this % matrix_arrays)
230
231 this % max_capacity = this % max_capacity + this % expand_size
232
233 new_num_mats = this % max_capacity / this % expand_size
234 this % matrix_index = new_num_mats
235 this % num_matrix_chunks = this % num_matrix_chunks + 1
236 allocate(this % matrix_arrays(this % num_matrix_chunks), stat = err)
237 call master_memory % track_memory(storage_size(this % matrix_arrays)/8, size(this % matrix_arrays), err, &
238 'MATRIXCACHE::MATRIXARRAY')
239
240 this % matrix_arrays(:) = temp_matrix(:)
241 this % matrix_arrays(this % num_matrix_chunks) % num_elems = 0
242 call this % matrix_arrays(this % num_matrix_chunks) % construct(this % expand_size)
243 !call master_timer%stop_timer("Expand array")
244
245 end subroutine expand_array
246
247
248 subroutine destroy_matrix_array (this)
249 class(matrixarray) :: this
250
251 if (.not. this % constructed) return
252 if (associated(this % ij)) then
253 call master_memory % free_memory(storage_size(this % ij)/8, size(this % ij))
254 deallocate(this % ij)
255 this % ij => null()
256 end if
257 if (associated(this % coefficient)) then
258 call master_memory % free_memory(storage_size(this % coefficient)/8, size(this % coefficient))
259 deallocate(this % coefficient)
260 this % coefficient => null()
261 end if
262 this % constructed = .false.
263 this % num_elems = 0
264
265 end subroutine destroy_matrix_array
266
267
268 subroutine construct_array (this, capacity)
269 class(matrixarray) :: this
270 integer, intent(in) :: capacity
271 integer :: err
272
273 if (.not. this % constructed) then
274 call master_memory % track_memory(storage_size(this % ij)/8, capacity * 2, 0, 'MATRIXCACHE::MATRIXARRAY::IJ')
275 call master_memory % track_memory(storage_size(this % coefficient)/8, capacity, 0, 'MATRIXCACHE::MATRIXARRAY::COEFF')
276 allocate(this % ij(2, capacity), this % coefficient(capacity), stat = err)
277 this % constructed = .true.
278 end if
279
280 end subroutine construct_array
281
282
283 subroutine clear_array (this)
284 class(matrixarray) :: this
285
286 this % num_elems = 0
287
288 end subroutine clear_array
289
290
291 subroutine insert_into_array (this, i, j, coeff)
292 class(matrixarray) :: this
293 integer, intent(in) :: i, j
294 real(wp), intent(in) :: coeff
295
296 this % num_elems = this % num_elems + 1
297 this % ij(this % num_elems, 1) = i
298 this % ij(this % num_elems, 2) = j
299 this % coefficient(this % num_elems) = coeff
300
301 end subroutine insert_into_array
302
303
304 subroutine get_from_array(this, idx, i, j, coeff)
305 class(matrixarray), intent(in) :: this
306 integer, intent(in) :: idx
307 integer, intent(out) :: i, j
308 real(wp), intent(out) :: coeff
309
310 if (idx > this % num_elems) stop "Matrix Array access segfault!"
311
312 i = this % ij(1, idx)
313 j = this % ij(2, idx)
314 coeff = this % coefficient(idx)
315
316 end subroutine get_from_array
317
318
319 subroutine expand_capacity (this, capacity)
320 class(matrixcache) :: this
321 integer, intent(in) :: capacity
322
323 do while (this % max_capacity < capacity)
324 call this % expand_array
325 end do
326
327 end subroutine expand_capacity
328
329
330 logical function check_bounds (this, i)
331 class(matrixcache), intent(in) :: this
332 integer, intent(in) :: i
333
334 if (i <= 0 .or. i > this % n) then
335 write (stdout, "('MatrixCache::check_bounds - Out of Bounds access', 2i4)") i, this % n
336 stop "MatrixCache::check_bounds - Out of Bounds access"
337 check_bounds = .false.
338 else
339 check_bounds = .true.
340
341 end if
342
343 end function check_bounds
344
345
346 logical function is_empty (this)
347 class(matrixcache), intent(in) :: this
348
349 is_empty = (this % n == 0)
350
351 end function is_empty
352
353
354 subroutine clear (this)
355 class(matrixcache) :: this
356 integer :: ido
357
358 if (allocated(this % matrix_arrays)) this % matrix_arrays(:) % num_elems = 0
359 this % n = 0
360
361 end subroutine clear
362
363
364 integer function get_size (this)
365 class(matrixcache), intent(in) :: this
366
367 get_size = this % n
368
369 end function get_size
370
371
372 subroutine shrink_capacity (this)
373 class(matrixcache) :: this
374 integer :: ido, total_size, shrink_size
375
376 type(matrixarray), allocatable :: temp_matrix(:)
377
378 if (allocated(this % matrix_arrays)) then
379 total_size = size(this % matrix_arrays)
380 shrink_size = min(this % n / this % expand_size + 1, this % num_matrix_chunks)
381
382 if (shrink_size == this % num_matrix_chunks) return
383
384 do ido = shrink_size + 1, total_size
385 call this % matrix_arrays(ido) % destroy
386 end do
387
388 allocate(temp_matrix(shrink_size))
389 call master_memory % track_memory(storage_size(temp_matrix)/8, size(temp_matrix), 0, 'MATRIXCACHE::SHRINK::TEMP')
390 temp_matrix(1:shrink_size) = this % matrix_arrays(1:shrink_size)
391 call master_memory % free_memory(storage_size(this % matrix_arrays)/8, size(this % matrix_arrays))
392 deallocate(this % matrix_arrays)
393
394 allocate(this % matrix_arrays(shrink_size))
395 call master_memory % track_memory(storage_size(this % matrix_arrays)/8, size(this % matrix_arrays), 0, &
396 'MATRIXCACHE::SHRINK::MATRIXARRAYS')
397 this % matrix_arrays(1:shrink_size) = temp_matrix(1:shrink_size)
398 this % max_capacity = shrink_size * this % expand_size
399 this % num_matrix_chunks = shrink_size
400 call master_memory % free_memory(storage_size(temp_matrix)/8, size(temp_matrix))
401 deallocate(temp_matrix)
402 end if
403
404 end subroutine shrink_capacity
405
406
407 subroutine clear_and_shrink (this)
408 class(matrixcache) :: this
409
410 call this % clear
411 call this % shrink_capacity
412
413 end subroutine clear_and_shrink
414
415
416 subroutine destroy_cache (this)
417 class(matrixcache) :: this
418 integer :: num_arrays, ido
419
420 if (allocated(this % matrix_arrays)) then
421 num_arrays = size(this % matrix_arrays)
422
423 do ido = 1, num_arrays
424 call this % matrix_arrays(ido) % destroy
425 end do
426
427 call master_memory % free_memory(storage_size(this % matrix_arrays)/8, size(this % matrix_arrays))
428 deallocate(this % matrix_arrays)
429 end if
430
431 this % constructed = .false.
432
433 end subroutine destroy_cache
434
435
436 subroutine print_matelems (this)
437 class(matrixcache) :: this
438 integer :: labels(8), ido, arrs, jdo, elm
439 real(wp) :: coeff
440
441 write (stdout, "('Outputting Matrix elements....')")
442
443 this % matrix_index = this % n / this % expand_size + 1
444 elm = 0
445
446 do ido = 1, this % num_matrix_chunks
447 write (stdout, *) this % matrix_arrays(ido) % num_elems
448 do jdo = 1, this % matrix_arrays(ido) % num_elems
449 write (stdout, "(2i8,' -- ',D16.8)") this % matrix_arrays(ido) % ij(1:2,jdo), &
450 this % matrix_arrays(ido) % coefficient(jdo)
451 end do
452 end do
453
454 end subroutine print_matelems
455
456
457 subroutine qsort_matelem (this)
458 class(matrixcache) :: this
459
460 call this % check_constructed
461 if (this % n <= 1) return
462 call this % quick_sort_function(1, this % n)
463 !call QsortMatrixElement_ji(this % matrix_elements(1:this % n))
464
465 end subroutine qsort_matelem
466
467
468 recursive subroutine quick_sort_function (this, start_idx, end_idx)
469 class(matrixcache) :: this
470 integer, intent(in) :: start_idx, end_idx
471 integer :: mat_size, iq
472
473 mat_size = end_idx - start_idx + 1
474
475 if (mat_size > 1) then
476 call this % partition_function(iq, start_idx, end_idx)
477 call this % quick_sort_function(start_idx, iq - 1)
478 call this % quick_sort_function(iq, end_idx)
479 end if
480
481 end subroutine quick_sort_function
482
483
484 subroutine partition_function (this, marker, start_idx, end_idx)
485 class(matrixcache) :: this
486 integer, intent(in) :: start_idx, end_idx
487 integer, intent(out) :: marker
488 integer :: i, j, temp_ij(2), x_ij(2), A_ij(2), mat_id_i, mat_id_j, local_index_i, local_index_j
489 real(wp) :: temp_coeff, x_coeff, A_coeff
490
491 call this % get_from_cache(start_idx, x_ij(1), x_ij(2), x_coeff)
492
493 i = start_idx - 1
494 j = end_idx + 1
495
496 do
497 j = j - 1
498 do
499 call this % get_from_cache(j, a_ij(1), a_ij(2), a_coeff)
500 if (a_ij(1) <= x_ij(1)) exit
501 j = j - 1
502 end do
503 i = i + 1
504 do
505 call this % get_from_cache(i, a_ij(1), a_ij(2), a_coeff)
506 if (a_ij(1) >= x_ij(1)) exit
507 i = i + 1
508 end do
509 if (i < j) then
510 ! exchange A(i) and A(j)
511 call this % get_local_mat(i, mat_id_i, local_index_i)
512 call this % get_local_mat(j, mat_id_j, local_index_j)
513
514 temp_ij(:) = this % matrix_arrays(mat_id_i) % ij(:,local_index_i)
515 temp_coeff = this % matrix_arrays(mat_id_i) % coefficient(local_index_i)
516
517 this % matrix_arrays(mat_id_i) % ij(:,local_index_i) &
518 = this % matrix_arrays(mat_id_j) % ij(:,local_index_j)
519 this % matrix_arrays(mat_id_i) % coefficient(local_index_i) &
520 = this % matrix_arrays(mat_id_j) % coefficient(local_index_j)
521
522 this % matrix_arrays(mat_id_j) % ij(:,local_index_j) = temp_ij(:)
523 this % matrix_arrays(mat_id_j) % coefficient(local_index_j) = temp_coeff
524 else if (i == j) then
525 marker = i + 1
526 return
527 else
528 marker = i
529 return
530 end if
531 end do
532
533 end subroutine partition_function
534
535 ! Recursive Fortran 95 quicksort routine adapted to
536 ! sort matrix elements into ascending either ij or ji
537 ! Author: Juli Rew, SCD Consulting (juliana@ucar.edu), 9/03
538 ! Based on algorithm from Cormen et al., Introduction to Algorithms,
539 ! 1997 printing
540
541 ! Made F conformant by Walt Brainerd
542
543 !subroutine construct(this,i,j,k,l,inttype,positron,coefficent)
544 ! class(MatrixElement),intent(inout) :: this
545 ! integer,intent(in) :: i,j,k,l,positron,inttype
546 ! real(wp),intent(in) :: coefficent
547 !
548 ! this%coefficient = coefficient
549 ! call pack4ints64(i,j,k,l,this%integral_label)
550 ! this%positron = positron
551 !
552 ! if(inttype /= ONE_ELECTRON_INTEGRAL .or. inttype /= TWO_ELECTRON_INTEGRAL) then
553 ! write(stdout,"('Error integral type of ',i4,' is not 1 or 2 electron')") inttype
554 ! stop "Integral label is neither 1 or 2 electron"
555 ! else
556 ! this%integral_type = inttype
557 ! endif
558 !
559 !
560 !
561 !
562 !end subroutine
563
564end module matrixcache_module
MPI SCATCI Constants module.
integer, parameter default_expand_size
Default expansion size for the cache.