• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

eT-program / eT / 22312

26 Feb 2025 10:33PM UTC coverage: 88.481%. Remained the same
22312

push

gitlab-ci

Merge branch 'speedup_cube_generation' into 'development'

Speedup .cube generation by ~260x

See merge request eT-program/eT!1543

28 of 30 new or added lines in 2 files covered. (93.33%)

1 existing line in 1 file now uncovered.

53549 of 60520 relevant lines covered (88.48%)

3036524.96 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.48
/src/memory/memory_manager_class.F90
1

2
! eT - a coupled cluster program
3
! Copyright (C) 2016-2024 the authors of eT
4
!
5
! eT is free software: you can redistribute it and/or modify
6
! it under the terms of the GNU General Public License as published by
7
! the Free Software Foundation, either version 3 of the License, or
8
! (at your option) any later version.
9
!
10
! eT is distributed in the hope that it will be useful,
11
! but WITHOUT ANY WARRANTY; without even the implied warranty of
12
! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
! GNU General Public License for more details.
14
!
15
! You should have received a copy of the GNU General Public License
16
! along with this program. If not, see <https://www.gnu.org/licenses/>.
17

18

19
module memory_manager_class
20

21
   !!
22
   !! Memory manager class module
23
   !! Written by Sarai D. Folkestad and Eirik F. Kjønstad, Dec 2017
24
   !!
25
   !! The memory manager class handles the memory used by the model calculation,
26
   !! and there is an object called 'mem' in the wavefunction object of this class.
27
   !!
28
   !! To account for the available memory, all large arrays must be allocated and deallocated
29
   !! using the memory manager. A typical usage of the 'mem' object is as follows:
30
   !!
31
   !!    real(dp), dimension(:,:), allocatable :: array -> declares an allocatable array
32
   !!
33
   !!    call mem%alloc(array, M, N)   -> allocates the array of dimension M x N
34
   !!
35
   !!    ... Do stuff with the array
36
   !!
37
   !!    call mem%dealloc(array, M, N) -> deallocates the array of dimension M x N
38
   !!
39
   !! Analogous calls are made to make one, three and four dimensional tensors as well,
40
   !! e.g. call mem%alloc(X, M, N, K) for an M x N x K tensor.
41
   !!
42
   !! Note: Large arrays MUST always be allocated using the memory manager object. Small arrays,
43
   !! integers, strings, etc., which use a negligible amount of memory, do not have to pass
44
   !! through the memory manager.
45
   !!
46
   !! The 'alloc' and 'dealloc' routines allow the memory manager keep track of the
47
   !! the memory available at a given time. From the specified total memory, the class
48
   !! can then set the batching information for a batching index (see the batching
49
   !! index class) or set of such indices (see the batch_setup routines below).
50
   !!
51

52
   use parameters
122✔
53
   use global_out,           only: output
54
   use batching_index_class, only: batching_index
55
   use memory_tracker_class, only: memory_tracker
56
   use array_initialization, only: zero_array, zero_array_complex
57
   use array_initialization, only: zero_array_int, zero_array_int32, zero_array_int64
58

59
   ! Debug option:
60
   ! Require that batch setup always gives batching
61

62
#ifdef _FORCED_BATCHING
63
   logical, parameter, private :: force_batch = .true.
64
#else
65
   logical, parameter, private :: force_batch = .false.
66
#endif
67

68
   ! Class definition
69

70
   type :: memory_manager
71

72
      ! The total amount of memory specified by user (standard: 8 GB)
73

74
      integer(i64), private :: total
75

76
      ! The amount of memory currently available, based on the arrays currently allocated
77
      ! (memory used by objects and local variables are not included in this estimate)
78

79
      integer(i64), private :: available
80

81
      ! Maximum amount of memory used at the same time
82

83
      integer(i64), private :: max_used
84

85
      ! Unit for memory, default is GB
86

87
      character(len=2), private :: units
88

89
      ! Batch memory tracking
90

91
      logical, private :: batching_on
92
      type(memory_tracker), allocatable :: batch_mem_tracker
93

94
   contains
95

96
      ! Check if there are memory leaks - on exit of program
97

98
      procedure, public :: check_for_leak => check_for_leak_memory_manager
99

100
      ! Printing
101

102
      procedure, public :: print_settings  => print_settings_memory_manager
103
      procedure, public :: print_max_used  => print_max_used_memory_manager
104
      procedure, public :: print_available => print_available_memory_manager
105

106
      procedure, public :: get_available   => get_available_memory_manager
107

108
      procedure, nopass, public :: get_memory_as_character &
109
                                => get_memory_as_character_memory_manager
110

111
      ! Routines for batching
112

113
      generic, public :: batch_setup => batch_setup_1, &
114
                                        batch_setup_2, &
115
                                        batch_setup_3
116

117
      procedure, public :: batch_finalize &
118
                        => batch_finalize_memory_manager
119

120
      ! Allocation and deallocation routines for arrays
121

122
      generic, public :: alloc => alloc_r_1,       &
123
                                  alloc_r_2,       &
124
                                  alloc_r_3,       &
125
                                  alloc_r_4,       &
126
                                  alloc_r_5,       &
127
                                  alloc_r_6,       &
128
                                  alloc_c_1,       &
129
                                  alloc_c_2,       &
130
                                  alloc_c_3,       &
131
                                  alloc_c_4,       &
132
                                  alloc_i64_1,     &
133
                                  alloc_i64_2,     &
134
                                  alloc_i64_3,     &
135
                                  alloc_i32_1,     &
136
                                  alloc_i32_2,     &
137
                                  alloc_i32_3,     &
138
                                  alloc_l_1,       &
139
                                  alloc_l_2,       &
140
                                  alloc_i_2_ranges,&
141
                                  alloc_char_1
142

143
      generic, public :: dealloc => dealloc_real,           &
144
                                    dealloc_complex,        &
145
                                    dealloc_64bit_integer,  &
146
                                    dealloc_32bit_integer,  &
147
                                    dealloc_logical,        &
148
                                    dealloc_char
149

150

151
      procedure, private :: alloc_r_1
152
      procedure, private :: alloc_r_2
153
      procedure, private :: alloc_r_3
154
      procedure, private :: alloc_r_4
155
      procedure, private :: alloc_r_5
156
      procedure, private :: alloc_r_6
157
      procedure, private :: alloc_c_1
158
      procedure, private :: alloc_c_2
159
      procedure, private :: alloc_c_3
160
      procedure, private :: alloc_c_4
161
      procedure, private :: alloc_i64_1
162
      procedure, private :: alloc_i64_2
163
      procedure, private :: alloc_i64_3
164
      procedure, private :: alloc_i32_1
165
      procedure, private :: alloc_i32_2
166
      procedure, private :: alloc_i32_3
167
      procedure, private :: alloc_l_1
168
      procedure, private :: alloc_l_2
169
      procedure, private :: alloc_i_2_ranges
170
      procedure, private :: alloc_char_1
171

172
      procedure, private :: dealloc_real
173
      procedure, private :: dealloc_complex
174
      procedure, private :: dealloc_64bit_integer
175
      procedure, private :: dealloc_32bit_integer
176
      procedure, private :: dealloc_logical
177
      procedure, private :: dealloc_char
178

179
      procedure, private :: batch_setup_1
180
      procedure, private :: batch_setup_2
181
      procedure, private :: batch_setup_3
182

183
      procedure, private :: initialize_batching_tracker
184

185
      procedure, private :: update_memory_after_alloc
186
      procedure, private :: update_memory_after_dealloc
187

188
      procedure, nopass, private :: print_allocation_error
189
      procedure, nopass, private :: print_deallocation_error
190

191
   end type memory_manager
192

193

194
   interface memory_manager
195

196
      procedure :: new_memory_manager
197

198
   end interface memory_manager
199

200
   ! Main memory object
201

202
   type(memory_manager) :: mem
203

204
contains
205

206

207
   function new_memory_manager(total, units) result(mem)
3,169✔
208
      !!
209
      !! Written by Sarai D. Folkestad and Eirik F. Kjønstad, Dec 2017
210
      !!
211
      !! Creates the memory manager object and sets the
212
      !! total and initial available memory.
213
      !!
214
      implicit none
215

216
      type(memory_manager) :: mem
217

218
      integer(i64), intent(in) :: total
219

220
      character(len=*), intent(in) :: units
221

222
      ! Set standard and read settings
223
      !
224
      ! Default is 8 GB
225

226
      mem%total = total
3,169✔
227
      mem%units = trim(units)
3,169✔
228

229
      ! Convert from current unit to B
230

231
      if (mem%units == 'gb') then
3,169✔
232

233
         mem%total =  mem%total*1000000000
3,151✔
234

235
      elseif (mem%units == 'mb') then
18✔
236

237
         mem%total =  mem%total*1000000
6✔
238

239
      elseif (mem%units == 'kb') then
12✔
240

241
         mem%total =  mem%total*1000
6✔
242

243
      elseif (trim(mem%units) == 'b') then
6✔
244

245
         ! Do nothing
246

247
      else
248

249
         call output%error_msg('did not recognize the memory unit specified in input')
×
250

251
      endif
252

253
      mem%available = mem%total
3,169✔
254
      mem%max_used = mem%total - mem%available
3,169✔
255

256
      mem%batching_on = .false.
3,169✔
257

258
      call mem%print_settings()
3,169✔
259

260
   end function new_memory_manager
3,169✔
261

262

263
   subroutine check_for_leak_memory_manager(mem)
3,139✔
264
      !!
265
      !! Written by Eirik F. Kjønstad, Apr 2019
266
      !!
267
      !! Issues a warning if there has been a leak since the
268
      !! the memory manager was prepared. Should only be called
269
      !! at the end of the program, when all arrays that were
270
      !! allocated since mem%prepare() should have been deallocated.
271
      !!
272
      implicit none
273

274
      class(memory_manager), intent(in) :: mem
275

276
      character(len=200) :: difference_string
277

278
      if (mem%available .ne. mem%total) then
3,139✔
279

