Octopus
isdf_serial.F90
Go to the documentation of this file.
1!! Copyright (C) 2024 - 2025. Alexander Buccheri
2!!
3!! This program is free software; you can redistribute it and/or modify
4!! it under the terms of the GNU General Public License as published by
5!! the Free Software Foundation; either version 2, or (at your option)
6!! any later version.
7!!
8!! This program is distributed in the hope that it will be useful,
9!! but WITHOUT ANY WARRANTY; without even the implied warranty of
10!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11!! GNU General Public License for more details.
12!!
13!! You should have received a copy of the GNU General Public License
14!! along with this program; if not, write to the Free Software
15!! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16!! 02110-1301, U
17
18#include "global.h"
19
22 use, intrinsic :: iso_fortran_env, only: real64
23 use batch_oct_m
25 use blas_oct_m
26 use debug_oct_m
30 use global_oct_m
31 use grid_oct_m
32 use ions_oct_m
36 use math_oct_m
37 use mesh_oct_m
40 use mpi_oct_m, only: mpi_world
48 use space_oct_m
51
52 implicit none
53 private
54
55 ! TODO(Alex) Issue #1195 Extend ISDF to spin-polarised systems
57 integer, parameter :: ik = 1
58
60 type isdf_options_t
61 integer :: n_interp
62 contains
63 procedure :: init => isdf_options_init
64 end type isdf_options_t
65
66 public :: &
70
71contains
72
74 subroutine isdf_options_init(this, namespace, st)
75 class(isdf_options_t), intent(out) :: this
76 type(namespace_t), intent(in ) :: namespace
77 type(states_elec_t), intent(in ) :: st
78
79 integer :: default_n_interp
80
81 push_sub(isdf_options_init)
82
83 default_n_interp = 10 * highest_occupied_index(st, ik)
84
85 !%Variable NCentroidPoints
86 !%Type integer
87 !%Default 10*Nocc
88 !%Section ISDF
89 !%Description
90 !% The total number of interpolation points used in ISDF density fitting.
91 !% This number should be considerably smaller than the number of grid points.
92 !% By default, Octopus sets the number of interpolation points to 10x the number
93 !% of occupied states.
94 !%End
95 call parse_variable(namespace, 'NCentroidPoints', default_n_interp, this%n_interp)
96
97 pop_sub(isdf_options_init)
98
99 end subroutine isdf_options_init
100
101
105 subroutine serial_interpolative_separable_density_fitting_vectors(namespace, mesh, st, int_indices, phi, isdf_vectors)
106 type(namespace_t), intent(in ) :: namespace
107 class(mesh_t), intent(in ) :: mesh
108 type(states_elec_t), intent(in ) :: st
109 integer(int64), intent(in ) :: int_indices(:)
110 real(real64), allocatable, intent(out) :: phi(:, :)
111 ! of shape either:
112 ! Packed (nst, npg)
113 ! Unpacked (npg, nst)
114 real(real64), allocatable, intent(out) :: isdf_vectors(:, :)
115
116 integer :: nocc
117
119
120 message(1) = "Serial ISDF"
121 call messages_write(1)
122
123 ! Reference implementation not parallel in states or domain
124 if (st%parallel_in_states .or. mesh%parallel_in_domains) then
125 message(1) = "Serial ISDF called when running state or domain-parallel"
126 call messages_fatal(1)
127 endif
128
129 ! TODO(Alex) Issue #1195 Extend ISDF to spin-polarised systems
130 if (st%d%nspin > 1) then
131 call messages_not_implemented("ISDF Serial for SPIN_POLARIZED and SPINOR calculations", namespace)
132 endif
133
134 ! TODO(Alex) Issue #1196 Template ISDF handle both real and complex states
135 if (.not. states_are_real(st)) then
136 call messages_not_implemented("ISDF Serial handling of complex states", namespace)
137 endif
138
139 nocc = highest_occupied_index(st, ik)
140
141 ! Alternate means of constructing phi from a batch - retained for reference.
142 ! Dress in a fake code path so the compiler does not warn that the routine is unused.
143 if (.false.) then
144 call collate_batches(mesh%np, st, nocc, phi)
145 if (debug%info) call output_matrix(namespace, "phi_r_serial.txt", phi)
146 safe_deallocate_a(phi)
147 endif
148
149 ! This gives identical results to the above
150 call collate_batches_get_state(mesh, st, nocc, phi)
151 if (debug%info) call output_matrix(namespace, "phi_r_serial3.txt", phi)
152
153 select case (st%group%psib(st%group%block_start, 1)%status())
154 case (batch_device_packed)
155 message(1) = "Serial ISDF not implemented for BATCH_DEVICE_PACKED"
156 call messages_fatal(1)
157
158 case (batch_packed)
159 call isdf_construct_interpolation_vectors_packed(namespace, mesh, phi, int_indices, isdf_vectors)
160
161 case (batch_not_packed)
162 message(1) = "Serial ISDF not implemented for BATCH_NOT_PACKED"
163 call messages_fatal(1)
164 assert(all(shape(phi) == [mesh%np, st%nst]))
165
166 end select
169
171
172
176 subroutine isdf_construct_interpolation_vectors_packed(namespace, mesh, phi, indices, isdf_vectors)
177 type(namespace_t), intent(in ) :: namespace
178 class(mesh_t), intent(in ) :: mesh
179 real(real64), intent(in ) :: phi(:, :)
180 integer(int64), intent(in ) :: indices(:)
181 real(real64), allocatable, intent(out) :: isdf_vectors(:, :)
182
183 real(real64), allocatable :: phi_mu(:, :)
184 real(real64), allocatable :: P_mu_nu(:, :)
185 real(real64), allocatable :: zct(:, :)
186 real(real64), allocatable :: cct(:, :)
187
188 integer :: n_int, i, j, n_states
189 logical, parameter :: construct_P_mu_nu = .false.
190
192
193 assert(size(phi, 2) == mesh%np)
194
195 n_states = size(phi, 1)
196 n_int = size(indices)
197
198 safe_allocate(phi_mu(1:n_states, 1:n_int))
199 call sample_phi_at_centroids(phi, indices, phi_mu)
200 if (debug%info) call output_matrix(namespace, "phi_mu_serial.txt", phi_mu)
201
202 ! Note, this actually constructs P_r_mu, but we want to mutate the memory in the subsequent call
203 safe_allocate(zct(1:mesh%np, 1:n_int))
204 call construct_density_matrix_packed(phi, phi_mu, zct)
205 if (debug%info) call output_matrix(namespace, "p_r_mu_serial.txt", zct)
206
207 ! [ZC^T] = P_r_mu o P_r_mu
208 call construct_zct(zct)
209 if (debug%info) call output_matrix(namespace, "zct_serial.txt", zct)
210
211 ! [CC^T] = ZC^T[indices, :]
212 safe_allocate(cct(1:n_int, 1:n_int))
213 call construct_cct(indices, zct, cct)
214 if (debug%info) call output_matrix(namespace, "cct_serial.txt", cct)
215 assert(is_symmetric(cct))
216
217 ! For testing, alternately, construct [CC^T] = P_mu_nu o P_mu_nu
218 if (construct_p_mu_nu) then
219 safe_allocate(p_mu_nu(1:n_int, 1:n_int))
221 assert(is_symmetric(p_mu_nu))
222
223 safe_deallocate_a(phi_mu)
224 if (debug%info) call output_matrix(namespace, "p_mu_nu_serial.txt", p_mu_nu)
225
226 ! Mutate P_mu_nu -> cct
227 write(message(1),'(a)') "ISDF Serial: Constructing [CC^T] = P_mu_nu o P_mu_nu"
228 call messages_info(1, namespace=namespace, debug_only=.true.)
229
230 do j = 1, n_int
231 do i = 1, n_int
232 p_mu_nu(i, j) = p_mu_nu(i, j) * p_mu_nu(i, j)
233 enddo
234 enddo
235
236 if (debug%info) call output_matrix(namespace, "cct_alt_serial.txt", p_mu_nu)
237 safe_deallocate_a(p_mu_nu)
238 endif
239
240 ! [CC^T]^{-1}, mutating cct in-place
241 ! NOTE, CC^T is extremely ill-conditioned, and it is not clear why this is the case.
242 ! * Using the pseudo-inverse (SVD) circumvents this problem, without addressing the route course
243 ! As CC^T and its inverse should be symmetric, ultimately want to:
244 ! * Only compute the inverse of the upper (or lower) triangle
245 ! * Modify the GEMM operation below to only use the upper (or lower) triangle of [CC^T]^{-1}
246 write(message(1),'(a)') "ISDF Serial: Inverting [CC^T]"
247 call messages_info(1, namespace=namespace, debug_only=.true.)
248
249 ! Invert [CC^T] and symmetrise
250 call lalg_svd_inverse(n_int, n_int, cct)
251 call symmetrize_matrix(n_int, cct)
252
253 ! Compute interpolation vectors, [ZC^T] [CC^T]^{-1}
254 safe_allocate(isdf_vectors(1:mesh%np, 1:n_int))
255
256 ! CC^T is by definition symmetric, implying [CC^T]^{-1} also is
257 call lalg_gemm(mesh%np, n_int, n_int, 1.0_real64, zct, cct, 0.0_real64, isdf_vectors)
258
259 if (debug%info) call output_matrix(namespace, "isdf_serial.txt", isdf_vectors)
260 safe_deallocate_a(zct)
261 safe_deallocate_a(cct)
262
264
266
267
271 subroutine collate_batches_get_state(mesh, st, max_state, psi)
272 class(mesh_t), intent(in ) :: mesh
273 type(states_elec_t), intent(in ) :: st
274 integer, intent(in ) :: max_state
275 real(real64), allocatable, intent(out) :: psi(:, :)
276
277 integer :: istate, ib, ist, minst, maxst, block_end
278
280
281 assert(max_state <= st%nst)
282
283 safe_allocate(psi(1:max_state, 1:mesh%np))
284 block_end = st%group%iblock(max_state)
285
286 istate = 0
287 do ib = 1, block_end
288 ! Normalisation did not affect condition number of CC^T matrix
289 !call dmesh_batch_normalize(mesh, st%group%psib(ib, ik))
290 minst = states_elec_block_min(st, ib)
291 maxst = min(states_elec_block_max(st, ib), max_state)
292 do ist = minst, maxst
293 istate = istate + 1
294 ! In case there are any states < max_state that are not occupied
295 if (abs(st%occ(ist, ik) * st%kweights(ik)) < m_min_occ) then
296 psi(istate, :) = 0.0_real64
297 else
298 call states_elec_get_state(st, mesh, st%d%dim, ist, ik, psi(istate, :))
299 endif
300 enddo
301 enddo
302
304
305 end subroutine collate_batches_get_state
306
307
309 subroutine collate_batches(np, st, max_state, psi)
310 integer, intent(in ) :: np
311 type(states_elec_t), intent(in ) :: st
312 integer, intent(in) :: max_state
313 real(real64), allocatable, intent(out) :: psi(:, :)
314
315 integer :: ip, ib, minst, maxst, block_end, ist, ist_local
316
317 push_sub(collate_batches)
318
319 select case (st%group%psib(1,1)%status())
320 case (batch_device_packed)
321 ! No GPU implementation
322 assert(.false.)
323
324 case (batch_packed)
325
326 ! Not state distributed, so expect ist to run [1: max_state], contiguously
327 safe_allocate(psi(1:max_state, 1:np))
328 block_end = st%group%iblock(max_state)
329
330 do ip = 1, np
331 do ib = 1, block_end
332 minst = states_elec_block_min(st, ib)
333 ! Truncate maxst to max_state in last block
334 maxst = min(states_elec_block_max(st, ib), max_state)
335 ! Assign states from current batch
336 do ist = minst, maxst
337 ist_local = ist - minst + 1
338 ! In case there are any states < max_state that are not occupied
339 if (abs(st%occ(ist, ik) * st%kweights(ik)) < m_min_occ) then
340 psi(ist, ip) = 0.0_real64
341 else
342 psi(ist, ip) = st%group%psib(ib, ik)%dff_pack(ist_local, ip)
343 endif
344 enddo
345 enddo
346 enddo
347
348 case (batch_not_packed)
349 assert(.false.)
350
351 end select
352
353 pop_sub(collate_batches)
354
355 end subroutine collate_batches
356
357
359 subroutine sample_phi_at_centroids(phi_r, indices, phi_mu)
360 real(real64), intent(in ) :: phi_r(:, :)
361 integer(int64), intent(in ) :: indices(:)
362 real(real64), intent(out) :: phi_mu(:, :)
363
364 integer :: ic, is, nst, n_int
365 integer(int64) :: ipg
366
368
369 write(message(1),'(a)') "ISDF Serial: Sampling phi(r) at mu"
370 call messages_info(1, debug_only=.true.)
371
372 nst = size(phi_r, 1)
373 assert(size(phi_mu, 1) == nst)
374
375 n_int = size(indices)
376 assert(size(phi_mu, 2) == n_int)
377
378 do ic = 1, n_int
379 ipg = indices(ic)
380 do is = 1, nst
381 phi_mu(is, ic) = phi_r(is, ipg)
382 enddo
383 enddo
384
386
387 end subroutine sample_phi_at_centroids
388
389
400 subroutine construct_zct(zct)
401 real(real64), intent(inout) :: zct(:, :)
402 ! Out: Contraction of Z and C^T matrices == element-wise square of quasi-density matrix
403
404 integer :: i, j
405
406 push_sub_with_profile(construct_zct)
407
408 write(message(1),'(a)') "ISDF Serial: Constructing ZC^T"
409 call messages_info(1, debug_only=.true.)
410
411 !$omp parallel do collapse(2)
412 do j = 1, size(zct, 2)
413 do i = 1, size(zct, 1)
414 zct(i, j) = zct(i, j)**2
415 end do
416 enddo
417 !$omp end parallel do
418
419 pop_sub_with_profile(construct_zct)
420
421 end subroutine construct_zct
422
423
426 subroutine construct_cct(indices, zct, cct)
427 integer(int64), intent(in ) :: indices(:)
428 real(real64), intent(in ) :: zct(:, :)
429
430 real(real64), intent(out) :: cct(:, :)
431
432 integer(int64) :: ipg
433 integer :: i_mu, i_nu, n_int
434
435 push_sub_with_profile(construct_cct)
436
437 write(message(1),'(a)') "ISDF Serial: Constructing CC^T by sampling ZC^T"
438 call messages_info(1, debug_only=.true.)
439
440 n_int = size(indices)
441 assert(all(shape(cct) == [n_int, n_int]))
442 assert(size(zct, 1) > n_int)
443 assert(size(zct, 2) == n_int)
444
445 ! Mask ZC^T to obtain CC^T
446 do i_nu = 1, n_int
447 do i_mu = 1, n_int
448 ipg = indices(i_mu)
449 cct(i_mu, i_nu) = zct(ipg, i_nu)
450 enddo
451 enddo
453 pop_sub_with_profile(construct_cct)
454
455 end subroutine construct_cct
456
457
466 subroutine construct_density_matrix_packed(phi, phi_mu, P_r_mu)
467 real(real64), intent(in ) :: phi(:, :)
468 ! of shape (m_states, np)
469 real(real64), intent(in ) :: phi_mu(:, :)
470
471 real(real64), intent(out) :: P_r_mu(:, :)
472
473 integer :: np
474 integer :: n_int
475 integer :: m_states
476
477 push_sub_with_profile(construct_density_matrix_packed)
478
479 write(message(1),'(a)') "ISDF Serial: Constructing P_r_mu"
480 call messages_info(1, debug_only=.true.)
481
482 m_states = size(phi, 1)
483 np = size(phi, 2)
484 n_int = size(phi_mu, 2)
485
486 assert(size(phi_mu, 1) == m_states)
487 assert(size(p_r_mu, 1) == np)
488 assert(size(p_r_mu, 2) == n_int)
489
490 ! Contract over the state index, P = phi^T @ phi_mu, with shape (np, m_states) (m_states, n_int)
491 call lalg_gemm(phi, phi_mu, p_r_mu, transa='T')
492
494
496
497
506 subroutine construct_density_matrix_all_centroids_packed(phi_mu, P_mu_nu)
507 real(real64), intent(in ) :: phi_mu(:, :)
508 ! of shape (m_states, n_int)
509 real(real64), intent(out) :: P_mu_nu(:, :)
510
511 integer :: n_int
512
514
515 write(message(1),'(a)') "ISDF Serial: Constructing P_mu_nu"
516 call messages_info(1, debug_only=.true.)
517
518 n_int = size(phi_mu, 2)
519 assert(size(p_mu_nu, 1) == n_int)
520 assert(size(p_mu_nu, 2) == n_int)
521
522 ! Contract over the state index
523 call lalg_gemm(phi_mu, phi_mu, p_mu_nu, transa='T')
524
526
528
529
531 subroutine quantify_error_and_visualise(namespace, st, space, mesh, ions, phi, indices, isdf_vectors, output_cubes)
532 type(namespace_t), intent(in) :: namespace
533 type(states_elec_t), intent(in) :: st
534 class(space_t), intent(in) :: space
535 class(mesh_t), intent(in) :: mesh
536 class(ions_t), pointer, intent(in) :: ions
537 real(real64), allocatable, intent(inout) :: phi(:, :)
538 integer(int64), intent(in) :: indices(:)
539 real(real64), allocatable, intent(inout) :: isdf_vectors(:, :)
540 logical, intent(in) :: output_cubes
541
542 real(real64), allocatable :: product_basis(:, :), approx_product_basis(:, :)
543 real(real64), allocatable :: phi_mu(:, :), phi_occ(:, :)
544 real(real64), allocatable :: product_error(:)
545 integer :: n_occ, n_products, n_int, i, j, ij, is, ip, unit
546 real(real64) :: mean_error
547
549
550 write(message(1),'(a)') "ISDF Serial: Computing exact pair products"
551 call messages_info(1, debug_only=.true.)
552
553 assert(size(phi, 2) == mesh%np)
554
555 ! Limit product basis comparison to occupied states
556 n_occ = highest_occupied_index(st, ik)
557 safe_allocate(phi_occ(1:n_occ, 1:mesh%np))
558 do ip = 1, mesh%np
559 do is = 1, n_occ
560 phi_occ(is, ip) = phi(is, ip)
561 enddo
562 enddo
563 safe_deallocate_a(phi)
564
565 n_products = n_occ * n_occ
566 safe_allocate(product_basis(1:n_products, 1:mesh%np))
567 call column_wise_khatri_rao_product(phi_occ, phi_occ, product_basis)
568 ! if (debug%info) call output_matrix(namespace, "exact_product_blas.txt", product_basis)
569
570 if (output_cubes) then
571 call generate_product_state_cubes(namespace, space, mesh, ions, "exact_product_", &
572 product_basis)
573 endif
574
575 write(message(1),'(a)') "ISDF Serial Test: Computing approximate pair products"
576 call messages_info(1, namespace=namespace, debug_only=.true.)
577
578 ! Rebuild phi_mu, again only for occupied states
579 n_int = size(indices)
580 safe_allocate(phi_mu(1:n_occ, 1:n_int))
581 call sample_phi_at_centroids(phi_occ, indices, phi_mu)
582 safe_deallocate_a(phi_occ)
583
584 safe_allocate(approx_product_basis(1:n_products, 1:mesh%np))
585 call approximate_pair_products(phi_mu, isdf_vectors, approx_product_basis)
586 ! if (debug%info) call output_matrix(namespace, "approx_product_blas.txt", approx_product_basis)
587
588 safe_deallocate_a(phi_mu)
589 safe_deallocate_a(isdf_vectors)
590
591 if (output_cubes) then
592 call generate_product_state_cubes(namespace, space, mesh, ions, "approx_product_", &
593 approx_product_basis)
594 endif
595
596 ! Quantify the error
597 safe_allocate(product_error(1:n_products))
598 call error_in_product_basis(mesh, product_basis, approx_product_basis, product_error, mean_error)
599 safe_deallocate_a(product_basis)
600 safe_deallocate_a(approx_product_basis)
601
602 if (mpi_world%is_root()) then
603 open(newunit=unit, file="isdf_error_serial.txt")
604 write(unit, *) 'Mean error', mean_error
605 ij = 0
606 do i = 1, n_occ
607 do j = 1, n_occ
608 ij = ij + 1
609 write(unit, *) i, j, product_error(ij)
610 enddo
611 enddo
612 close(unit)
613 endif
614
615 safe_deallocate_a(product_error)
616
618
619 end subroutine quantify_error_and_visualise
620
621
632 subroutine approximate_pair_products(psi_mu, zeta, product_basis)
633 real(real64), intent(in ) :: psi_mu(:, :)
634 real(real64), intent(in ) :: zeta(:, :)
635 real(real64), intent(out) :: product_basis(:, :)
636
637 real(real64), allocatable :: psi_ij_mu(:, :)
638 integer :: mn_states, n_int, np
639
641
642 mn_states = size(psi_mu, 1)**2
643 np = size(zeta, 1)
644 n_int = size(zeta, 2)
645
646 assert(size(product_basis, 1) == mn_states)
647 assert(size(product_basis, 2) == np)
648
649 safe_allocate(psi_ij_mu(1:mn_states, 1:n_int))
650 call column_wise_khatri_rao_product(psi_mu, psi_mu, psi_ij_mu)
651
652 ! Contract product_basis = [psi_ij_mu] [zeta]^T over interpolation vector index
653 call lalg_gemm(psi_ij_mu, zeta, product_basis, transb='T')
654
655 safe_deallocate_a(psi_ij_mu)
656
658
659 end subroutine approximate_pair_products
660
661
669 subroutine error_in_product_basis(mesh, product_basis, approx_product_basis, error, mean_error)
670 class(mesh_t), intent(in ) :: mesh
671 real(real64), intent(in ) :: product_basis(:, :)
672 real(real64), intent(in ) :: approx_product_basis(:, :)
673
674 real(real64), intent(out) :: error(:)
675 real(real64), intent(out) :: mean_error
676
677 integer :: mn_states, np, ij, ip
678
679 push_sub(error_in_product_basis)
680
681 mn_states = size(product_basis, 1)
682 np = size(product_basis, 2)
683
684 ! product_basis shape is not as expected
685 assert(mesh%np == np)
686
687 ! Two arrays should be the same dimensions
688 assert(all(shape(product_basis) == shape(approx_product_basis)))
689
690 ! error should be allocated, and with the correct size
691 assert(size(error) == mn_states)
692
693 ! Initialise error with first point from the grid
694 do ij = 1, mn_states
695 error(ij) = (product_basis(ij, 1) - approx_product_basis(ij, 1))**2
696 enddo
697
698 do ip = 2, np
699 do ij = 1, mn_states
700 error(ij) = error(ij) + (product_basis(ij, ip) - approx_product_basis(ij, ip))**2
701 enddo
702 enddo
703
704 mean_error = 0.0_real64
705 do ij = 1, mn_states
706 error(ij) = sqrt(mesh%volume_element * error(ij))
707 mean_error = mean_error + error(ij)
708 enddo
709
710 mean_error = mean_error / real(mn_states, real64)
711
713
714 end subroutine error_in_product_basis
715
716
718 subroutine output_matrix(namespace, fname, matrix)
719 type(namespace_t), intent(in) :: namespace
720 character(len=*), intent(in) :: fname
721 real(real64), intent(in) :: matrix(:, :)
722
723 integer :: i, j, unit
724
726
727 write(message(1),'(a)') "ISDF Serial. Outputting: " // trim(adjustl(fname))
728 call messages_info(1, namespace=namespace, debug_only=.true.)
729
730 if (mpi_world%is_root()) then
731
732 open(newunit=unit, file=trim(adjustl(fname)))
733 do j = 1, size(matrix, 2)
734 do i = 1, size(matrix, 1)
735 write(unit, *) matrix(i, j)
736 enddo
737 enddo
738 close(unit)
739
740 endif
741
742 pop_sub(output_matrix)
743
744 end subroutine output_matrix
745
746
748 subroutine generate_product_state_cubes(namespace, space, mesh, ions, file_prefix, data, limits)
749 type(namespace_t), intent(in) :: namespace
750 class(space_t), intent(in) :: space
751 class(mesh_t), intent(in) :: mesh
752 class(ions_t), pointer, intent(in) :: ions
753 character(len=*), intent(in) :: file_prefix
754 real(real64), intent(in) :: data(:, :)
755 integer, optional, intent(in) :: limits(2)
756
757 integer :: m_states, limit_j, limit_i, i, j, ij, ierr
758 real(real64) :: size_data
759 character(len=4) :: i_char, j_char
760 character(len=120) :: file_name
761
762 ! product basis size is currently defined as (m_states * m_states)
763 size_data = real(size(data, 1), real64)
764 m_states = int(sqrt(size_data))
765
766 if (present(limits)) then
767 limit_j = limits(1)
768 limit_i = limits(2)
769 else
770 limit_j = m_states
771 limit_i = m_states
772 endif
773
774 do i = 1, limit_i
775 do j = 1, limit_j
776 ij = j + (i - 1) * m_states
777 write(i_char, '(I4)') i
778 write(j_char, '(I4)') j
779 file_name = trim(adjustl(file_prefix)) // trim(adjustl(i_char)) // '_' // trim(adjustl(j_char))
780 call dio_function_output(option__outputformat__cube, "./cubes", trim(adjustl(file_name)), namespace, space, mesh, &
781 data(ij,:) , unit_one, ierr, pos=ions%pos, atoms=ions%atom)
782 enddo
783 enddo
784
785 end subroutine generate_product_state_cubes
786
787
797 function highest_occupied_index(st, ik_index) result(i_max_occ)
798 type(states_elec_t), intent(in) :: st
799 integer, intent(in) :: ik_index
800 integer :: i_max_occ
801
802 integer :: ist
803
804 push_sub(highest_occupied_index)
805
806 if (st%smear%method /= smear_semiconductor) then
807 i_max_occ = st%nst
808 endif
809
810 i_max_occ = 0
811 do ist = 1, st%nst
812 if (abs(st%occ(ist, ik_index)) < m_min_occ) exit
813 i_max_occ = ist
814 enddo
815 assert(i_max_occ > 0)
816
818
819 end function highest_occupied_index
820
821end module isdf_serial_oct_m
822
823!! Local Variables:
824!! mode: f90
825!! coding: utf-8
826!! End:
double sqrt(double __x) __attribute__((__nothrow__
This module implements batches of mesh functions.
Definition: batch.F90:133
This module implements common operations on batches of mesh functions.
Definition: batch_ops.F90:116
This module contains interfaces for BLAS routines You should not use these routines directly....
Definition: blas.F90:118
This module implements the underlying real-space grid.
Definition: grid.F90:117
Serial prototype for benchmarking and validating ISDF implementation.
subroutine, public serial_interpolative_separable_density_fitting_vectors(namespace, mesh, st, int_indices, phi, isdf_vectors)
Compute a set of ISDF interpolation vectors in serial, for code validation.
subroutine, public quantify_error_and_visualise(namespace, st, space, mesh, ions, phi, indices, isdf_vectors, output_cubes)
Wrapper for quantifying the error in the expansion of the product basis.
subroutine generate_product_state_cubes(namespace, space, mesh, ions, file_prefix, data, limits)
Helper function to output a set of pair product states.
subroutine output_matrix(namespace, fname, matrix)
Helper routine to output a 2D matrix.
subroutine collate_batches_get_state(mesh, st, max_state, psi)
Loop over states per block, which makes applying the maximum state limit much simpler Use this to com...
subroutine isdf_options_init(this, namespace, st)
Initialise isdf_inp_options_t instance.
integer function highest_occupied_index(st, ik_index)
Return the index of highest occupied Kohn-Sham state for k-point ik.
subroutine sample_phi_at_centroids(phi_r, indices, phi_mu)
Sample KS states at centroid points.
subroutine collate_batches(np, st, max_state, psi)
Put batches into a single 2D array.
subroutine construct_density_matrix_all_centroids_packed(phi_mu, P_mu_nu)
@ brief Construct the density matrix with shape (n_int, n_int). Denoted packed, because it expects ph...
subroutine error_in_product_basis(mesh, product_basis, approx_product_basis, error, mean_error)
Quantify the error in the product basis expansion.
subroutine construct_zct(zct)
Construct the product of Z and C matrices from the element-wise product of the quasi-density matrix.
subroutine isdf_construct_interpolation_vectors_packed(namespace, mesh, phi, indices, isdf_vectors)
Compute a set of ISDF interpolation vectors, where intermediate quantities such as phi are constructe...
subroutine construct_cct(indices, zct, cct)
Construct the product from by masking the first dimension of .
integer, parameter ik
Hard-coded for Gamma-point, spin-unpolarised calculations.
subroutine construct_density_matrix_packed(phi, phi_mu, P_r_mu)
@ brief Construct the density matrix with shape (np, n_int). Denoted packed, because it expects phi i...
subroutine approximate_pair_products(psi_mu, zeta, product_basis)
Construct a set of approximate pair products using the ISDF interpolation vectors.
This module is intended to contain "only mathematical" functions and procedures.
Definition: math.F90:115
This module defines functions over batches of mesh functions.
Definition: mesh_batch.F90:116
This module defines the meshes, which are used in Octopus.
Definition: mesh.F90:118
type(mpi_grp_t), public mpi_world
Definition: mpi.F90:270
integer, parameter, public smear_semiconductor
Definition: smear.F90:171
pure logical function, public states_are_real(st)
This module provides routines for communicating states when using states parallelization.
This module defines the unit system, used for input and output.
type(unit_t), public unit_angstrom
For XYZ files.
type(unit_t), public unit_one
some special units required for particular quantities
Class describing the electron system.
Definition: electrons.F90:218
int true(void)