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, int64
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
40 use math_oct_m
41 use mesh_oct_m
50 use space_oct_m
53 use xc_cam_oct_m, only: xc_cam_t
54
55 implicit none
56 private
57
58 public :: &
62
63 ! TODO(Alex) Issue #1195 Extend ISDF to spin-polarised systems
65 integer, parameter :: ik = 1
66
67contains
68
72 subroutine isdf_serial_interpolation_vectors(isdf, namespace, mesh, st, indices, phi_mu, P_r_mu, isdf_vectors)
73 type(isdf_options_t), intent(in ) :: isdf
74 type(namespace_t), intent(in ) :: namespace
75 class(mesh_t), intent(in ) :: mesh
76 type(states_elec_t), intent(in ) :: st
77 integer(int64), contiguous, intent(in ) :: indices(:)
78
79 real(real64), allocatable, intent(out) :: phi_mu(:, :)
80 ! defined at interpolation points: \f$ \varphi_i(\mathbf{r}_\mu) \f$
81 real(real64), allocatable, intent(out) :: P_r_mu(:, :)
82 ! \f$P_{\mathbf{r},\mu}\f$, with size (np, n_int)
83 real(real64), allocatable, intent(out) :: isdf_vectors(:, :)
84
85 real(real64), allocatable :: phi(:, :), cct(:, :)
86 integer :: n_states, n_int, rank
87 logical :: data_is_packed
88
89 push_sub_with_profile(isdf_serial_interpolation_vectors)
90 call messages_write(1)
91
92 ! Reference serial implementation not parallel in states or domain
93 if (st%parallel_in_states .or. mesh%parallel_in_domains) then
94 message(1) = "Serial ISDF called when running state or domain-parallel"
95 call messages_fatal(1)
96 endif
97
98 ! TODO(Alex) Issue #1195 Extend ISDF to spin-polarised systems
99 if (st%d%nspin > 1) then
100 call messages_not_implemented("ISDF Serial for SPIN_POLARIZED and SPINOR calculations", namespace)
101 endif
102
103 ! TODO(Alex) Issue #1196 Template ISDF handle both real and complex states
104 if (.not. states_are_real(st)) then
105 call messages_not_implemented("ISDF Serial handling of complex states", namespace)
106 endif
107
108 ! TODO(Alex) Implement algorithms for unpacked data structure
109 data_is_packed = st%group%psib(st%group%block_start, 1)%status() == batch_packed
110
111 if (.not. data_is_packed) then
112 message(1) = "Serial ISDF only implemented for BATCH_PACKED"
113 call messages_fatal(1)
114 endif
115
116 n_states = isdf%n_ks_states
117 n_int = size(indices)
118
119 call collate_batches_get_state(mesh, st, n_states, phi)
120 if (debug%info) call output_matrix(namespace, "phi_r_serial.txt", phi)
121
122 safe_allocate(phi_mu(1:n_states, 1:n_int))
123 call sample_phi_at_centroids(phi, indices, phi_mu)
124 if (debug%info) call output_matrix(namespace, "phi_mu_serial.txt", phi_mu)
125
126 safe_allocate(p_r_mu(1:mesh%np, 1:n_int))
127 call construct_density_matrix_packed(phi, phi_mu, p_r_mu)
128 if (debug%info) call output_matrix(namespace, "p_r_mu_serial.txt", p_r_mu)
129
130 ! Mutate P_r_mu to [ZC^T] = P_r_mu o P_r_mu
131 call construct_zct(p_r_mu)
132 if (debug%info) call output_matrix(namespace, "zct_serial.txt", p_r_mu)
133
134 ! [CC^T] = ZC^T[indices, :]
135 safe_allocate(cct(1:n_int, 1:n_int))
136 call construct_cct(indices, p_r_mu, cct)
137 if (debug%info) call output_matrix(namespace, "cct_serial.txt", cct)
138 assert(is_symmetric(cct))
139
140 ! Note, in principle one could use lalg_pseudo_inverse and just add an optional
141 ! arg for returning the rank, but annoyingly the criterion in that is > rather than >=.
142 ! Quantify the optimal number of interpolation points to use.
143 ! If the rank of CC^T is <= ISDFNpoints, this does not give any indication how many
144 ! additional points are required. It is only indicative when oversampling.
145 if (isdf%check_n_interp) then
146 rank = lalg_matrix_rank_svd(cct, preserve_mat=.true.)
147 write(message(1),'(a, I4)') "ISDF Serial: Rank of CC^T is ", rank
148 if (rank < n_int) then
149 write(message(2),'(a)') " - This rank is the optimal ISDFNpoints to run the calculation with"
150 else
151 write(message(2),'(a)') " - This suggests that ISDFNpoints is either optimal, or could be larger"
152 endif
153 call messages_info(2, namespace=namespace)
154 endif
155
156 ! [CC^T]^{-1}, mutating cct in-place
157 ! NOTE, if the number of interpolation points exceeds the rank of CC^T, CC^T is by definition
158 ! ill-conditioned, and requires inverting with the pseudo-inverse (SVD).
159 ! Tests show that this is a much better solution than regularisation of either the diagonals
160 ! or off-diagonals of CC^T. If one limits the number of interpolation points, there is no problem
161 ! with inversion, but this limits the total accuracy achievable with the method.
162
163 ! As CC^T and its inverse should be symmetric, ultimately want to:
164 ! * Only compute the inverse of the upper (or lower) triangle
165 ! * Modify the GEMM operation below to only use the upper (or lower) triangle of [CC^T]^{-1}
166 write(message(1),'(a)') "ISDF Serial: Inverting [CC^T]"
167 call messages_info(1, namespace=namespace, debug_only=.true.)
168
169 ! Invert [CC^T] and symmetrise
170 call lalg_svd_inverse(n_int, n_int, cct)
171 call symmetrize_matrix(n_int, cct)
172
173 ! Compute interpolation vectors, [ZC^T] [CC^T]^{-1}
174 safe_allocate(isdf_vectors(1:mesh%np, 1:n_int))
175 ! CC^T is by definition symmetric, implying [CC^T]^{-1} also is
176 call lalg_gemm(mesh%np, n_int, n_int, 1.0_real64, p_r_mu, cct, 0.0_real64, isdf_vectors)
177 if (debug%info) call output_matrix(namespace, "isdf_serial.txt", isdf_vectors)
178 safe_deallocate_a(cct)
179
180 ! Rebuild P_r_mu, with occupation numbers absorbed into it. Used in construction of W_ace
181 call construct_density_matrix_with_occ_packed(st, phi, phi_mu, p_r_mu)
182 safe_deallocate_a(phi)
183 if (debug%info) call output_matrix(namespace, "OccP_r_mu_serial.txt", p_r_mu)
184
185 pop_sub_with_profile(isdf_serial_interpolation_vectors)
186
188
189
193 subroutine collate_batches_get_state(mesh, st, max_state, psi)
194 class(mesh_t), intent(in ) :: mesh
195 type(states_elec_t), intent(in ) :: st
196 integer, intent(in ) :: max_state
197 real(real64), allocatable, intent(out) :: psi(:, :)
198
199 integer :: istate, ib, ist, minst, maxst, block_end
200
201 push_sub_with_profile(collate_batches_get_state)
202
203 assert(max_state > 0 .and. max_state <= st%nst)
204
205 safe_allocate(psi(1:max_state, 1:mesh%np))
206 block_end = st%group%iblock(max_state)
207
208 istate = 0
209 do ib = 1, block_end
210 ! Normalisation did not affect condition number of CC^T matrix
211 !call dmesh_batch_normalize(mesh, st%group%psib(ib, ik))
212 minst = states_elec_block_min(st, ib)
213 maxst = min(states_elec_block_max(st, ib), max_state)
214 do ist = minst, maxst
215 istate = istate + 1
216 call states_elec_get_state(st, mesh, st%d%dim, ist, ik, psi(istate, :))
217 enddo
218 enddo
219
220 pop_sub_with_profile(collate_batches_get_state)
221
222 end subroutine collate_batches_get_state
223
224
226 subroutine sample_phi_at_centroids(phi_r, indices, phi_mu)
227 real(real64), contiguous, intent(in ) :: phi_r(:, :)
228 integer(int64), contiguous, intent(in ) :: indices(:)
229 real(real64), contiguous, intent(out) :: phi_mu(:, :)
230
231 integer :: ic, is, nst, n_int
232 integer(int64) :: ipg
233
234 push_sub_with_profile(sample_phi_at_centroids)
235
236 write(message(1),'(a)') "ISDF Serial: Sampling phi(r) at mu"
237 call messages_info(1, debug_only=.true.)
238
239 nst = size(phi_r, 1)
240 assert(size(phi_mu, 1) == nst)
241
242 n_int = size(indices)
243 assert(size(phi_mu, 2) == n_int)
244
245 do ic = 1, n_int
246 ipg = indices(ic)
247 do is = 1, nst
248 phi_mu(is, ic) = phi_r(is, ipg)
249 enddo
250 enddo
251
252 pop_sub_with_profile(sample_phi_at_centroids)
253
254 end subroutine sample_phi_at_centroids
255
256
267 subroutine construct_zct(zct)
268 real(real64), contiguous, intent(inout) :: zct(:, :)
269 ! Out: Contraction of Z and C^T matrices == element-wise square of quasi-density matrix
270
271 integer :: i, j
272
273 push_sub_with_profile(construct_zct)
274
275 write(message(1),'(a)') "ISDF Serial: Constructing ZC^T"
276 call messages_info(1, debug_only=.true.)
277
278 !$omp parallel do collapse(2)
279 do j = 1, size(zct, 2)
280 do i = 1, size(zct, 1)
281 zct(i, j) = zct(i, j)**2
282 end do
283 enddo
284 !$omp end parallel do
285
286 pop_sub_with_profile(construct_zct)
287
288 end subroutine construct_zct
289
290
293 subroutine construct_cct(indices, zct, cct)
294 integer(int64), contiguous, intent(in ) :: indices(:)
295 real(real64), contiguous, intent(in ) :: zct(:, :)
296
297 real(real64), contiguous, intent(out) :: cct(:, :)
298
299 integer(int64) :: ipg
300 integer :: i_mu, i_nu, n_int
301
302 push_sub_with_profile(construct_cct)
303
304 write(message(1),'(a)') "ISDF Serial: Constructing CC^T by sampling ZC^T"
305 call messages_info(1, debug_only=.true.)
306
307 n_int = size(indices)
308 assert(all(shape(cct) == [n_int, n_int]))
309 assert(size(zct, 1) > n_int)
310 assert(size(zct, 2) == n_int)
311
312 ! Mask ZC^T to obtain CC^T
313 do i_nu = 1, n_int
314 do i_mu = 1, n_int
315 ipg = indices(i_mu)
316 cct(i_mu, i_nu) = zct(ipg, i_nu)
317 enddo
318 enddo
319
320 pop_sub_with_profile(construct_cct)
322 end subroutine construct_cct
323
324
333 subroutine construct_density_matrix_packed(phi, phi_mu, P_r_mu)
334 real(real64), contiguous, intent(in ) :: phi(:, :)
335 ! of shape (m_states, np)
336 real(real64), contiguous, intent(in ) :: phi_mu(:, :)
337
338 real(real64), contiguous, intent(out) :: P_r_mu(:, :)
339
340 integer :: np
341 integer :: n_int
342 integer :: m_states
343
344 push_sub_with_profile(construct_density_matrix_packed)
345
346 write(message(1),'(a)') "ISDF Serial: Constructing P_r_mu"
347 call messages_info(1, debug_only=.true.)
348
349 m_states = size(phi, 1)
350 np = size(phi, 2)
351 n_int = size(phi_mu, 2)
352
353 assert(size(phi_mu, 1) == m_states)
354 assert(size(p_r_mu, 1) == np)
355 assert(size(p_r_mu, 2) == n_int)
356
357 ! Contract over the state index, P = phi^T @ phi_mu, with shape (np, m_states) (m_states, n_int)
358 call lalg_gemm(phi, phi_mu, p_r_mu, transa='T')
359
360 pop_sub_with_profile(construct_density_matrix_packed)
361
363
364
365 subroutine construct_density_matrix_with_occ_packed(st, phi, phi_mu, P_r_mu)
366 type(states_elec_t), intent(in ) :: st
367 real(real64), contiguous, intent(in ) :: phi(:, :)
368 ! of shape (m_states, np)
369 real(real64), contiguous, intent(in ) :: phi_mu(:, :)
370
371 real(real64), contiguous, intent(out) :: P_r_mu(:, :)
372
373 integer :: np, n_int, m_states, imu, ist
374 real(real64), allocatable :: focc_phi_mu(:, :)
375
376 push_sub_with_profile(construct_p_with_occ_packed)
377
378 write(message(1),'(a)') "ISDF Serial: Constructing P_r_mu with occupations"
379 call messages_info(1, debug_only=.true.)
380
381 m_states = size(phi_mu, 1)
382 np = size(phi, 2)
383 n_int = size(phi_mu, 2)
384
385 assert(size(phi, 1) == m_states)
386 assert(size(p_r_mu, 1) == np)
387 assert(size(p_r_mu, 2) == n_int)
389 ! Element-wise multiply the occupations with the smaller of the two arrays
390 safe_allocate(focc_phi_mu(1:m_states, 1:n_int))
391 do imu = 1, n_int
392 do ist = 1, m_states
393 focc_phi_mu(ist, imu) = st%kweights(ik) * st%occ(ist, ik) * phi_mu(ist, imu)
394 enddo
395 enddo
396
397 ! Contract over the state index, P = phi^T @ focc_phi_mu, with shape (np, m_states) (m_states, n_int)
398 call lalg_gemm(phi, focc_phi_mu, p_r_mu, transa='T')
399 safe_deallocate_a(focc_phi_mu)
400
401 pop_sub_with_profile(construct_p_with_occ_packed)
402
404
405
407 subroutine quantify_error_and_visualise(isdf, namespace, st, space, mesh, ions, indices, isdf_vectors, output_cubes)
408 type(isdf_options_t), intent(in) :: isdf
409 type(namespace_t), intent(in) :: namespace
410 type(states_elec_t), intent(in) :: st
411 class(space_t), intent(in) :: space
412 class(mesh_t), intent(in) :: mesh
413 class(ions_t), pointer, intent(in) :: ions
414 integer(int64), contiguous, intent(in) :: indices(:)
415 real(real64), allocatable, intent(inout) :: isdf_vectors(:, :)
416 logical, intent(in) :: output_cubes
417
418 real(real64), allocatable :: product_basis(:, :), approx_product_basis(:, :)
419 real(real64), allocatable :: phi(:, :), phi_mu(:, :)
420 real(real64), allocatable :: product_error(:)
421 integer :: n_occ, n_products, n_int, i, j, ij, unit
422 real(real64) :: mean_error
423
424 push_sub_with_profile(quantify_error_and_visualise)
425
426 write(message(1),'(a)') "ISDF Serial: Computing exact pair products"
427 call messages_info(1, debug_only=.true.)
429 ! Rebuild phi matrix
430 n_occ = isdf%n_ks_states
431 call collate_batches_get_state(mesh, st, n_occ, phi)
432 assert(size(phi, 2) == mesh%np)
433
434 n_products = n_occ * n_occ
435 safe_allocate(product_basis(1:n_products, 1:mesh%np))
436 call column_wise_khatri_rao_product(phi, phi, product_basis)
437
438 if (output_cubes) then
439 call generate_product_state_cubes(namespace, space, mesh, ions, "exact_product_", &
440 product_basis)
441 endif
442
443 write(message(1),'(a)') "ISDF Serial Test: Computing approximate pair products"
444 call messages_info(1, namespace=namespace, debug_only=.true.)
445
446 ! Rebuild phi_mu, again only for occupied states
447 n_int = size(indices)
448 safe_allocate(phi_mu(1:n_occ, 1:n_int))
449 call sample_phi_at_centroids(phi, indices, phi_mu)
450 safe_deallocate_a(phi)
451
452 safe_allocate(approx_product_basis(1:n_products, 1:mesh%np))
453 call approximate_pair_products(phi_mu, isdf_vectors, approx_product_basis)
454 ! if (debug%info) call output_matrix(namespace, "approx_product_blas.txt", approx_product_basis)
455
456 safe_deallocate_a(phi_mu)
457 safe_deallocate_a(isdf_vectors)
458
459 if (output_cubes) then
460 call generate_product_state_cubes(namespace, space, mesh, ions, "approx_product_", &
461 approx_product_basis)
462 endif
463
464 ! Quantify the error
465 safe_allocate(product_error(1:n_products))
466 call error_in_product_basis(mesh, product_basis, approx_product_basis, product_error, mean_error)
467 safe_deallocate_a(product_basis)
468 safe_deallocate_a(approx_product_basis)
469
470 if (st%system_grp%is_root()) then
471 open(newunit=unit, file="isdf_error_serial.txt")
472 write(unit, *) 'Mean error', mean_error
473 ij = 0
474 do i = 1, n_occ
475 do j = 1, n_occ
476 ij = ij + 1
477 write(unit, *) i, j, product_error(ij)
478 enddo
479 enddo
480 close(unit)
481 endif
482
483 safe_deallocate_a(product_error)
484
485 pop_sub_with_profile(quantify_error_and_visualise)
486
487 end subroutine quantify_error_and_visualise
488
489
500 subroutine approximate_pair_products(psi_mu, zeta, product_basis)
501 real(real64), contiguous, intent(in ) :: psi_mu(:, :)
502 real(real64), contiguous, intent(in ) :: zeta(:, :)
503 real(real64), contiguous, intent(out) :: product_basis(:, :)
504
505 real(real64), allocatable :: psi_ij_mu(:, :)
506 integer :: mn_states, n_int, np
507
508 push_sub_with_profile(approximate_pair_products)
509
510 mn_states = size(psi_mu, 1)**2
511 np = size(zeta, 1)
512 n_int = size(zeta, 2)
513
514 assert(size(product_basis, 1) == mn_states)
515 assert(size(product_basis, 2) == np)
516
517 safe_allocate(psi_ij_mu(1:mn_states, 1:n_int))
518 call column_wise_khatri_rao_product(psi_mu, psi_mu, psi_ij_mu)
519
520 ! Contract product_basis = [psi_ij_mu] [zeta]^T over interpolation vector index
521 call lalg_gemm(psi_ij_mu, zeta, product_basis, transb='T')
522
523 safe_deallocate_a(psi_ij_mu)
524
525 pop_sub_with_profile(approximate_pair_products)
526
527 end subroutine approximate_pair_products
528
529
537 subroutine error_in_product_basis(mesh, product_basis, approx_product_basis, error, mean_error)
538 class(mesh_t), intent(in ) :: mesh
539 real(real64), contiguous, intent(in ) :: product_basis(:, :)
540 real(real64), contiguous, intent(in ) :: approx_product_basis(:, :)
541
542 real(real64), contiguous, intent(out) :: error(:)
543 real(real64), intent(out) :: mean_error
544
545 integer :: mn_states, np, ij, ip
546
547 push_sub_with_profile(error_in_product_basis)
548
549 mn_states = size(product_basis, 1)
550 np = size(product_basis, 2)
551
552 ! product_basis shape is not as expected
553 assert(mesh%np == np)
554
555 ! Two arrays should be the same dimensions
556 assert(all(shape(product_basis) == shape(approx_product_basis)))
557
558 ! error should be allocated, and with the correct size
559 assert(size(error) == mn_states)
560
561 ! Initialise error with first point from the grid
562 do ij = 1, mn_states
563 error(ij) = (product_basis(ij, 1) - approx_product_basis(ij, 1))**2
564 enddo
565
566 do ip = 2, np
567 do ij = 1, mn_states
568 error(ij) = error(ij) + (product_basis(ij, ip) - approx_product_basis(ij, ip))**2
569 enddo
570 enddo
571
572 mean_error = 0.0_real64
573 do ij = 1, mn_states
574 error(ij) = sqrt(mesh%volume_element * error(ij))
575 mean_error = mean_error + error(ij)
576 enddo
577
578 mean_error = mean_error / real(mn_states, real64)
579
580 pop_sub_with_profile(error_in_product_basis)
581
582 end subroutine error_in_product_basis
583
584
586 subroutine generate_product_state_cubes(namespace, space, mesh, ions, file_prefix, data, limits)
587 type(namespace_t), intent(in) :: namespace
588 class(space_t), intent(in) :: space
589 class(mesh_t), intent(in) :: mesh
590 class(ions_t), pointer, intent(in) :: ions
591 character(len=*), intent(in) :: file_prefix
592 real(real64), contiguous, intent(in) :: data(:, :)
593 integer, optional, intent(in) :: limits(2)
594
595 integer :: m_states, limit_j, limit_i, i, j, ij, ierr
596 real(real64) :: size_data
597 character(len=4) :: i_char, j_char
598 character(len=120) :: file_name
599
600 ! product basis size is currently defined as (m_states * m_states)
601 size_data = real(size(data, 1), real64)
602 m_states = int(sqrt(size_data))
603
604 if (present(limits)) then
605 limit_j = limits(1)
606 limit_i = limits(2)
607 else
608 limit_j = m_states
609 limit_i = m_states
610 endif
611
612 do i = 1, limit_i
613 do j = 1, limit_j
614 ij = j + (i - 1) * m_states
615 write(i_char, '(I4)') i
616 write(j_char, '(I4)') j
617 file_name = trim(adjustl(file_prefix)) // trim(adjustl(i_char)) // '_' // trim(adjustl(j_char))
618 call dio_function_output(option__outputformat__cube, "./cubes", trim(adjustl(file_name)), namespace, space, mesh, &
619 data(ij,:) , unit_one, ierr, pos=ions%pos, atoms=ions%atom)
620 enddo
621 enddo
622
623 end subroutine generate_product_state_cubes
624
625
639 subroutine isdf_serial_ace_compute_potentials(exxop, namespace, space, mesh, st, Vx_on_st, kpoints)
640 type(exchange_operator_t), intent(in ) :: exxop
641 ! ISDF interpolation points, and cam parameters.
642 ! An ISDF instance is not passed directly so this API is consistent with the other "compute_potential" routines.
643 type(namespace_t), intent(in ) :: namespace
644 class(space_t), intent(in ) :: space
645 ! with the existing routines
646 class(mesh_t), intent(in ) :: mesh
647 type(states_elec_t), intent(in ) :: st
648 type(kpoints_t), intent(in ) :: kpoints
649 ! with the existing routines
650
651 type(states_elec_t), intent(out) :: Vx_on_st
652
653 real(real64), allocatable :: psi_mu(:, :), P_r_mu(:, :), W_ace(:, :), isdf_vectors(:, :)
654 integer(int64), allocatable :: indices(:)
655
657
658 ! TODO(Alex) Issue #1195 Extend ISDF to spin-polarised and periodic systems
659 assert(kpoints%gamma_only())
660 assert(.not. space%is_periodic())
661 assert(st%d%nspin == 1)
662
663 indices = exxop%isdf%centroids%global_mesh_indices()
664
665 call isdf_serial_interpolation_vectors(exxop%isdf, namespace, mesh, st, &
666 indices, psi_mu, p_r_mu, isdf_vectors)
667
668 safe_allocate(w_ace(1:mesh%np, exxop%isdf%n_ks_states))
669 call isdf_serial_ace_w_unpacked(namespace, p_r_mu, isdf_vectors, psi_mu, exxop%psolver, exxop%cam, st, w_ace)
670 safe_deallocate_a(psi_mu)
671 safe_deallocate_a(p_r_mu)
672 safe_deallocate_a(isdf_vectors)
673
674 call isdf_serial_ace_batch_w(exxop%isdf, st, w_ace, vx_on_st)
675 safe_deallocate_a(w_ace)
676
678
680
689 subroutine isdf_serial_ace_w_unpacked(namespace, P_r_mu, isdf_vectors, psi_mu, poisson_solver, cam, st, W_ace)
690 type(namespace_t), intent(in ) :: namespace
691 real(real64), intent(in ), contiguous :: P_r_mu(:, :)
692 real(real64), intent(in ), contiguous :: isdf_vectors(:, :)
693 real(real64), intent(in ), contiguous :: psi_mu(:, :)
694 type(poisson_t), intent(in ) :: poisson_solver
695 type(xc_cam_t), intent(in) :: cam
696 type(states_elec_t), intent(in ) :: st
697
698 real(real64), intent(out), contiguous :: W_ace(:, :)
699
700 integer :: ip, i_mu, ist, np, n_int, nst
701 real(real64) :: psi_ist_mu
702 real(real64), allocatable :: V_r_nu(:, :)
703 logical :: use_external_kernel
704 real(real64) :: exx_weight
705 real(real64) :: weight
706
707 push_sub_with_profile(isdf_serial_ace_w_unpacked)
708
709 ! Number of states defines the number used in ISDF, which is typically ~ N occupied states
710 nst = size(psi_mu, 1)
711 np = size(p_r_mu, 1)
712 n_int = size(p_r_mu, 2)
713
714 assert(all(shape(p_r_mu) == shape(isdf_vectors)))
715 assert(size(psi_mu, 2) == n_int)
716 assert(size(w_ace, 1) == np)
717 ! Implies a size issue with either W_ace or psi_mu
718 assert(size(w_ace, 2) == nst)
719
720 use_external_kernel = (st%nik > st%d%spin_channels .or. cam%omega > m_epsilon)
721 if (use_external_kernel) then
722 message(1) = "External kernel not supported in ISDF"
723 call messages_fatal(1)
724 endif
725 exx_weight = cam%alpha
726 weight = exx_weight / st%smear%el_per_state
727
728 safe_allocate(v_r_nu(1:np, 1:n_int))
729 call profiling_in('isdf_potential')
730 do i_mu = 1, n_int
731 call dpoisson_solve(poisson_solver, namespace, v_r_nu(:, i_mu), isdf_vectors(:, i_mu), all_nodes=.false.)
732 enddo
733 call profiling_out("isdf_potential")
735 write(message(1),'(a)') "ISDF: Writing V from isdf_ace_w_unpacked"
736 call messages_info(1, namespace=namespace, debug_only=.true.)
737
738 ! Initialise elements of W_ace with data from the first interpolation point
739 i_mu = 1
740 do ist = 1, nst
741 psi_ist_mu = weight * psi_mu(ist, i_mu)
742 do ip = 1, np
743 w_ace(ip, ist) = - (p_r_mu(ip, i_mu) * v_r_nu(ip, i_mu) * psi_ist_mu)
744 enddo
745 enddo
746
747 ! Construct W_ace
748 do i_mu = 2, n_int
749 do ist = 1, nst
750 psi_ist_mu = weight * psi_mu(ist, i_mu)
751 do ip = 1, np
752 w_ace(ip, ist) = w_ace(ip, ist) - (p_r_mu(ip, i_mu) * v_r_nu(ip, i_mu) * psi_ist_mu)
753 enddo
754 enddo
755 enddo
756
757 safe_deallocate_a(v_r_nu)
758
759 pop_sub_with_profile(isdf_serial_ace_w_unpacked)
760
761 end subroutine isdf_serial_ace_w_unpacked
762
763
767 subroutine isdf_serial_ace_batch_w(isdf, st, W_ace, Vx_on_st)
768 type(isdf_options_t), intent(in ) :: isdf
769 type(states_elec_t), intent(in ) :: st
770 real(real64), intent(in ), contiguous :: w_ace(:, :)
771
772 type(states_elec_t), intent(out) :: vx_on_st
773
774 integer :: ist, ib, ist_b, np, max_state, minst, maxst, block_end, block_size
775
776 push_sub_with_profile(isdf_serial_ace_batch_w)
777
778 assert(size(w_ace, 2) == isdf%n_ks_states)
779 assert(st%d%dim == 1)
780
781 ! Copy memory layout, without data
782 call states_elec_copy(vx_on_st, st)
783 call states_elec_set_zero(vx_on_st)
784 np = size(w_ace, 1)
785
786 ! Ensure we do not go beyond the total number of occupied states
787 max_state = min(isdf%n_ks_states, st%st_end)
788 block_end = min(st%group%block_end, st%group%iblock(max_state))
789
790 do ib = st%group%block_start, block_end
791 minst = states_elec_block_min(st, ib)
792 maxst = min(states_elec_block_max(st, ib), max_state)
793 block_size = maxst - minst + 1
794 ! States in a block
795 do ist_b = 1, block_size
796 ! Global state index
797 ist = minst - 1 + ist_b
798 call batch_set_state(vx_on_st%group%psib(ib, 1), ist_b, np, w_ace(:, ist))
799 enddo
800 enddo
801
802 pop_sub_with_profile(isdf_serial_ace_batch_w)
803
804 end subroutine isdf_serial_ace_batch_w
805
806end module isdf_serial_oct_m
807
808!! Local Variables:
809!! mode: f90
810!! coding: utf-8
811!! End:
There are several ways how to call batch_set_state and batch_get_state:
Definition: batch_ops.F90:221
Matrix-matrix multiplication plus matrix.
Definition: lalg_basic.F90:279
double sqrt(double __x) __attribute__((__nothrow__
This module implements batches of mesh functions.
Definition: batch.F90:135
integer, parameter, public batch_packed
functions are stored in CPU memory, in transposed (packed) order
Definition: batch.F90:311
This module implements common operations on batches of mesh functions.
Definition: batch_ops.F90:118
This module contains interfaces for BLAS routines You should not use these routines directly....
Definition: blas.F90:120
type(debug_t), save, public debug
Definition: debug.F90:156
subroutine, public column_wise_khatri_rao_product(y, x, z)
Column-wise Kronecker product.
real(real64), parameter, public m_epsilon
Definition: global.F90:216
This module implements the underlying real-space grid.
Definition: grid.F90:119
subroutine, public dio_function_output(how, dir, fname, namespace, space, mesh, ff, unit, ierr, pos, atoms, grp, root)
Top-level IO routine for functions defined on the mesh.
Serial prototype for benchmarking and validating ISDF implementation.
subroutine generate_product_state_cubes(namespace, space, mesh, ions, file_prefix, data, limits)
Helper function to output a set of pair product states.
subroutine construct_density_matrix_with_occ_packed(st, phi, phi_mu, P_r_mu)
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_serial_ace_w_unpacked(namespace, P_r_mu, isdf_vectors, psi_mu, poisson_solver, cam, st, W_ace)
Compute the action of the exchange potential on KS states for adaptively-compressed exchange.
subroutine sample_phi_at_centroids(phi_r, indices, phi_mu)
Sample KS states at centroid points.
subroutine, public isdf_serial_interpolation_vectors(isdf, namespace, mesh, st, indices, phi_mu, P_r_mu, isdf_vectors)
Construct interpolative separable density fitting (ISDF) vectors and other intermediate quantities re...
subroutine error_in_product_basis(mesh, product_basis, approx_product_basis, error, mean_error)
Quantify the error in the product basis expansion.
subroutine, public isdf_serial_ace_compute_potentials(exxop, namespace, space, mesh, st, Vx_on_st, kpoints)
ISDF wrapper computing interpolation points and vectors, which are used to build the potential used ...
subroutine construct_zct(zct)
Construct the product of Z and C matrices from the element-wise product of the quasi-density matrix.
subroutine construct_cct(indices, zct, cct)
Construct the product from by masking the first dimension of .
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.
subroutine, public quantify_error_and_visualise(isdf, namespace, st, space, mesh, ions, indices, isdf_vectors, output_cubes)
Wrapper for quantifying the error in the expansion of the product basis.
subroutine isdf_serial_ace_batch_w(isdf, st, W_ace, Vx_on_st)
Put the bare array representation of W into a batch.
subroutine, public output_matrix(namespace, fname, matrix)
Helper routine to output a 2D matrix.
Definition: isdf_utils.F90:151
This module is intended to contain "only mathematical" functions and procedures.
Definition: math.F90:117
logical function, public is_symmetric(a, tol)
Check if a 2D array is symmetric.
Definition: math.F90:1460
This module defines functions over batches of mesh functions.
Definition: mesh_batch.F90:118
This module defines the meshes, which are used in Octopus.
Definition: mesh.F90:120
subroutine, public messages_not_implemented(feature, namespace)
Definition: messages.F90:1068
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
Definition: messages.F90:162
subroutine, public messages_fatal(no_lines, only_root_writes, namespace)
Definition: messages.F90:410
subroutine, public messages_info(no_lines, iunit, debug_only, stress, all_nodes, namespace)
Definition: messages.F90:594
subroutine, public dpoisson_solve(this, namespace, pot, rho, all_nodes, kernel, reset)
Calculates the Poisson equation. Given the density returns the corresponding potential.
Definition: poisson.F90:916
subroutine, public profiling_out(label)
Increment out counter and sum up difference between entry and exit time.
Definition: profiling.F90:668
subroutine, public profiling_in(label, exclude)
Increment in counter and save entry time.
Definition: profiling.F90:600
pure logical function, public states_are_real(st)
integer pure function, public states_elec_block_max(st, ib)
return index of last state in block ib
subroutine, public states_elec_copy(stout, stin, exclude_wfns, exclude_eigenval, special, dest_type, copy_data)
make a (selective) copy of a states_elec_t object
integer pure function, public states_elec_block_min(st, ib)
return index of first state in block ib
subroutine, public states_elec_set_zero(st)
Explicitly set all wave functions in the states to zero.
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_one
some special units required for particular quantities
Describes mesh distribution to nodes.
Definition: mesh.F90:188
The states_elec_t class contains all electronic wave functions.
Coulomb-attenuating method parameters, used in the partitioning of the Coulomb potential into a short...
Definition: xc_cam.F90:141
int true(void)