280
         call output%printf('m', 'Mismatch in memory according to eT and &
281
                            &specified on input:', fs='(/t3,a)')
6✔
282

283
         call output%printf('m', 'Memory available (eT):    (a0)', &
284
                            chars=[mem%get_memory_as_character(mem%available,.true.)], fs='(/t6,a)')
12✔
285

286
         call output%printf('m', 'Memory available (input): (a0)', &
287
                            chars=[mem%get_memory_as_character(mem%total,.true.)], fs='(t6,a)')
12✔
288

289

290
         difference_string = mem%get_memory_as_character(mem%total-mem%available,.true.)
6✔
291
         call output%printf('m', 'Difference:               (a0)', &
292
                            chars=[trim(difference_string)], fs='(t6,a)')
18✔
293

294
         call output%error_msg('Deallocations are missing or specified with &
295
                                 &incorrect dimensionalities.')
6✔
296

297
      endif
298

299
   end subroutine check_for_leak_memory_manager
3,133✔
300

301

302
   pure function get_available_memory_manager(mem) result(memory)
139,182✔
303
      !!
304
      !! Get available
305
      !! Written by Eirik F. Kjønstad, Jan 2019
306
      !!
307
      implicit none
308

309
      class(memory_manager), intent(in) :: mem
310

311
      integer(i64) :: memory
312

313
      memory = mem%available
139,182✔
314

315
   end function get_available_memory_manager
139,182✔
316

317

318
   pure function get_memory_as_character_memory_manager(input_mem, all_digits) result(memory)
9,489✔
319
      !!
320
      !! Get available memory as character
321
      !! Written by Alexander C. Paul, Oct 2019
322
      !!
323
      !! Receives an 8 byte integer containing the memory in byte.
324
      !! Returns character containing the same the number with a reasonable unit
325
      !!
326
      !! If all_digits is .true. the full memory is returned in bytes
327
      !!
328
      implicit none
329

330
      integer(i64), intent(in) :: input_mem
331

332
      logical, intent(in), optional :: all_digits
333

334
      character(len=17) :: memory
335

336
      logical :: all_digits_local
337

338
      ! Print all digits? (i.e. give memory in B)
339

340
      all_digits_local = .false.
341
      if (present(all_digits)) all_digits_local = all_digits
9,489✔
342

343
      if (all_digits_local) then
48✔
344

345
         write(memory,'(i0, a)') input_mem, ' B'
48✔
346
         memory = trim(adjustl(memory))
48✔
347

348
      else if (abs(input_mem) .lt. 1d6) then
9,441✔
349

350
         write(memory,'(f10.3, a)') dble(input_mem)/1.0d3, ' KB'
2,174✔
351
         memory = trim(adjustl(memory))
2,174✔
352

353
      else if (abs(input_mem) .lt. 1d9) then
7,267✔
354

355
         write(memory,'(f10.6, a)') dble(input_mem)/1.0d6, ' MB'
4,098✔
356
         memory = trim(adjustl(memory))
4,098✔
357

358
      else if (abs(input_mem) .lt. 1d12) then
3,169✔
359

360
         write(memory,'(f10.6, a)') dble(input_mem)/1.0d9, ' GB'
3,169✔
361
         memory = trim(adjustl(memory))
3,169✔
362

363
      else if (abs(input_mem) .lt. 1d15) then
×
364

365
         write(memory,'(f13.6, a)') dble(input_mem)/1.0d12, ' TB'
×
366
         memory = trim(adjustl(memory))
×
367

368
      end if
369

370
   end function get_memory_as_character_memory_manager
9,489✔
371

372

373
   subroutine print_available_memory_manager(mem)
6✔
374
      !!
375
      !! Written by Eirik F. Kjønstad, Jan 2019
376
      !!
377
      implicit none
378

379
      class(memory_manager), intent(in) :: mem
380

381
      call output%printf('m', 'Currently available memory: (a0)', &
382
                         chars=[mem%get_memory_as_character(mem%available, .true.)])
12✔
383

384
   end subroutine print_available_memory_manager
6✔
385

386

387
   subroutine print_max_used_memory_manager(mem)
3,133✔
388
      !!
389
      !! Written by Alexander C. Paul, May 2020
390
      !!
391
      implicit none
392

393
      class(memory_manager), intent(in) :: mem
394

395
      call output%printf('n', 'Peak memory usage during the execution of eT: (a0)', &
396
                         chars=[mem%get_memory_as_character(mem%max_used)], fs='(/t3,a)')
6,266✔
397

398
   end subroutine print_max_used_memory_manager
3,133✔
399

400

401
   subroutine initialize_memory_real(array, N)
139,842,008✔
402
      !!
403
      !! Written by Marcus T. Lexander, 2023
404
      !!
405
      use, intrinsic :: ieee_arithmetic, only: IEEE_Value, IEEE_QUIET_NAN
406
      use warning_suppressor, only: do_nothing
407

408
      implicit none
409

410
      integer, intent(in) :: N
411

412
      real(dp), dimension(N), intent(inout) :: array
413

414
      real(dp) :: nan
415

416
#ifdef INITIALIZE_NAN
417
      nan = IEEE_Value(nan, IEEE_QUIET_NAN)
418

419
      call dscal(N, nan, array, 1)
420
#else
421
      call do_nothing(array)
69,921,004✔
422
      call do_nothing(nan)
69,921,004✔
423
#endif
424

425
   end subroutine initialize_memory_real
69,921,004✔
426

427

428
   subroutine initialize_memory_complex(array, N)
3,051,062✔
429
      !!
430
      !! Written by Marcus T. Lexander, 2023
431
      !!
432
      use, intrinsic :: ieee_arithmetic, only: IEEE_Value, IEEE_QUIET_NAN
69,921,004✔
433
      use warning_suppressor, only: do_nothing
434

435
      implicit none
436

437
      integer, intent(in) :: N
438

439
      complex(dp), dimension(N), intent(inout) :: array
440

441
      real(dp) :: nan
442

443
#ifdef INITIALIZE_NAN
444
      nan = IEEE_Value(nan, IEEE_QUIET_NAN)
445

446
      call zscal(N, cmplx(nan, nan, dp), array, 1)
447
#else
448
      call do_nothing(array)
1,525,531✔
449
      call do_nothing(nan)
1,525,531✔
450
#endif
451

452
   end subroutine initialize_memory_complex
1,525,531✔
453

454

455
   subroutine alloc_r_1(mem, array, M, set_zero)
6,638,653✔
456
      !!
457
      !! Written by Rolf H. Myhre, January 2019
458
      !!
459
      !! Allocates a one dimensional double precision array and updates the available
460
      !! memory accordingly.
461
      !!
462
      !! set_zero: optional initialize array to zero
463
      !!
464
      implicit none
465

466
      class(memory_manager) :: mem
467

468
      real(dp), dimension(:), allocatable :: array
469
      logical, intent(in), optional :: set_zero
470

471
      integer, intent(in) :: M ! Dimension of array that is being allocated
472

473
      integer(i64) :: size_array ! Total size of array (M)
474
      integer :: error = 0
475
      logical :: set_zero_ = .false.
476

477
      character(len=100) :: error_msg
478

479
      size_array = M
6,638,653✔
480

481
      allocate(array(M), stat=error, errmsg=error_msg)
11,122,994✔
482

483
      if (error .ne. 0) call mem%print_allocation_error(size_array, error_msg)
6,638,653✔
484

485
      call initialize_memory_real(array, int(size_array))
6,638,653✔
486

487
      if (present(set_zero)) set_zero_ = set_zero
6,638,653✔
488
      if (set_zero_) call zero_array(array, int(size_array))
6,638,653✔
489

490
      call mem%update_memory_after_alloc(size_array, dp)
6,638,653✔
491

492
   end subroutine alloc_r_1
1,525,531✔
493

494

495
   subroutine alloc_r_2(mem, array, M, N, set_zero)
23,051,496✔
496
      !!
497
      !! Written by Sarai D. Folkestad and Eirik F. Kjønstad, Dec 2017
498
      !!
499
      !! Allocates a two dimensional double precision array and updates the available
500
      !! memory accordingly.
501
      !!
502
      !! set_zero: optional initialize array to zero
503
      !!
504
      implicit none
505

506
      class(memory_manager) :: mem
507

508
      real(dp), dimension(:,:), allocatable :: array
509
      logical, intent(in), optional :: set_zero
510

511
      integer, intent(in) :: M, N ! First and second dimension of array that is being allocated
512

513
      integer(i64) :: size_array ! Total size of array (M*N)
514
      integer :: error = 0
515
      logical :: set_zero_ = .false.
516

517
      character(len=100) :: error_msg
518

519
      size_array = M*N
23,051,496✔
520

521
      allocate(array(M,N), stat=error, errmsg=error_msg)
61,580,402✔
522

523
      if (error .ne. 0) then
23,051,496✔
524
         call mem%print_allocation_error(size_array, error_msg)
×
525
      endif
526

527
      call initialize_memory_real(array, int(size_array))
23,051,496✔
528

529
      if (present(set_zero)) set_zero_ = set_zero
23,051,496✔
530
      if (set_zero_) call zero_array(array, int(size_array))
23,051,496✔
531

532
      call mem%update_memory_after_alloc(size_array, dp)
23,051,496✔
533

534
   end subroutine alloc_r_2
23,051,496✔
535

536

537
   subroutine alloc_r_3(mem, array, M, N, O, set_zero)
19,669,753✔
538
      !!
539
      !! Written by Rolf H. Myhre, January 2019
540
      !!
541
      !! Allocates a three dimensional double precision array and updates the available
542
      !! memory accordingly.
543
      !!
544
      !! set_zero: optional initialize array to zero
545
      !!
546
      implicit none
547

548
      class(memory_manager) :: mem
549

550
      real(dp), dimension(:,:,:), allocatable :: array
551
      logical, intent(in), optional :: set_zero
552

553
      integer, intent(in) :: M, N, O ! First, second and third dimension of array
554

555
      integer(i64) :: size_array ! Total size of array (M*N*O)
556
      integer :: error = 0
557
      logical :: set_zero_ = .false.
558

559
      character(len=100) :: error_msg
560

561
      size_array = M*N*O
19,669,753✔
562

563
      allocate(array(M,N,O), stat=error, errmsg=error_msg)
72,139,055✔
564

565
      if (error .ne. 0) then
19,669,753✔
566
         call mem%print_allocation_error(size_array, error_msg)
×
567
      endif
568

569
      call initialize_memory_real(array, int(size_array))
19,669,753✔
570

571
      if (present(set_zero)) set_zero_ = set_zero
19,669,753✔
572
      if (set_zero_) call zero_array(array, int(size_array))
19,669,753✔
573

574
      call mem%update_memory_after_alloc(size_array, dp)
19,669,753✔
575

576
   end subroutine alloc_r_3
19,669,753✔
577

578

579
   subroutine alloc_r_4(mem, array, M, N, O, P, set_zero)
20,164,698✔
580
      !!
581
      !! Written by Rolf H. Myhre, January 2019
582
      !!
583
      !! Allocates a four dimensional double precision array and updates the available
584
      !! memory accordingly.
585
      !!
586
      !! set_zero: optional initialize array to zero
587
      !!
588
      implicit none
589

590
      class(memory_manager) :: mem
591

592
      real(dp), dimension(:,:,:,:), allocatable :: array
593
      logical, intent(in), optional :: set_zero
594

595
      integer, intent(in) :: M, N, O, P ! First, second, third and fourth dimension of array
596

597
      integer(i64) :: size_array ! Total size of array (M*N*O*P)
598
      integer :: error = 0
599
      logical :: set_zero_ = .false.
600

601
      character(len=100) :: error_msg
602

603
      size_array = M*N*O*P
20,164,698✔
604

605
      allocate(array(M,N,O,P), stat=error, errmsg=error_msg)
94,115,860✔
606

607
      if (error .ne. 0) then
20,164,698✔
608
         call mem%print_allocation_error(size_array, error_msg)
×
609
      endif
610

611
      call initialize_memory_real(array, int(size_array))
20,164,698✔
612

613
      if (present(set_zero)) set_zero_ = set_zero
20,164,698✔
614
      if (set_zero_) call zero_array(array, int(size_array))
20,164,698✔
615

616
      call mem%update_memory_after_alloc(size_array, dp)
20,164,698✔
617

618
   end subroutine alloc_r_4
20,164,698✔
619

620

621
   subroutine alloc_r_5(mem, array, M, N, O, P, Q, set_zero)
7,520✔
622
      !!
623
      !! Written by Rolf H. Myhre, January 2019
624
      !!
625
      !! Allocates a five dimensional double precision array and updates the available
626
      !! memory accordingly.
627
      !!
628
      !! set_zero: optional initialize array to zero
629
      !!
630
      implicit none
631

632
      class(memory_manager) :: mem
633

634
      real(dp), dimension(:,:,:,:,:), allocatable :: array
635
      logical, intent(in), optional :: set_zero
636

637
      integer, intent(in) :: M, N, O, P, Q ! First, second, third, fourth, fifth dimension of array
638

639
      integer(i64) :: size_array ! Total size of array (M*N*O*P*Q)
640
      integer :: error = 0
641
      logical :: set_zero_ = .false.
642

643
      character(len=100) :: error_msg
644

645
      size_array = M*N*O*P*Q
7,520✔
646

647
      allocate(array(M,N,O,P,Q), stat=error, errmsg=error_msg)
42,999✔
648

649
      if (error .ne. 0) then
7,520✔
650
         call mem%print_allocation_error(size_array, error_msg)
×
651
      endif
652

653
      call initialize_memory_real(array, int(size_array))
7,520✔
654

655
      if (present(set_zero)) set_zero_ = set_zero
7,520✔
656
      if (set_zero_) call zero_array(array, int(size_array))
7,520✔
657

658
      call mem%update_memory_after_alloc(size_array, dp)
7,520✔
659

660
   end subroutine alloc_r_5
7,520✔
661

662

663
   subroutine alloc_r_6(mem, array, M, N, O, P, Q, R, set_zero)
388,884✔
664
      !!
665
      !! Alloc (memory manager)
666
      !! Written by Rolf H. Myhre, January 2019
667
      !!
668
      !! Allocates a six dimensional double precision array and updates the available
669
      !! memory accordingly.
670
      !!
671
      !! set_zero: optional initialize array to zero
672
      !!
673
      implicit none
674

675
      class(memory_manager) :: mem
676

677
      real(dp), dimension(:,:,:,:,:,:), allocatable :: array
678
      logical, intent(in), optional :: set_zero
679

680
      integer, intent(in) :: M, N, O, P, Q, R ! First, second, third, fourth, fifth, sixth dimension of array
681

682
      integer(i64) :: size_array ! Total size of array (M*N*O*P*Q)
683
      integer :: error = 0
684
      logical :: set_zero_ = .false.
685

686
      character(len=100) :: error_msg
687

688
      size_array = M*N*O*P*Q*R
388,884✔
689

690
      allocate(array(M,N,O,P,Q,R), stat=error, errmsg=error_msg)
2,592,560✔
691

692
      if (error .ne. 0) then
388,884✔
693
         call mem%print_allocation_error(size_array, error_msg)
×
694
      endif
695

696
      call initialize_memory_real(array, int(size_array))
388,884✔
697

698
      if (present(set_zero)) set_zero_ = set_zero
388,884✔
699
      if (set_zero_) call zero_array(array, int(size_array))
388,884✔
700

701
      call mem%update_memory_after_alloc(size_array, dp)
388,884✔
702

703
   end subroutine alloc_r_6
388,884✔
704

705

706
   subroutine alloc_c_1(mem, array, M, set_zero)
123,042✔
707
      !!
708
      !! Written by Rolf H. Myhre, January 2019
709
      !!
710
      !! Allocates a one dimensional double precision array and updates the available
711
      !! memory accordingly.
712
      !!
713
      !! set_zero: optional initialize array to zero
714
      !!
715
      implicit none
716

717
      class(memory_manager) :: mem
718

719
      complex(dp), dimension(:), allocatable :: array
720
      logical, intent(in), optional :: set_zero
721

722
      integer, intent(in) :: M ! Dimension of array that is being allocated
723

724
      integer(i64) :: size_array ! Total size of array (M)
725
      integer :: error = 0
726
      logical :: set_zero_ = .false.
727

728
      character(len=100) :: error_msg
729

730
      size_array = M
123,042✔
731

732
      allocate(array(M), stat=error, errmsg=error_msg)
205,196✔
733

734
      if (error .ne. 0) then
123,042✔
735
         call mem%print_allocation_error(size_array, error_msg)
×
736
      endif
737

738
      call initialize_memory_complex(array, int(size_array))
123,042✔
739

740
      if (present(set_zero)) set_zero_ = set_zero
123,042✔
741
      if (set_zero_) call zero_array_complex(array, int(size_array))
123,042✔
742

743
      call mem%update_memory_after_alloc(size_array, 2*dp)
123,042✔
744

745
   end subroutine alloc_c_1
123,042✔
746

747

748
   subroutine alloc_c_2(mem, array, M, N, set_zero)
284,646✔
749
      !!
750
      !! Written by Sarai D. Folkestad and Eirik F. Kjønstad, Dec 2017
751
      !!
752
      !! Allocates a two dimensional double precision array and updates the available
753
      !! memory accordingly.
754
      !!
755
      !! set_zero: optional initialize array to zero
756
      !!
757
      implicit none
758

759
      class(memory_manager) :: mem
760

761
      complex(dp), dimension(:,:), allocatable :: array
762
      logical, intent(in), optional :: set_zero
763

764
      integer, intent(in) :: M, N ! First and second dimension of array that is being allocated
765

766
      integer(i64) :: size_array ! Total size of array (M*N)
767
      integer :: error = 0
768
      logical :: set_zero_ = .false.
769

770
      character(len=100) :: error_msg
771

772
      size_array = M*N
284,646✔
773

774
      allocate(array(M,N), stat=error, errmsg=error_msg)
759,073✔
775

776
      if (error .ne. 0) then
284,646✔
777
         call mem%print_allocation_error(size_array, error_msg)
×
778
      endif
779

780
      call initialize_memory_complex(array, int(size_array))
284,646✔
781

782
      if (present(set_zero)) set_zero_ = set_zero
284,646✔
783
      if (set_zero_) call zero_array_complex(array, int(size_array))
284,646✔
784

785
      call mem%update_memory_after_alloc(size_array, 2*dp)
284,646✔
786

787
   end subroutine alloc_c_2
284,646✔
788

789

790
   subroutine alloc_c_3(mem, array, M, N, O, set_zero)
532,421✔
791
      !!
792
      !! Written by Rolf H. Myhre, January 2019
793
      !!
794
      !! Allocates a three dimensional double precision array and updates the available
795
      !! memory accordingly.
796
      !!
797
      !! set_zero: optional initialize array to zero
798
      !!
799
      implicit none
800

801
      class(memory_manager) :: mem
802

803
      complex(dp), dimension(:,:,:), allocatable :: array
804

805
      integer, intent(in) :: M, N, O ! First, second and third dimension of array
806
      logical, intent(in), optional :: set_zero
807

808
      integer(i64) :: size_array ! Total size of array (M*N*O)
809
      integer :: error = 0
810
      logical :: set_zero_ = .false.
811

812
      character(len=100) :: error_msg
813

814
      size_array = M*N*O
532,421✔
815

816
      allocate(array(M,N,O), stat=error, errmsg=error_msg)
1,953,448✔
817

818
      if (error .ne. 0) then
532,421✔
819
         call mem%print_allocation_error(size_array, error_msg)
×
820
      endif
821

822
      call initialize_memory_complex(array, int(size_array))
532,421✔
823

824
      if (present(set_zero)) set_zero_ = set_zero
532,421✔
825
      if (set_zero_) call zero_array_complex(array, int(size_array))
532,421✔
826

827
      call mem%update_memory_after_alloc(size_array, 2*dp)
532,421✔
828

829
   end subroutine alloc_c_3
532,421✔
830

831

832
   subroutine alloc_c_4(mem, array, M, N, O, P, set_zero)
585,422✔
833
      !!
834
      !! Written by Rolf H. Myhre, January 2019
835
      !!
836
      !! Allocates a four dimensional double precision array and updates the available
837
      !! memory accordingly.
838
      !!
839
      !! set_zero: optional initialize array to zero
840
      !!
841
      implicit none
842

843
      class(memory_manager) :: mem
844

845
      complex(dp), dimension(:,:,:,:), allocatable :: array
846
      logical, intent(in), optional :: set_zero
847

848
      integer, intent(in) :: M, N, O, P ! First, second, third and fourth dimension of array
849

850
      integer(i64) :: size_array ! Total size of array (M*N*O*P)
851
      integer :: error = 0
852
      logical :: set_zero_ = .false.
853

854
      character(len=100) :: error_msg
855

856
      size_array = M*N*O*P
585,422✔
857

858
      allocate(array(M,N,O,P), stat=error, errmsg=error_msg)
2,732,091✔
859

860
      if (error .ne. 0) then
585,422✔
861
         call mem%print_allocation_error(size_array, error_msg)
×
862
      endif
863

864
      call initialize_memory_complex(array, int(size_array))
585,422✔
865

866
      if (present(set_zero)) set_zero_ = set_zero
585,422✔
867
      if (set_zero_) call zero_array_complex(array, int(size_array))
585,422✔
868

869
      call mem%update_memory_after_alloc(size_array, 2*dp)
585,422✔
870

871
   end subroutine alloc_c_4
585,422✔
872

873

874
   subroutine alloc_i64_1(mem, array, M, set_zero)
1,711,389✔
875
      !!
876
      !! Written by Rolf H. Myhre, January 2019
877
      !!
878
      !! Allocates a one dimensional integer array and updates the available
879
      !! memory accordingly.
880
      !!
881
      !! set_zero: optional initialize array to zero
882
      !!
883
      implicit none
884

885
      class(memory_manager) :: mem
886

887
      integer(i64), dimension(:), allocatable :: array
888
      logical, intent(in), optional :: set_zero
889

890
      integer, intent(in) :: M ! Dimension of array
891

892
      integer(i64) :: size_array ! Total size of array (M)
893
      integer :: error = 0
894
      logical :: set_zero_ = .false.
895

896
      character(len=100) :: error_msg
897

898
      size_array = M
1,711,389✔
899

900
      allocate(array(M), stat=error, errmsg=error_msg)
3,422,778✔
901

902
      if (error .ne. 0) then
1,711,389✔
903
         call mem%print_allocation_error(size_array, error_msg)
×
904
      endif
905

906
      if (present(set_zero)) set_zero_ = set_zero
1,711,389✔
907
      if (set_zero_) call zero_array_int64(array, int(size_array))
1,711,389✔
908

909
      call mem%update_memory_after_alloc(size_array, 8)
1,711,389✔
910

911
   end subroutine alloc_i64_1
1,711,389✔
912

913

914
   subroutine alloc_i64_2(mem, array, M, N, set_zero)
372,970✔
915
      !!
916
      !! Written by Sarai D. Folkestad and Eirik F. Kjønstad, Dec 2017
917
      !!
918
      !! Allocates a two dimensional integer array and updates the available
919
      !! memory accordingly.
920
      !!
921
      !! set_zero: optional initialize array to zero
922
      !!
923
      implicit none
924

925
      class(memory_manager) :: mem
926

927
      integer(i64), dimension(:,:), allocatable :: array
928
      logical, intent(in), optional :: set_zero
929

930
      integer, intent(in) :: M, N ! First and second dimension of array
931

932
      integer(i64) :: size_array ! Total size of array (M*N)
933
      integer :: error = 0
934
      logical :: set_zero_ = .false.
935

936
      character(len=100) :: error_msg
937

938
      size_array = M*N
372,970✔
939

940
      allocate(array(M,N), stat=error, errmsg=error_msg)
1,118,894✔
941

942
      if (error .ne. 0) then
372,970✔
943
         call mem%print_allocation_error(size_array, error_msg)
×
944
      endif
945

946
      if (present(set_zero)) set_zero_ = set_zero
372,970✔
947
      if (set_zero_) call zero_array_int64(array, int(size_array))
372,970✔
948

949
      call mem%update_memory_after_alloc(size_array, 8)
372,970✔
950

951
   end subroutine alloc_i64_2
372,970✔
952

953

954
   subroutine alloc_i64_3(mem, array, M, N, K, set_zero)
416✔
955
      !!
956
      !! Written by Sarai D. Folkestad and Eirik F. Kjønstad, Dec 2017
957
      !!
958
      !! Allocates a three dimensional integer array and updates the available
959
      !! memory accordingly.
960
      !!
961
      !! set_zero: optional initialize array to zero
962
      !!
963
      implicit none
964

965
      class(memory_manager) :: mem
966

967
      integer(i64), dimension(:,:,:), allocatable :: array
968
      logical, intent(in), optional :: set_zero
969

970
      integer, intent(in) :: M, N, K ! First and second dimension of array
971

972
      integer(i64) :: size_array ! Total size of array (M*N*K)
973
      integer :: error = 0
974
      logical :: set_zero_ = .false.
975

976
      character(len=100) :: error_msg
977

978
      size_array = M*N*K
416✔
979

980
      allocate(array(M,N,K), stat=error, errmsg=error_msg)
1,648✔
981

982
      if (error .ne. 0) then
416✔
983
         call mem%print_allocation_error(size_array, error_msg)
×
984
      endif
985

986
      if (present(set_zero)) set_zero_ = set_zero
416✔
987
      if (set_zero_) call zero_array_int64(array, int(size_array))
416✔
988

989
      call mem%update_memory_after_alloc(size_array, 8)
416✔
990

991
   end subroutine alloc_i64_3
416✔
992

993

994
   subroutine alloc_i32_1(mem, array, M, set_zero)
800,793✔
995
      !!
996
      !! Written by Rolf H. Myhre, January 2019
997
      !!
998
      !! Allocates a one dimensional integer array and updates the available
999
      !! memory accordingly.
1000
      !!
1001
      !! set_zero: optional initialize array to zero
1002
      !!
1003
      implicit none
1004

1005
      class(memory_manager) :: mem
1006

1007
      integer(i32), dimension(:), allocatable :: array
1008
      logical, intent(in), optional :: set_zero
1009

1010
      integer, intent(in) :: M ! Dimension of array
1011

1012
      integer(i64) :: size_array ! Total size of array (M)
1013
      integer :: error = 0
1014
      logical :: set_zero_ = .false.
1015

1016
      character(len=100) :: error_msg
1017

1018
      size_array = M
800,793✔
1019

1020
      allocate(array(M), stat=error, errmsg=error_msg)
800,793✔
1021

1022
      if (error .ne. 0) then
800,793✔
1023
         call mem%print_allocation_error(size_array, error_msg)
×
1024
      endif
1025

1026
      if (present(set_zero)) set_zero_ = set_zero
800,793✔
1027
      if (set_zero_) call zero_array_int32(array, int(size_array))
800,793✔
1028

1029
      call mem%update_memory_after_alloc(size_array, 4)
800,793✔
1030

1031
   end subroutine alloc_i32_1
800,793✔
1032

1033

1034
   subroutine alloc_i32_2(mem, array, M, N, set_zero)
225,158✔
1035
      !!
1036
      !! Written by Sarai D. Folkestad and Eirik F. Kjønstad, Dec 2017
1037
      !!
1038
      !! Allocates a two dimensional integer array and updates the available
1039
      !! memory accordingly.
1040
      !!
1041
      !! set_zero: optional initialize array to zero
1042
      !!
1043
      implicit none
1044

1045
      class(memory_manager) :: mem
1046

1047
      integer(i32), dimension(:,:), allocatable :: array
1048
      logical, intent(in), optional :: set_zero
1049

1050
      integer, intent(in) :: M, N ! First and second dimension of array
1051

1052
      integer(i64) :: size_array ! Total size of array (M*N)
1053
      integer :: error = 0
1054
      logical :: set_zero_ = .false.
1055

1056
      character(len=100) :: error_msg
1057

1058
      size_array = M*N
225,158✔
1059

1060
      allocate(array(M,N), stat=error, errmsg=error_msg)
304,502✔
1061

1062
      if (error .ne. 0) then
225,158✔
1063
         call mem%print_allocation_error(size_array, error_msg)
×
1064
      endif
1065

1066
      if (present(set_zero)) set_zero_ = set_zero
225,158✔
1067
      if (set_zero_) call zero_array_int32(array, int(size_array))
225,158✔
1068

1069
      call mem%update_memory_after_alloc(size_array, 4)
225,158✔
1070

1071
   end subroutine alloc_i32_2
225,158✔
1072

1073

1074
   subroutine alloc_i32_3(mem, array, M, N, K, set_zero)
208✔
1075
      !!
1076
      !! Written by Sarai D. Folkestad and Eirik F. Kjønstad, Dec 2017
1077
      !!
1078
      !! Allocates a three dimensional integer array and updates the available
1079
      !! memory accordingly.
1080
      !!
1081
      !! set_zero: optional initialize array to zero
1082
      !!
1083
      implicit none
1084

1085
      class(memory_manager) :: mem
1086

1087
      integer(i32), dimension(:,:,:), allocatable :: array
1088
      logical, intent(in), optional :: set_zero
1089

1090
      integer, intent(in) :: M, N, K ! First and second dimension of array
1091

1092
      integer(i64) :: size_array ! Total size of array (M*N*K)
1093
      integer :: error = 0
1094
      logical :: set_zero_ = .false.
1095

1096
      character(len=100) :: error_msg
1097

1098
      size_array = M*N*K
208✔
1099

1100
      allocate(array(M,N,K), stat=error, errmsg=error_msg)
624✔
1101

1102
      if (error .ne. 0) then
208✔
1103
         call mem%print_allocation_error(size_array, error_msg)
×
1104
      endif
1105

1106
      if (present(set_zero)) set_zero_ = set_zero
208✔
1107
      if (set_zero_) call zero_array_int32(array, int(size_array))
208✔
1108

1109
      call mem%update_memory_after_alloc(size_array, 4)
208✔
1110

1111
   end subroutine alloc_i32_3
208✔
1112

1113

1114
   subroutine alloc_i_2_ranges(mem, array, range1, range2, set_zero)
298✔
1115
      !!
1116
      !! Written by Alexander C. Paul, Oct 2022
1117
      !!
1118
      !! Allocates a two dimensional integer array given 2 index ranges
1119
      !! and updates the available memory accordingly.
1120
      !!
1121
      !! set_zero: optional initialize array to zero
1122
      !!
1123
      use range_class, only: range_
1124

1125
      implicit none
1126

1127
      class(memory_manager) :: mem
1128

1129
      integer, dimension(:,:), allocatable :: array
1130
      logical, intent(in), optional :: set_zero
1131

1132
      ! First and second dimension of array that is being allocated
1133
      type(range_), intent(in) :: range1, range2
1134

1135
      integer(i64) :: size_array
1136
      integer :: first_1, last_1, first_2, last_2, error = 0
1137
      logical :: set_zero_ = .false.
1138

1139
      character(len=100) :: error_msg
1140

1141
      size_array = range1%get_length() * range2%get_length()
298✔
1142
      first_1 = range1%get_first()
298✔
1143
      last_1 = range1%get_last()
298✔
1144

1145
      first_2 = range2%get_first()
298✔
1146
      last_2 = range2%get_last()
298✔
1147

1148
      allocate(array(first_1:last_1, first_2:last_2), &
1149
               stat=error, errmsg=error_msg)
894✔
1150

1151
      if (present(set_zero)) set_zero_ = set_zero
298✔
1152
      if (set_zero_) call zero_array_int(array, int(size_array))
298✔
1153

1154
      if (error .ne. 0) then
298✔
1155
         call mem%print_allocation_error(size_array, error_msg)
×
1156
      endif
1157

1158
      call mem%update_memory_after_alloc(size_array, int_size)
298✔
1159

1160
   end subroutine alloc_i_2_ranges
298✔
1161

1162

1163
   subroutine alloc_l_1(mem, array, M, set_to)
373,569✔
1164
      !!
1165
      !! Written by Rolf H. Myhre, September 2019
1166
      !!
1167
      !! Allocates a one dimensional logical array and updates the available
1168
      !! memory accordingly.
1169
      !!
1170
      !! set_to: optional, initialize array to the value of set_to
1171
      !!
1172
      use array_initialization, only: set_logicals
1173

1174
      implicit none
1175

1176
      class(memory_manager) :: mem
1177

1178
      logical, dimension(:), allocatable :: array
1179
      logical, intent(in), optional :: set_to
1180

1181
      integer, intent(in) :: M ! Dimension of array
1182

1183
      integer(i64) :: size_array ! Total size of array (M)
1184
      integer :: error = 0, log_size
1185

1186
      character(len=100) :: error_msg
1187

1188
      size_array = M
373,569✔
1189

1190
      allocate(array(M), stat=error, errmsg=error_msg)
629,845✔
1191

1192
      if (present(set_to)) call set_logicals(array, int(size_array), set_to)
373,569✔
1193

1194
      if (error .ne. 0) then
373,569✔
1195
         call mem%print_allocation_error(size_array, error_msg)
×
1196
      endif
1197
      ! Figure out how big a logical is.
1198

1199
      log_size = storage_size(array(1))/8
373,569✔
1200
      call mem%update_memory_after_alloc(size_array, log_size)
373,569✔
1201

1202
   end subroutine alloc_l_1
373,569✔
1203

1204

1205
   subroutine alloc_l_2(mem, array, M, N, set_to)
563,700✔
1206
      !!
1207
      !! Written by Rolf H. Myhre, September 2019
1208
      !!
1209
      !! Allocates a two dimensional logical array and updates the available
1210
      !! memory accordingly.
1211
      !!
1212
      !! set_to: optional, initialize array to the value of set_to
1213
      !!
1214
      use array_initialization, only: set_logicals
1215

1216
      implicit none
1217

1218
      class(memory_manager) :: mem
1219

1220
      logical, dimension(:,:), allocatable :: array
1221
      logical, intent(in), optional :: set_to
1222

1223
      integer, intent(in) :: M, N ! Dimension of array
1224

1225
      integer(i64) :: size_array ! Total size of array (M*N)
1226
      integer :: error = 0, log_size
1227

1228
      character(len=100) :: error_msg
1229

1230
      size_array = M * N
563,700✔
1231

1232
      allocate(array(M,N), stat=error, errmsg=error_msg)
1,319,852✔
1233
      if (present(set_to)) call set_logicals(array, int(size_array), set_to)
563,700✔
1234

1235
      if (error .ne. 0) then
563,700✔
1236
         call mem%print_allocation_error(size_array, error_msg)
×
1237
      endif
1238

1239
      ! Figure out how big a logical is.
1240
      log_size = storage_size(array(1,1))/8
563,700✔
1241
      call mem%update_memory_after_alloc(size_array, log_size)
563,700✔
1242

1243
   end subroutine alloc_l_2
563,700✔
1244

1245

1246
   subroutine alloc_char_1(mem, array, L, N)
366✔
1247
      !!
1248
      !! Written by Marcus T. Lexander, 2025
1249
      !!
1250
      !! Allocates an array of character(len=L)
1251
      !!
1252
      implicit none
1253

1254
      class(memory_manager) :: mem
1255

1256
      character(len=:), dimension(:), allocatable :: array
1257

1258
      integer, intent(in) :: L, N ! length of each string and number of strings
1259

1260
      integer(i64) :: size_array ! Total size of array (L*N)
1261
      integer :: error = 0
1262

1263
      character(len=100) :: error_msg
1264

1265
      size_array = L*N
366✔
1266

1267
      allocate(character(len=L) :: array(N), stat=error, errmsg=error_msg)
732✔
1268

1269
      if (error .ne. 0) then
366✔
NEW
1270
         call mem%print_allocation_error(size_array, error_msg)
×
1271
      endif
1272

1273
      call mem%update_memory_after_alloc(size_array, 1)
366✔
1274

1275
   end subroutine alloc_char_1
366✔
1276

1277

1278
   subroutine dealloc_real(mem, array)
69,918,500✔
1279
      !!
1280
      !! Written by Rolf H. Myhre and Alexander C. Paul, 2019-2023
1281
      !!
1282
      !! Deallocates a real double precision array and updates the available
1283
      !! memory accordingly.
1284
      !!
1285
      implicit none
1286

1287
      class(memory_manager),                intent(inout) :: mem
1288
      real(dp), dimension(..), allocatable, intent(inout) :: array
1289

1290
      integer(i64) :: size_array
1291
      integer :: error = 0
1292

1293
      character(len=100) :: error_msg
1294

1295
      if(.not. allocated(array)) call output%error_msg("Trying to deallocate an unallocated array!")
69,918,500✔
1296

1297
      size_array = size(array, kind=i64)
69,918,500✔
1298

1299
      select rank(array)
1300
         rank(1)
1301
            deallocate(array, stat=error, errmsg=error_msg)
6,637,035✔
1302
         rank(2)
1303
            deallocate(array, stat=error, errmsg=error_msg)
23,050,748✔
1304
         rank(3)
1305
            deallocate(array, stat=error, errmsg=error_msg)
19,669,627✔
1306
         rank(4)
1307
            deallocate(array, stat=error, errmsg=error_msg)
20,164,686✔
1308
         rank(5)
1309
            deallocate(array, stat=error, errmsg=error_msg)
7,520✔
1310
         rank(6)
1311
            deallocate(array, stat=error, errmsg=error_msg)
388,884✔
1312
         rank default ! GCC 10.2 does not allow a call to output%error_msg here
1313
            error stop 'Deallocation not implemented for real array of rank 7+'
×
1314
      end select
1315

1316
      if (error .ne. 0) call mem%print_deallocation_error(size_array, error_msg)
69,918,500✔
1317

1318
      call mem%update_memory_after_dealloc(size_array, dp)
69,918,500✔
1319

1320
   end subroutine dealloc_real
69,918,500✔
1321

1322

1323
   subroutine dealloc_complex(mem, array)
1,525,531✔
1324
      !!
1325
      !! Written by Rolf H. Myhre and Alexander C. Paul, 2019-2023
1326
      !!
1327
      !! Deallocates a complex double precision array and updates the available
1328
      !! memory accordingly.
1329
      !!
1330
      implicit none
1331

1332
      class(memory_manager),               intent(inout) :: mem
1333
      complex(dp), dimension(..), allocatable, intent(inout) :: array
1334

1335
      integer(i64) :: size_array
1336
      integer :: error = 0
1337

1338
      character(len=100) :: error_msg
1339

1340
      if(.not. allocated(array)) call output%error_msg("Trying to deallocate an unallocated array!")
1,525,531✔
1341

1342
      size_array = size(array, kind=i64)
1,525,531✔
1343

1344
      select rank(array)
1345
         rank(1)
1346
            deallocate(array, stat=error, errmsg=error_msg)
123,042✔
1347
         rank(2)
1348
            deallocate(array, stat=error, errmsg=error_msg)
284,646✔
1349
         rank(3)
1350
            deallocate(array, stat=error, errmsg=error_msg)
532,421✔
1351
         rank(4)
1352
            deallocate(array, stat=error, errmsg=error_msg)
585,422✔
1353
         rank default ! GCC 10.2 does not allow a call to output%error_msg here
1354
            error stop 'Deallocation not implemented for complex array of rank 5+'
×
1355
      end select
1356

1357
      if (error .ne. 0) call mem%print_deallocation_error(size_array, error_msg)
1,525,531✔
1358

1359
      call mem%update_memory_after_dealloc(size_array, 2*dp)
1,525,531✔
1360

1361
   end subroutine dealloc_complex
1,525,531✔
1362

1363

1364
   subroutine dealloc_64bit_integer(mem, array)
2,084,613✔
1365
      !!
1366
      !! Written by Rolf H. Myhre and Alexander C. Paul, 2019-2023
1367
      !!
1368
      !! Deallocates a 64 bit integer array and updates the available memory accordingly.
1369
      !!
1370
      implicit none
1371

1372
      class(memory_manager),               intent(inout) :: mem
1373
      integer(i64), dimension(..), allocatable, intent(inout) :: array
1374

1375
      integer(i64) :: size_array
1376
      integer :: error = 0
1377

1378
      character(len=100) :: error_msg
1379

1380
      if(.not. allocated(array)) call output%error_msg("Trying to deallocate an unallocated array!")
2,084,613✔
1381

1382
      size_array = size(array, kind=i64)
2,084,613✔
1383

1384
      select rank(array)
1385
         rank(1)
1386
            deallocate(array, stat=error, errmsg=error_msg)
1,711,103✔
1387
         rank(2)
1388
            deallocate(array, stat=error, errmsg=error_msg)
373,094✔
1389
         rank(3)
1390
            deallocate(array, stat=error, errmsg=error_msg)
416✔
1391
         rank default ! GCC 10.2 does not allow a call to output%error_msg here
1392
            error stop 'Deallocation not implemented for 64-bit integer array of rank 4+'
×
1393
      end select
1394

1395
      if (error .ne. 0) call mem%print_deallocation_error(size_array, error_msg)
2,084,613✔
1396

1397
      mem%available = mem%available + 8*size_array
2,084,613✔
1398

1399
   end subroutine dealloc_64bit_integer
2,084,613✔
1400

1401

1402
   subroutine dealloc_32bit_integer(mem, array)
1,026,077✔
1403
      !!
1404
      !! Written by Marcus T. Lexander and Alexander C. Paul, 2022-2023
1405
      !!
1406
      !! Deallocates a 32 bit integer array and updates the available memory accordingly.
1407
      !!
1408
      implicit none
1409

1410
      class(memory_manager),               intent(inout) :: mem
1411
      integer(i32), dimension(..), allocatable, intent(inout) :: array
1412

1413
      integer(i64) :: size_array
1414
      integer :: error = 0
1415

1416
      character(len=100) :: error_msg
1417

1418
      if(.not. allocated(array)) call output%error_msg("Trying to deallocate an unallocated array!")
1,026,077✔
1419

1420
      size_array = size(array, kind=i64)
1,026,077✔
1421

1422
      select rank(array)
1423
         rank(1)
1424
            deallocate(array, stat=error, errmsg=error_msg)
800,713✔
1425
         rank(2)
1426
            deallocate(array, stat=error, errmsg=error_msg)
225,156✔
1427
         rank(3)
1428
            deallocate(array, stat=error, errmsg=error_msg)
208✔
1429
         rank default ! GCC 10.2 does not allow a call to output%error_msg here
1430
            error stop 'Deallocation not implemented for 32-bit integer array of rank 4+'
×
1431
      end select
1432

1433
      if (error .ne. 0) call mem%print_deallocation_error(size_array, error_msg)
1,026,077✔
1434

1435
      mem%available = mem%available + 4*size_array
1,026,077✔
1436

1437
   end subroutine dealloc_32bit_integer
1,026,077✔
1438

1439

1440
   subroutine dealloc_logical(mem, array)
937,163✔
1441
      !!
1442
      !! Written by Rolf H. Myhre and Alexander C. Paul, 2019-2023
1443
      !!
1444
      !! Deallocates a logical array and updates the available memory accordingly.
1445
      !!
1446
      implicit none
1447

1448
      class(memory_manager),               intent(inout) :: mem
1449
      logical, dimension(..), allocatable, intent(inout) :: array
1450

1451
      integer(i64) :: size_array
1452
      integer :: error = 0, log_size
1453
      logical :: dummy_logical
1454

1455
      character(len=100) :: error_msg
1456

1457
      if(.not. allocated(array)) call output%error_msg("Trying to deallocate an unallocated array!")
937,163✔
1458

1459
      size_array = size(array, kind=i64)
937,163✔
1460

1461
      select rank(array)
1462
         rank(1)
1463
            deallocate(array, stat=error, errmsg=error_msg)
373,463✔
1464
         rank(2)
1465
            deallocate(array, stat=error, errmsg=error_msg)
563,700✔
1466
         rank default ! GCC 10.2 does not allow a call to output%error_msg here
1467
            error stop 'Deallocation not implemented for logical array of rank 3+'
×
1468
      end select
1469

1470
      if (error .ne. 0) call mem%print_deallocation_error(size_array, error_msg)
937,163✔
1471

1472
      log_size = storage_size(dummy_logical)/8
1473
      mem%available = mem%available + log_size*size_array
937,163✔
1474

1475
   end subroutine dealloc_logical
937,163✔
1476

1477

1478
   subroutine dealloc_char(mem, array)
366✔
1479
      !!
1480
      !! Written by Marcus T. Lexander, 2025
1481
      !!
1482
      !! Deallocates a character array and updates the available memory accordingly.
1483
      !!
1484
      implicit none
1485

1486
      class(memory_manager),                        intent(inout) :: mem
1487
      character(len=:), dimension(..), allocatable, intent(inout) :: array
1488

1489
      integer(i64) :: size_array, char_len
1490
      integer :: error = 0
1491

1492
      character(len=100) :: error_msg
1493

1494
      if(.not. allocated(array)) call output%error_msg("Trying to deallocate an unallocated array!")
366✔
1495

1496
      char_len = len(array)
366✔
1497
      size_array = size(array)
366✔
1498

1499
      select rank(array)
1500
         rank(1)
1501
            deallocate(array, stat=error, errmsg=error_msg)
366✔
1502
         rank default ! GCC 10.2 does not allow a call to output%error_msg here
NEW
1503
            error stop 'Deallocation not implemented for character array of rank 2+'
×
1504
      end select
1505

1506
      if (error .ne. 0) call mem%print_deallocation_error(size_array, error_msg)
366✔
1507

1508
      mem%available = mem%available + char_len*size_array
366✔
1509

1510
   end subroutine dealloc_char
366✔
1511

1512

UNCOV
1513
   subroutine print_allocation_error(size_array, error_msg)
×
1514
      !!
1515
      !! Written by Alexander C. Paul, March 2020
1516
      !!
1517
      implicit none
1518

1519
      integer(i64), intent(in) :: size_array
1520
      character (len=*), intent(in) :: error_msg
1521

1522
      character(len=17) :: number_string
1523

1524
      if (size_array < 0) &
×
1525
         call output%error_msg('Trying to allocate array with negative number of elements. &
1526
                               &This could be an integer overflow.')
×
1527

1528
      write(number_string, '(i0)') size_array
×
1529

1530
      call output%printf('m', error_msg, fs='(/t3,a)')
×
1531
      call output%printf('m', 'Note: Error message from gfortran might not be accurate.', &
1532
                          fs='(t3,a)')
×
1533
      call output%error_msg('Could not allocate array with #elements = (a0).', &
1534
                             chars=[trim(number_string)], ffs='(t3,a)')
×
1535

1536
   end subroutine print_allocation_error
×
1537

1538

1539
   subroutine print_deallocation_error(size_array, error_msg)
×
1540
      !!
1541
      !! Written by Alexander C. Paul, March 2020
1542
      !!
1543
      implicit none
1544

1545
      integer(i64), intent(in) :: size_array
1546
      character (len=*), intent(in) :: error_msg
1547

1548
      character(len=17) :: number_string
1549

1550
      write(number_string, '(i0)') size_array
×
1551

1552
      call output%printf('m', error_msg)
×
1553
      call output%printf('m', 'Note: Error message from gfortran might not be accurate.')
×
1554
      call output%error_msg('Could not deallocate array with #elements = (a0).', &
1555
                             chars=[trim(number_string)])
×
1556

1557
   end subroutine print_deallocation_error
×
1558

1559

1560
   subroutine update_memory_after_alloc(mem, size_array, size_type)
75,495,402✔
1561
      !!
1562
      !! Written by Alexander C. Paul, May 2020
1563
      !!
1564
      !! size_array: total size of the array allocated
1565
      !! size_type : storage size of one element of the array in Byte
1566
      !!
1567
      implicit none
1568

1569
      class(memory_manager) :: mem
1570

1571
      integer(i64), intent(in) :: size_array
1572
      integer, intent(in) :: size_type
1573

1574
      integer(i64) :: bytes
1575
      character(len=17) :: number_string
1576

1577
      bytes = size_array*int(size_type, kind=i64)
75,495,402✔
1578

1579
      if (bytes < 0) &
75,495,402✔
1580
         call output%error_msg('Trying to allocate array with less than 0 B. &
1581
                               &This could be an integer overflow.')
×
1582

1583
      mem%available = mem%available - bytes
75,495,402✔
1584

1585
      if (mem%available .lt. 0) then
75,495,402✔
1586

1587
         write(number_string, '(i0)') size_array
6✔
1588

1589
         call output%error_msg('User-specified memory insufficient in mem%alloc. &
1590
                               &Tried to allocate array with (a0) elements.', &
1591
                                chars=[trim(number_string)], ll=50)
18✔
1592

1593
      endif
1594

1595
      ! Update max used memory if needed
1596
      if (mem%max_used < (mem%total - mem%available)) &
75,495,396✔
1597
          mem%max_used =  mem%total - mem%available
711,920✔
1598

1599
      if (mem%batching_on) call mem%batch_mem_tracker%update(bytes)
75,495,396✔
1600

1601
   end subroutine update_memory_after_alloc
75,495,396✔
1602

1603

1604
   subroutine update_memory_after_dealloc(mem, size_array, size_type)
71,444,031✔
1605
      !!
1606
      !! Written by Alexander C. Paul, May 2020
1607
      !!
1608
      !! size_array: total size of the array allocated
1609
      !! size_type : storage size of one element of the array in Byte
1610
      !!
1611
      implicit none
1612

1613
      class(memory_manager) :: mem
1614

1615
      integer(i64), intent(in) :: size_array
1616
      integer, intent(in) :: size_type
1617

1618
      integer(i64) :: bytes
1619

1620
      bytes = size_array*int(size_type, kind=i64)
71,444,031✔
1621

1622
      mem%available = mem%available + bytes
71,444,031✔
1623

1624
      if (mem%batching_on) call mem%batch_mem_tracker%update(-bytes)
71,444,031✔
1625

1626
   end subroutine update_memory_after_dealloc
71,444,031✔
1627

1628

1629
   subroutine print_settings_memory_manager(mem)
3,169✔
1630
      !!
1631
      !! Written by Sarai D. Folkestad and Eirik F. Kjønstad, Sep 2018
1632
      !!
1633
      implicit none
1634

1635
      class(memory_manager) :: mem
1636

1637
      call output%printf('m', 'Memory available for calculation: ' //  &
1638
                         mem%get_memory_as_character(mem%total))
3,169✔
1639

1640
   end subroutine print_settings_memory_manager
3,169✔
1641

1642

1643
   subroutine batch_finalize_memory_manager(mem)
4,719,843✔
1644
      !!
1645
      !! Written by Eirik F. Kjønstad, June 2021
1646
      !!
1647
      !! Must be called after a batching loop is finished.
1648
      !!
1649
      !! The routine turns of batching mode and deallocates the
1650
      !! memory tracker for the batching procedure.
1651
      !!
1652
      implicit none
1653

1654
      class(memory_manager), intent(inout) :: mem
1655

1656
      mem%batching_on = .false.
4,719,843✔
1657

1658
      if (allocated(mem%batch_mem_tracker)) then
4,719,843✔
1659

1660
         deallocate(mem%batch_mem_tracker)
4,719,843✔
1661

1662
      else
1663

1664
         call output%error_msg('Asked to finalize batch, but batching tracker &
1665
                               &not allocated! Was batch_finalize already called &
1666
                               &for the current batching loop?')
×
1667

1668
      endif
1669

1670
   end subroutine batch_finalize_memory_manager
4,719,843✔
1671

1672

1673
   subroutine initialize_batching_tracker(mem, max_memory_usage, tag)
4,719,843✔
1674
      !!
1675
      !! Written by Eirik F. Kjønstad, June 2021
1676
      !!
1677
      !! To be called when batching has been determined.
1678
      !!
1679
      !! Makes sure memory usage is tracked during the batching loops.
1680
      !!
1681
      implicit none
1682

1683
      class(memory_manager), intent(inout) :: mem
1684

1685
      integer(i64), intent(in) :: max_memory_usage
1686
      character(len=*), intent(in) :: tag
1687

1688
      if (mem%batching_on) then
4,719,843✔
1689

1690
         call output%error_msg('Tried to initialize memory tracker for batching loop, &
1691
                               &but the memory manager is already in batching mode! &
1692
                               &Have you forgotten to finalize the previous batching loop?')
×
1693

1694
      endif
1695

1696
      mem%batching_on = .true.
4,719,843✔
1697
      mem%batch_mem_tracker = memory_tracker(max_memory_usage, tag)
4,719,843✔
1698

1699
   end subroutine initialize_batching_tracker
4,719,843✔
1700

1701

1702
   subroutine batch_setup_1(mem, batch_p, req0, req1, tag, element_size)
3,688,893✔
1703
      !!
1704
      !! Written by Rolf H. Myhre and Eirik F. Kjønstad, December 2018
1705
      !!
1706
      !! Batching setup for a single index.
1707
      !!
1708
      !! batch_p:  Initialized batching object.
1709
      !!
1710
      !! req0:     Memory required that does not change with the index dimension.
1711
      !!           E.g., n_o**2*n_v**2 for (vo|vo) if none of the indices
1712
      !!           in the integral is batched over.
1713
      !!
1714
      !! req1:     Memory required per batching index (linear with batch size).
1715
      !!           E.g., n_v**3 for (vv|vo) when batching over the
1716
      !!           occupied index.
1717
      !!
1718
      implicit none
1719

1720
      class(memory_manager) :: mem
1721

1722
      class(batching_index) :: batch_p ! The index being batched over
1723

1724
      integer, intent(in) :: req0
1725
      integer, intent(in) :: req1
1726

1727
      character(len=*), intent(in) :: tag
1728

1729
      integer, intent(in), optional :: element_size
1730

1731

1732
      integer(i64):: req0_tot
1733
      integer(i64):: req1_min
1734
      integer(i64):: req_min
1735

1736
      integer(i64):: req_tot
1737

1738
      integer :: e_size
1739
      character(len=17), allocatable :: reqChar
1740

1741
      if (.not. batch_p%initialized) then
3,688,893✔
1742

1743
         call output%error_msg('batch_setup_1 called on uninitialized batch')
×
1744

1745
      endif
1746

1747
      e_size = dp
1748
      if(present(element_size)) then
3,688,893✔
1749
         e_size = element_size
128,352✔
1750
      endif
1751

1752
      req0_tot = int(req0, kind=i64) * int(e_size, kind=i64)
3,688,893✔
1753
      req1_min = int(req1, kind=i64) * int(e_size, kind=i64)
3,688,893✔
1754

1755
      req_min = req0_tot + req1_min
3,688,893✔
1756
      req_tot = req0_tot + req1_min*int(batch_p%index_dimension, kind=i64)
3,688,893✔
1757

1758
      if (req_tot .lt. mem%available) then
3,688,893✔
1759

1760
         ! No need to batch
1761

1762
         batch_p%num_batches = 1
3,688,893✔
1763
         batch_p%max_length  = batch_p%index_dimension
3,688,893✔
1764

1765
      else if (req_min .gt. mem%available) then
×
1766

1767
         ! Hack because intel flips out if we put two functions in chars=[]
1768

1769
         reqChar = mem%get_memory_as_character(req_min, .true.)
×
1770
         call output%printf('m', 'Need at least (a0) but only have (a0)', &
1771
                            chars=[reqChar, mem%get_memory_as_character(mem%available, .true.)])
×
1772
         call output%error_msg('Not enough memory for a batch.')
×
1773

1774
      else
1775

1776
         ! We need to batch
1777
         !
1778
         ! Determine maximum batch length
1779

1780
         batch_p%max_length = int((mem%available - req0_tot)/req1_min)
×
1781

1782
         ! Number of full batches
1783

1784
         batch_p%num_batches = (batch_p%index_dimension-1)/(batch_p%max_length)+1
×
1785

1786
      endif
1787

1788
      if (force_batch) call batch_p%force_batch()
1,837,383✔
1789

1790
      if (batch_p%num_batches > 1) call output%printf('v', 'Batching in (a0)', chars=[tag])
7,132,291✔
1791

1792
      call mem%initialize_batching_tracker(req0_tot + req1_min*int(batch_p%max_length, kind=i64), &
1793
                                           tag)
3,688,893✔
1794

1795
   end subroutine batch_setup_1
7,377,786✔
1796

1797

1798
   subroutine batch_setup_2(mem, batch_p, batch_q, req0, req1_p, req1_q, &
961,853✔
1799
                                           req2, tag, element_size, req_single_batch)
1800
      !!
1801
      !! Written by Rolf H. Myhre and Eirik F. Kjønstad, Dec 2018
1802
      !!
1803
      !! Batching setup for two batching indices.
1804
      !!
1805
      !! batch_p: Initialized batching object
1806
      !! batch_q: Initialized batching object
1807
      !!
1808
      !! req0: required memory that does not scale with batch size
1809
      !!
1810
      !! req1_p: required memory that scales linearly with p batch size
1811
      !! req1_q: required memory that scales linearly with q batch size
1812
      !!
1813
      !! req2: required memory that scales quadratically with batch size
1814
      !!
1815
      !! element_size: memory per element, default is double precision
1816
      !!
1817
      !! req_single_batch: optional specifying the minimal memory needed to not batch
1818
      !!
1819
      !! If you are batching over i and j and need to keep g_abij, g_abci and g_abcj in memory,
1820
      !! req1_i = n_v**3, req1_j = n_v**3 and req2 = n_v**2.
1821
      !! Memory per batch is then batch_size*(req1_i + req1_j) + batch_size**2*req2
1822
      !!
1823
      !! If you are batching over a and j and need to keep g_abij, g_abci and g_abcj in memory,
1824
      !! req1_a = n_o*n_v**2, req1_j = 0, and req2 = n_o*n_v + n_v**2. Note that one integral (g_abci)
1825
      !! scales linearly with the a-index but that there are no such integrals for the j-index.
1826
      !!
1827
      !! Be careful with symmetries and permutations!
1828
      !!
1829
      implicit none
1830

1831
      class(memory_manager) :: mem
1832

1833
      class(batching_index) :: batch_p ! An index being batched over
1834
      class(batching_index) :: batch_q ! An index being batched over
1835

1836
      integer, intent(in) :: req0
1837
      integer, intent(in) :: req1_p
1838
      integer, intent(in) :: req1_q
1839
      integer, intent(in) :: req2
1840

1841
      character(len=*), intent(in) :: tag
1842

1843
      integer, intent(in), optional :: element_size
1844
      integer, intent(in), optional :: req_single_batch
1845

1846

1847
      logical :: figured_out
1848

1849
      integer(i64):: req0_tot
1850
      integer(i64):: req1_p_min
1851
      integer(i64):: req1_q_min
1852
      integer(i64):: req2_min
1853
      integer(i64):: req_min
1854
      integer(i64):: req_tot
1855

1856
      integer(i64):: p_elements, q_elements
1857

1858
      integer :: e_size
1859
      character(len=17), allocatable :: reqChar
1860

1861
      integer(i64) :: max_memory_usage
1862

1863
      if ((.not. batch_p%initialized) .or. (.not. batch_q%initialized)) then
961,853✔
1864

1865
         call output%error_msg('batch_setup_2 called on uninitialized batch')
×
1866

1867
      endif
1868

1869
      e_size = dp
1870
      if(present(element_size)) then
961,853✔
1871
         e_size = element_size
21,930✔
1872
      endif
1873

1874
      req0_tot   = int(req0, kind=i64) * int(e_size, kind=i64)
961,853✔
1875
      req1_p_min = int(req1_p, kind=i64) * int(e_size, kind=i64)
961,853✔
1876
      req1_q_min = int(req1_q, kind=i64) * int(e_size, kind=i64)
961,853✔
1877
      req2_min   = int(req2, kind=i64) * int(e_size, kind=i64)
961,853✔
1878

1879
      req_min = req0_tot + req1_p_min + req1_q_min + req2_min
961,853✔
1880

1881
      ! Determine or copy the memory needed to not batch
1882

1883
      if (present(req_single_batch)) then
961,853✔
1884
         req_tot = int(req_single_batch, kind=i64)*int(e_size, kind=i64)
270,329✔
1885
      else
1886

1887
         req_tot = req0_tot + req1_p_min*int(batch_p%index_dimension, kind=i64)  &
1888
                  + req1_q_min*int(batch_q%index_dimension, kind=i64)  &
1889
                  + req2_min*int(batch_p%index_dimension, kind=i64)    &
1890
                     *int(batch_q%index_dimension, kind=i64)
691,524✔
1891

1892
      end if
1893

1894
      if (req_tot .lt. mem%available) then
961,853✔
1895

1896
         ! No need to batch
1897

1898
         batch_p%num_batches = 1
961,853✔
1899
         batch_p%max_length  = batch_p%index_dimension
961,853✔
1900

1901
         batch_q%num_batches = 1
961,853✔
1902
         batch_q%max_length  = batch_q%index_dimension
961,853✔
1903

1904
      else if (req_min .gt. mem%available) then
×
1905

1906
         ! Hack because intel flips out if we put two functions in chars=[]
1907
         reqChar = mem%get_memory_as_character(req_min, .true.)
×
1908
         call output%printf('m', 'Need at least (a0) but only have (a0)', &
1909
                            chars=[reqChar, mem%get_memory_as_character(mem%available, .true.)])
×
1910
         call output%error_msg('Not enough memory for a batch.')
×
1911

1912
      else
1913

1914
         ! We need to batch
1915
         !
1916
         ! Figure out how many we have room for
1917
         !
1918
         ! I. First, try to increment both indices simultaneously
1919

1920
         p_elements = 1
1921
         q_elements = 1
1922

1923
         figured_out = .false.
1924
         do while (.not. figured_out                                    &
1925
                     .and. int(p_elements) .lt. batch_p%index_dimension &
1926
                     .and. int(q_elements) .lt. batch_q%index_dimension)
×
1927

1928
            if (((p_elements+1)*(q_elements+1)*req2_min &
×
1929
                  + (p_elements+1)*req1_p_min          &
1930
                  + (q_elements+1)*req1_q_min          &
1931
                  + req0_tot) .lt. mem%available) then
×
1932

1933
               p_elements = p_elements + 1 ! can hold +1 batch size
1934
               q_elements = q_elements + 1
1935

1936
            else
1937

1938
               figured_out = .true.       ! cannot hold +1 batch size
1939

1940
            endif
1941

1942
         enddo
1943

1944

1945
         ! II. If simultaneous incrementation was not sufficient,
1946
         !      then try to increment the largest index further. This is
1947
         !      guaranteed to work, so let's just go ahead and increment
1948
         !      with no safeguards in place.
1949

1950
         if (.not. figured_out) then
×
1951

1952
            if (batch_p%index_dimension .gt. batch_q%index_dimension) then
×
1953

1954
               ! Increment p
1955

1956
               do while (((p_elements+1)*q_elements*req2_min &
×
1957
                           + (p_elements+1)*req1_p_min       &
1958
                           + q_elements*req1_q_min           &
1959
                           + req0_tot) .lt. mem%available)
1960

1961
                  p_elements = p_elements + 1
×
1962

1963
               enddo
1964

1965
            elseif (batch_p%index_dimension .lt. batch_q%index_dimension) then
×
1966

1967
               ! Increment q
1968

1969
               do while ((p_elements*(q_elements+1)*req2_min &
×
1970
                           + p_elements*req1_p_min           &
1971
                           + (q_elements+1)*req1_q_min       &
1972
                           + req0_tot) .lt. mem%available)
1973

1974
                  q_elements = q_elements + 1
×
1975

1976
               enddo
1977

1978
            else
1979

1980
               call output%error_msg('Something went very wrong! Expected different-sized' // &
1981
                                      'indices, but got same-sized indices (in batching setup).')
×
1982

1983
            endif
1984

1985
            figured_out = .true.
1986

1987
         endif
1988

1989
         batch_p%max_length = int(p_elements)
×
1990
         batch_q%max_length = int(q_elements)
×
1991

1992
         ! Figure out how many batches
1993

1994
         batch_p%num_batches = (batch_p%index_dimension-1)/(batch_p%max_length)+1
×
1995
         batch_q%num_batches = (batch_q%index_dimension-1)/(batch_q%max_length)+1
×
1996

1997
      endif
1998

1999
      ! Debug feature: enforced random batching
2000

2001
      if (force_batch) then
2002

2003
         if (batch_p%index_dimension == batch_q%index_dimension) then
478,847✔
2004

2005
            call batch_p%force_batch()
173,073✔
2006

2007
            batch_q%max_length  = batch_p%max_length
173,073✔
2008
            batch_q%num_batches = batch_p%num_batches
173,073✔
2009

2010
         else
2011

2012
            call batch_p%force_batch()
305,774✔
2013
            call batch_q%force_batch()
305,774✔
2014

2015
         endif
2016

2017
      endif
2018

2019
      max_memory_usage = req0_tot + &
2020
                         req1_p_min*int(batch_p%max_length, kind=i64) + &
2021
                         req1_q_min*int(batch_q%max_length, kind=i64) + &
2022
                         req2_min*int(batch_q%max_length, kind=i64)     &
2023
                                 *int(batch_q%max_length, kind=i64)
961,853✔
2024

2025
      if (batch_p%num_batches .eq. 1 .and. &
961,853✔
2026
          batch_q%num_batches .eq. 1) then
2027

2028
         if (present(req_single_batch)) then
494,069✔
2029

2030
            max_memory_usage = int(req_single_batch, kind=i64)*int(e_size, kind=i64)
135,561✔
2031

2032
         endif
2033

2034
      endif
2035

2036
      if (any([batch_p%num_batches, batch_q%num_batches] > 1)) then
961,853✔
2037
         call output%printf('v', 'Batching in (a0)', chars=[tag])
1,403,352✔
2038
      end if
2039

2040
      call mem%initialize_batching_tracker(max_memory_usage, tag)
961,853✔
2041

2042
   end subroutine batch_setup_2
1,923,706✔
2043

2044

2045
   subroutine batch_setup_3(mem, batch_p, batch_q, batch_r, req0, &
69,097✔
2046
                                           req1_p, req1_q, req1_r, req2_pq,      &
2047
                                           req2_pr, req2_qr, req3, tag,          &
2048
                                           element_size, req_single_batch)
2049
      !!
2050
      !! Written by Rolf H. Myhre December 2018
2051
      !!
2052
      !! Batching setup for three batching indices.
2053
      !!
2054
      !! batch_p: Initialized batching object
2055
      !! batch_q: Initialized batching object
2056
      !! batch_r: Initialized batching object
2057
      !!
2058
      !! req0: required memory that does not scale with batch size
2059
      !!
2060
      !! req1_p: required memory that scales linearly with p batch size
2061
      !! req1_q: required memory that scales linearly with q batch size
2062
      !! req1_r: required memory that scales linearly with r batch size
2063
      !!
2064
      !! req2_pq: required memory that scales quadratically with pq batch size
2065
      !! req2_pr: required memory that scales quadratically with pr batch size
2066
      !! req2_qr: required memory that scales quadratically with qr batch size
2067
      !!
2068
      !! req3: required memory that scales cubically with batch indices pqr
2069
      !!
2070
      !! element_size: memory per element, default is double precision
2071
      !!
2072
      !! req_single_batch: optional specifying the minimal memory needed to not batch
2073
      !!
2074
      !! Be careful with symmetries and permutations!
2075
      !!
2076
      implicit none
2077

2078
      class(memory_manager) :: mem
2079

2080
      class(batching_index) :: batch_p ! An index being batched over
2081
      class(batching_index) :: batch_q ! An index being batched over
2082
      class(batching_index) :: batch_r ! An index being batched over
2083

2084
      integer, intent(in) :: req0
2085

2086
      integer, intent(in) :: req1_p
2087
      integer, intent(in) :: req1_q
2088
      integer, intent(in) :: req1_r
2089

2090
      integer, intent(in) :: req2_pq
2091
      integer, intent(in) :: req2_pr
2092
      integer, intent(in) :: req2_qr
2093

2094
      integer, intent(in) :: req3
2095

2096
      character(len=*), intent(in) :: tag
2097

2098
      integer, intent(in), optional :: element_size
2099
      integer, intent(in), optional :: req_single_batch
2100

2101

2102
      integer(i64):: req0_tot
2103

2104
      integer(i64):: req1_p_min
2105
      integer(i64):: req1_q_min
2106
      integer(i64):: req1_r_min
2107

2108
      integer(i64):: req2_pq_min
2109
      integer(i64):: req2_pr_min
2110
      integer(i64):: req2_qr_min
2111

2112
      integer(i64):: req3_min
2113

2114
      integer(i64):: req_min
2115
      integer(i64):: req_tot
2116

2117
      integer(i64):: p_elements, q_elements, r_elements
2118

2119
      logical :: found_batch_size, p_incremented, q_incremented, r_incremented
2120

2121
      integer :: e_size
2122
      character(len=17), allocatable :: reqChar
2123

2124
      integer(i64) :: max_memory_usage
2125

2126
      if ((.not. batch_p%initialized)        &
2127
            .or. (.not. batch_q%initialized) &
2128
            .or. (.not. batch_r%initialized)) then
69,097✔
2129

2130
         call output%error_msg('batch_setup_3 called on uninitialized batch')
×
2131

2132
      endif
2133

2134
      e_size = dp
2135
      if(present(element_size)) then
69,097✔
2136
         e_size = element_size
×
2137
      endif
2138

2139
      req0_tot   = int(req0, kind=i64) * int(e_size, kind=i64)
69,097✔
2140

2141
      req1_p_min = int(req1_p, kind=i64) * int(e_size, kind=i64)
69,097✔
2142
      req1_q_min = int(req1_q, kind=i64) * int(e_size, kind=i64)
69,097✔
2143
      req1_r_min = int(req1_r, kind=i64) * int(e_size, kind=i64)
69,097✔
2144

2145
      req2_pq_min = int(req2_pq, kind=i64) * int(e_size, kind=i64)
69,097✔
2146
      req2_pr_min = int(req2_pr, kind=i64) * int(e_size, kind=i64)
69,097✔
2147
      req2_qr_min = int(req2_qr, kind=i64) * int(e_size, kind=i64)
69,097✔
2148

2149
      req3_min = int(req3, kind=i64) * int(e_size, kind=i64)
69,097✔
2150

2151
      req_min = req0_tot + req1_p_min + req1_q_min + req1_r_min &
2152
              + req2_pq_min + req2_pr_min + req2_qr_min + req3_min
69,097✔
2153

2154
      ! Determine or copy the memory needed to not batch
2155

2156
      if (present(req_single_batch)) then
69,097✔
2157
         req_tot = int(req_single_batch, kind=i64) * int(e_size, kind=i64)
67,174✔
2158
      else
2159

2160
         req_tot = req0_tot + req1_p_min                              &
2161
                              *int(batch_p%index_dimension, kind=i64) &
2162
                            + req1_q_min                              &
2163
                              *int(batch_q%index_dimension, kind=i64) &
2164
                            + req1_r_min                              &
2165
                              *int(batch_r%index_dimension, kind=i64) &
2166
                            + req2_pq_min                             &
2167
                              *int(batch_p%index_dimension, kind=i64) &
2168
                              *int(batch_q%index_dimension, kind=i64) &
2169
                            + req2_pr_min                             &
2170
                              *int(batch_p%index_dimension, kind=i64) &
2171
                              *int(batch_r%index_dimension, kind=i64) &
2172
                            + req2_qr_min                             &
2173
                              *int(batch_q%index_dimension, kind=i64) &
2174
                              *int(batch_r%index_dimension, kind=i64) &
2175
                            + req3_min                                &
2176
                              *int(batch_p%index_dimension, kind=i64) &
2177
                              *int(batch_q%index_dimension, kind=i64) &
2178
                              *int(batch_r%index_dimension, kind=i64)
1,923✔
2179

2180
      end if
2181

2182
      if (req_tot .lt. mem%available) then
69,097✔
2183

2184
         ! No need to batch
2185

2186
         batch_p%num_batches = 1
69,097✔
2187
         batch_p%max_length  = batch_p%index_dimension
69,097✔
2188

2189
         batch_q%num_batches = 1
69,097✔
2190
         batch_q%max_length  = batch_q%index_dimension
69,097✔
2191

2192
         batch_r%num_batches = 1
69,097✔
2193
         batch_r%max_length  = batch_r%index_dimension
69,097✔
2194

2195
      else if (req_min .gt. mem%available) then
×
2196

2197
         ! Hack because intel flips out if we put two functions in chars=[]
2198
         reqChar = mem%get_memory_as_character(req_min, .true.)
×
2199
         call output%printf('m', 'Need at least (a0) but only have (a0)', &
2200
                            chars=[reqChar, mem%get_memory_as_character(mem%available, .true.)])
×
2201
         call output%error_msg('Not enough memory for a batch.')
×
2202

2203
      else
2204

2205
         ! First, try to increment all indices simultaneously
2206

2207
         p_elements = 1
2208
         q_elements = 1
2209
         r_elements = 1
2210

2211
         found_batch_size = .false.
2212
         p_incremented = .true.
2213
         q_incremented = .true.
2214
         r_incremented = .true.
2215

2216
         do while (.not. found_batch_size &
2217
                   .and. (p_incremented .or. q_incremented .or. r_incremented))
×
2218

2219
            if (int(p_elements) .lt. batch_p%index_dimension) then
×
2220
               p_elements = p_elements + 1
×
2221
               p_incremented = .true.
2222
            else
2223
               p_incremented = .false.
2224
            endif
2225

2226
            if (int(q_elements) .lt. batch_q%index_dimension) then
×
2227
               q_elements = q_elements + 1
×
2228
               q_incremented = .true.
2229
            else
2230
               q_incremented = .false.
2231
            endif
2232

2233
            if (int(r_elements) .lt. batch_r%index_dimension) then
×
2234
               r_elements = r_elements + 1
×
2235
               r_incremented = .true.
2236
            else
2237
               r_incremented = .false.
2238
            endif
2239

2240
            if (    p_elements*q_elements*r_elements*req3_min    &
×
2241
                  + p_elements*q_elements           *req2_pq_min &
2242
                  + p_elements*r_elements           *req2_pr_min &
2243
                  + q_elements*r_elements           *req2_qr_min &
2244
                  + p_elements                      *req1_p_min  &
2245
                  + q_elements                      *req1_q_min  &
2246
                  + r_elements                      *req1_r_min  &
2247
                  + req0_tot .ge. mem%available) then
×
2248

2249
               found_batch_size = .true.       ! cannot hold +1 batch size
2250
               if (p_incremented) p_elements = p_elements - 1
×
2251
               if (q_incremented) q_elements = q_elements - 1
×
2252
               if (r_incremented) r_elements = r_elements - 1
×
2253

2254
            endif
2255

2256
         enddo
2257

2258
         batch_p%max_length = int(p_elements)
×
2259
         batch_q%max_length = int(q_elements)
×
2260
         batch_r%max_length = int(r_elements)
×
2261

2262
         ! Figure out how many batches
2263

2264
         batch_p%num_batches = (batch_p%index_dimension-1)/(batch_p%max_length)+1
×
2265
         batch_q%num_batches = (batch_q%index_dimension-1)/(batch_q%max_length)+1
×
2266
         batch_r%num_batches = (batch_r%index_dimension-1)/(batch_r%max_length)+1
×
2267

2268
      endif
2269

2270
      if (force_batch) then
2271

2272
         call batch_p%force_batch()
34,295✔
2273

2274
         if (batch_p%index_dimension .eq. batch_q%index_dimension .and. &
34,295✔
2275
             batch_p%index_dimension .eq. batch_r%index_dimension) then
2276

2277
            batch_q%max_length  = batch_p%max_length
33,365✔
2278
            batch_q%num_batches = batch_p%num_batches
33,365✔
2279

2280
            batch_r%max_length  = batch_p%max_length
33,365✔
2281
            batch_r%num_batches = batch_p%num_batches
33,365✔
2282

2283
         else
2284

2285
            call batch_q%force_batch()
930✔
2286
            call batch_r%force_batch()
930✔
2287

2288
         end if
2289

2290
      endif
2291

2292
      max_memory_usage = req0_tot + &
2293
                         req1_p_min*int(batch_p%max_length, kind=i64)  +   &
2294
                         req1_q_min*int(batch_q%max_length, kind=i64)  +   &
2295
                         req1_r_min*int(batch_r%max_length, kind=i64)  +   &
2296
                         req2_pq_min*int(batch_p%max_length, kind=i64)     &
2297
                                    *int(batch_q%max_length, kind=i64) +   &
2298
                         req2_pr_min*int(batch_p%max_length, kind=i64)     &
2299
                                    *int(batch_r%max_length, kind=i64) +   &
2300
                         req2_qr_min*int(batch_q%max_length, kind=i64)     &
2301
                                    *int(batch_r%max_length, kind=i64) +   &
2302
                         req3_min   *int(batch_p%max_length, kind=i64)     &
2303
                                    *int(batch_q%max_length, kind=i64)     &
2304
                                    *int(batch_r%max_length, kind=i64)
69,097✔
2305

2306
      if (batch_p%num_batches .eq. 1 .and. &
2307
          batch_q%num_batches .eq. 1 .and. &
69,097✔
2308
          batch_r%num_batches .eq. 1) then
2309

2310
         if (present(req_single_batch)) then
34,802✔
2311

2312
            max_memory_usage = int(req_single_batch, kind=i64) * int(e_size, kind=i64)
33,833✔
2313

2314
         endif
2315

2316
      endif
2317

2318
      if (any([batch_p%num_batches, batch_q%num_batches, batch_r%num_batches] > 1)) then
69,097✔
2319
         call output%printf('v', 'Batching in (a0)', chars=[tag])
102,885✔
2320
      end if
2321

2322
      call mem%initialize_batching_tracker(max_memory_usage, tag)
69,097✔
2323

2324
   end subroutine batch_setup_3
138,194✔
2325

2326

2327
end module memory_manager_class
75,492,250✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc