22 use,
intrinsic :: iso_fortran_env, only: real64, int64
67 integer,
parameter :: ik = 1
75 type(isdf_options_t),
intent(in ) :: isdf
76 type(namespace_t),
intent(in ) :: namespace
77 class(mesh_t),
intent(in ) :: mesh
78 type(states_elec_t),
intent(in ) :: st
79 integer(int64),
contiguous,
intent(in ) :: indices(:)
81 real(real64),
allocatable,
intent(out) :: phi_mu(:, :)
83 real(real64),
allocatable,
intent(out) :: P_r_mu(:, :)
85 real(real64),
allocatable,
intent(out) :: isdf_vectors(:, :)
87 real(real64),
allocatable :: phi(:, :), cct(:, :)
88 integer :: n_states, n_int, rank
89 logical :: data_is_packed
95 if (st%parallel_in_states .or. mesh%parallel_in_domains)
then
96 message(1) =
"Serial ISDF called when running state or domain-parallel"
101 if (st%d%nspin > 1)
then
111 data_is_packed = st%group%psib(st%group%block_start, 1)%status() ==
batch_packed
113 if (.not. data_is_packed)
then
114 message(1) =
"Serial ISDF only implemented for BATCH_PACKED"
118 n_states = isdf%n_ks_states
119 n_int =
size(indices)
124 safe_allocate(phi_mu(1:n_states, 1:n_int))
128 safe_allocate(p_r_mu(1:mesh%np, 1:n_int))
137 safe_allocate(cct(1:n_int, 1:n_int))
147 if (isdf%check_n_interp)
then
149 write(
message(1),
'(a, I4)')
"ISDF Serial: Rank of CC^T is ", rank
150 if (rank < n_int)
then
151 write(
message(2),
'(a)')
" - This rank is the optimal ISDFNpoints to run the calculation with"
153 write(
message(2),
'(a)')
" - This suggests that ISDFNpoints is either optimal, or could be larger"
168 write(
message(1),
'(a)')
"ISDF Serial: Inverting [CC^T]"
176 safe_allocate(isdf_vectors(1:mesh%np, 1:n_int))
178 call lalg_gemm(mesh%np, n_int, n_int, 1.0_real64, p_r_mu, cct, 0.0_real64, isdf_vectors)
180 safe_deallocate_a(cct)
184 safe_deallocate_a(phi)
196 class(
mesh_t),
intent(in ) :: mesh
198 integer,
intent(in ) :: max_state
199 real(real64),
allocatable,
intent(out) :: psi(:, :)
201 integer :: istate, ib, ist, minst, maxst, block_end
205 assert(max_state > 0 .and. max_state <= st%nst)
207 safe_allocate(psi(1:max_state, 1:mesh%np))
208 block_end = st%group%iblock(max_state)
216 do ist = minst, maxst
229 real(real64),
contiguous,
intent(in ) :: phi_r(:, :)
230 integer(int64),
contiguous,
intent(in ) :: indices(:)
231 real(real64),
contiguous,
intent(out) :: phi_mu(:, :)
233 integer :: ic, is, nst, n_int
234 integer(int64) :: ipg
238 write(
message(1),
'(a)')
"ISDF Serial: Sampling phi(r) at mu"
242 assert(
size(phi_mu, 1) == nst)
244 n_int =
size(indices)
245 assert(
size(phi_mu, 2) == n_int)
250 phi_mu(is, ic) = phi_r(is, ipg)
270 real(real64),
contiguous,
intent(inout) :: zct(:, :)
277 write(
message(1),
'(a)')
"ISDF Serial: Constructing ZC^T"
281 do j = 1,
size(zct, 2)
282 do i = 1,
size(zct, 1)
283 zct(i, j) = zct(i, j)**2
296 integer(int64),
contiguous,
intent(in ) :: indices(:)
297 real(real64),
contiguous,
intent(in ) :: zct(:, :)
299 real(real64),
contiguous,
intent(out) :: cct(:, :)
301 integer(int64) :: ipg
302 integer :: i_mu, i_nu, n_int
306 write(
message(1),
'(a)')
"ISDF Serial: Constructing CC^T by sampling ZC^T"
309 n_int =
size(indices)
310 assert(all(shape(cct) == [n_int, n_int]))
311 assert(
size(zct, 1) > n_int)
312 assert(
size(zct, 2) == n_int)
318 cct(i_mu, i_nu) = zct(ipg, i_nu)
336 real(real64),
contiguous,
intent(in ) :: phi(:, :)
338 real(real64),
contiguous,
intent(in ) :: phi_mu(:, :)
340 real(real64),
contiguous,
intent(out) :: P_r_mu(:, :)
348 write(
message(1),
'(a)')
"ISDF Serial: Constructing P_r_mu"
351 m_states =
size(phi, 1)
353 n_int =
size(phi_mu, 2)
355 assert(
size(phi_mu, 1) == m_states)
356 assert(
size(p_r_mu, 1) == np)
357 assert(
size(p_r_mu, 2) == n_int)
360 call lalg_gemm(phi, phi_mu, p_r_mu, transa=
'T')
369 real(real64),
intent(in ) :: phi(:, :)
371 real(real64),
intent(in ) :: phi_mu(:, :)
373 real(real64),
intent(out) :: P_r_mu(:, :)
375 integer :: np, n_int, m_states, imu, ist
376 real(real64),
allocatable :: focc_phi_mu(:, :)
378 push_sub_with_profile(construct_p_with_occ_packed)
380 write(
message(1),
'(a)')
"ISDF Serial: Constructing P_r_mu with occupations"
383 m_states =
size(phi_mu, 1)
385 n_int =
size(phi_mu, 2)
387 assert(
size(phi, 1) == m_states)
388 assert(
size(p_r_mu, 1) == np)
389 assert(
size(p_r_mu, 2) == n_int)
392 safe_allocate(focc_phi_mu(1:m_states, 1:n_int))
395 focc_phi_mu(ist, imu) = st%kweights(ik) * st%occ(ist, ik) * phi_mu(ist, imu)
400 call lalg_gemm(phi, focc_phi_mu, p_r_mu, transa=
'T')
401 safe_deallocate_a(focc_phi_mu)
403 pop_sub_with_profile(construct_p_with_occ_packed)
413 class(
space_t),
intent(in) :: space
414 class(
mesh_t),
intent(in) :: mesh
415 class(
ions_t),
pointer,
intent(in) :: ions
416 integer(int64),
contiguous,
intent(in) :: indices(:)
417 real(real64),
allocatable,
intent(inout) :: isdf_vectors(:, :)
418 logical,
intent(in) :: output_cubes
420 real(real64),
allocatable :: product_basis(:, :), approx_product_basis(:, :)
421 real(real64),
allocatable :: phi(:, :), phi_mu(:, :)
422 real(real64),
allocatable :: product_error(:)
423 integer :: n_occ, n_products, n_int, i, j, ij, unit
424 real(real64) :: mean_error
428 write(
message(1),
'(a)')
"ISDF Serial: Computing exact pair products"
432 n_occ = isdf%n_ks_states
434 assert(
size(phi, 2) == mesh%np)
436 n_products = n_occ * n_occ
437 safe_allocate(product_basis(1:n_products, 1:mesh%np))
440 if (output_cubes)
then
445 write(
message(1),
'(a)')
"ISDF Serial Test: Computing approximate pair products"
449 n_int =
size(indices)
450 safe_allocate(phi_mu(1:n_occ, 1:n_int))
452 safe_deallocate_a(phi)
454 safe_allocate(approx_product_basis(1:n_products, 1:mesh%np))
458 safe_deallocate_a(phi_mu)
459 safe_deallocate_a(isdf_vectors)
461 if (output_cubes)
then
463 approx_product_basis)
467 safe_allocate(product_error(1:n_products))
469 safe_deallocate_a(product_basis)
470 safe_deallocate_a(approx_product_basis)
473 open(newunit=unit, file=
"isdf_error_serial.txt")
474 write(unit, *)
'Mean error', mean_error
479 write(unit, *) i, j, product_error(ij)
485 safe_deallocate_a(product_error)
503 real(real64),
contiguous,
intent(in ) :: psi_mu(:, :)
504 real(real64),
contiguous,
intent(in ) :: zeta(:, :)
505 real(real64),
contiguous,
intent(out) :: product_basis(:, :)
507 real(real64),
allocatable :: psi_ij_mu(:, :)
508 integer :: mn_states, n_int, np
512 mn_states =
size(psi_mu, 1)**2
514 n_int =
size(zeta, 2)
516 assert(
size(product_basis, 1) == mn_states)
517 assert(
size(product_basis, 2) == np)
519 safe_allocate(psi_ij_mu(1:mn_states, 1:n_int))
523 call lalg_gemm(psi_ij_mu, zeta, product_basis, transb=
'T')
525 safe_deallocate_a(psi_ij_mu)
540 class(
mesh_t),
intent(in ) :: mesh
541 real(real64),
contiguous,
intent(in ) :: product_basis(:, :)
542 real(real64),
contiguous,
intent(in ) :: approx_product_basis(:, :)
544 real(real64),
contiguous,
intent(out) :: error(:)
545 real(real64),
intent(out) :: mean_error
547 integer :: mn_states, np, ij, ip
551 mn_states =
size(product_basis, 1)
552 np =
size(product_basis, 2)
555 assert(mesh%np == np)
558 assert(all(shape(product_basis) == shape(approx_product_basis)))
561 assert(
size(error) == mn_states)
565 error(ij) = (product_basis(ij, 1) - approx_product_basis(ij, 1))**2
570 error(ij) = error(ij) + (product_basis(ij, ip) - approx_product_basis(ij, ip))**2
574 mean_error = 0.0_real64
576 error(ij) =
sqrt(mesh%volume_element * error(ij))
577 mean_error = mean_error + error(ij)
580 mean_error = mean_error / real(mn_states, real64)
590 class(
space_t),
intent(in) :: space
591 class(
mesh_t),
intent(in) :: mesh
592 class(
ions_t),
pointer,
intent(in) :: ions
593 character(len=*),
intent(in) :: file_prefix
594 real(real64),
contiguous,
intent(in) :: data(:, :)
595 integer,
optional,
intent(in) :: limits(2)
597 integer :: m_states, limit_j, limit_i, i, j, ij, ierr
598 real(real64) :: size_data
599 character(len=4) :: i_char, j_char
600 character(len=120) :: file_name
603 size_data = real(
size(
data, 1), real64)
604 m_states = int(
sqrt(size_data))
606 if (
present(limits))
then
616 ij = j + (i - 1) * m_states
617 write(i_char,
'(I4)') i
618 write(j_char,
'(I4)') j
619 file_name = trim(adjustl(file_prefix)) // trim(adjustl(i_char)) //
'_' // trim(adjustl(j_char))
620 call dio_function_output(option__outputformat__cube,
"./cubes", trim(adjustl(file_name)), namespace, space, mesh, &
621 data(ij,:) ,
unit_one, ierr, pos=ions%pos, atoms=ions%atom)
646 class(
space_t),
intent(in ) :: space
648 class(
mesh_t),
intent(in ) :: mesh
655 real(real64),
allocatable :: psi_mu(:, :), P_r_mu(:, :), W_ace(:, :), isdf_vectors(:, :)
656 integer(int64),
allocatable :: indices(:)
661 assert(kpoints%gamma_only())
662 assert(.not. space%is_periodic())
663 assert(st%d%nspin == 1)
665 indices = exxop%isdf%centroids%global_mesh_indices()
668 indices, psi_mu, p_r_mu, isdf_vectors)
670 safe_allocate(w_ace(1:mesh%np, exxop%isdf%n_ks_states))
672 safe_deallocate_a(psi_mu)
673 safe_deallocate_a(p_r_mu)
674 safe_deallocate_a(isdf_vectors)
677 safe_deallocate_a(w_ace)
693 real(real64),
intent(in ),
contiguous :: P_r_mu(:, :)
694 real(real64),
intent(in ),
contiguous :: isdf_vectors(:, :)
695 real(real64),
intent(in ),
contiguous :: psi_mu(:, :)
696 type(
poisson_t),
intent(in ) :: poisson_solver
700 real(real64),
intent(out),
contiguous :: W_ace(:, :)
702 integer :: ip, i_mu, ist, np, n_int, nst
703 real(real64) :: psi_ist_mu
704 real(real64),
allocatable :: V_r_nu(:, :)
705 logical :: use_external_kernel
706 real(real64) :: exx_weight
707 real(real64) :: weight
712 nst =
size(psi_mu, 1)
714 n_int =
size(p_r_mu, 2)
716 assert(all(shape(p_r_mu) == shape(isdf_vectors)))
717 assert(
size(psi_mu, 2) == n_int)
718 assert(
size(w_ace, 1) == np)
720 assert(
size(w_ace, 2) == nst)
722 use_external_kernel = (st%nik > st%d%spin_channels .or. cam%omega >
m_epsilon)
723 if (use_external_kernel)
then
724 message(1) =
"External kernel not supported in ISDF"
727 exx_weight = cam%alpha
728 weight = exx_weight / st%smear%el_per_state
730 safe_allocate(v_r_nu(1:np, 1:n_int))
731 call isdf_potential(namespace, poisson_solver, isdf_vectors, v_r_nu)
733 write(
message(1),
'(a)')
"ISDF: Writing V from isdf_ace_w_unpacked"
739 psi_ist_mu = weight * psi_mu(ist, i_mu)
741 w_ace(ip, ist) = - (p_r_mu(ip, i_mu) * v_r_nu(ip, i_mu) * psi_ist_mu)
748 psi_ist_mu = weight * psi_mu(ist, i_mu)
750 w_ace(ip, ist) = w_ace(ip, ist) - (p_r_mu(ip, i_mu) * v_r_nu(ip, i_mu) * psi_ist_mu)
755 safe_deallocate_a(v_r_nu)
768 real(real64),
intent(in ),
contiguous :: w_ace(:, :)
772 integer :: ist, ib, ist_b, np, max_state, minst, maxst, block_end, block_size
776 assert(
size(w_ace, 2) == isdf%n_ks_states)
777 assert(st%d%dim == 1)
785 max_state = min(isdf%n_ks_states, st%st_end)
786 block_end = min(st%group%block_end, st%group%iblock(max_state))
788 do ib = st%group%block_start, block_end
791 block_size = maxst - minst + 1
793 do ist_b = 1, block_size
795 ist = minst - 1 + ist_b
796 call batch_set_state(vx_on_st%group%psib(ib, 1), ist_b, np, w_ace(:, ist))
There are several ways how to call batch_set_state and batch_get_state:
Matrix-matrix multiplication plus matrix.
double sqrt(double __x) __attribute__((__nothrow__
This module implements batches of mesh functions.
integer, parameter, public batch_packed
functions are stored in CPU memory, in transposed (packed) order
This module implements common operations on batches of mesh functions.
This module contains interfaces for BLAS routines You should not use these routines directly....
type(debug_t), save, public debug
subroutine, public column_wise_khatri_rao_product(y, x, z)
Column-wise Kronecker product.
real(real64), parameter, public m_epsilon
This module implements the underlying real-space grid.
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.
subroutine, public isdf_potential(namespace, poisson_solver, isdf_vectors, V_r_nu)
Compute the effective potential in the ISDF vector basis.
This module is intended to contain "only mathematical" functions and procedures.
logical function, public is_symmetric(a, tol)
Check if a 2D array is symmetric.
This module defines functions over batches of mesh functions.
This module defines the meshes, which are used in Octopus.
subroutine, public messages_not_implemented(feature, namespace)
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
subroutine, public messages_fatal(no_lines, only_root_writes, namespace)
subroutine, public messages_info(no_lines, iunit, debug_only, stress, all_nodes, namespace)
type(mpi_grp_t), public mpi_world
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)
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_angstrom
For XYZ files.
type(unit_t), public unit_one
some special units required for particular quantities
Describes mesh distribution to nodes.
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...