22 use,
intrinsic :: iso_fortran_env, only: real64, int64
66 integer,
parameter :: ik = 1
74 type(isdf_options_t),
intent(in ) :: isdf
75 type(namespace_t),
intent(in ) :: namespace
76 class(mesh_t),
intent(in ) :: mesh
77 type(states_elec_t),
intent(in ) :: st
78 integer(int64),
contiguous,
intent(in ) :: indices(:)
80 real(real64),
allocatable,
intent(out) :: phi_mu(:, :)
82 real(real64),
allocatable,
intent(out) :: P_r_mu(:, :)
84 real(real64),
allocatable,
intent(out) :: isdf_vectors(:, :)
86 real(real64),
allocatable :: phi(:, :), cct(:, :)
87 integer :: n_states, n_int, rank
88 logical :: data_is_packed
94 if (st%parallel_in_states .or. mesh%parallel_in_domains)
then
95 message(1) =
"Serial ISDF called when running state or domain-parallel"
100 if (st%d%nspin > 1)
then
110 data_is_packed = st%group%psib(st%group%block_start, 1)%status() ==
batch_packed
112 if (.not. data_is_packed)
then
113 message(1) =
"Serial ISDF only implemented for BATCH_PACKED"
117 n_states = isdf%n_ks_states
118 n_int =
size(indices)
123 safe_allocate(phi_mu(1:n_states, 1:n_int))
127 safe_allocate(p_r_mu(1:mesh%np, 1:n_int))
136 safe_allocate(cct(1:n_int, 1:n_int))
146 if (isdf%check_n_interp)
then
148 write(
message(1),
'(a, I4)')
"ISDF Serial: Rank of CC^T is ", rank
149 if (rank < n_int)
then
150 write(
message(2),
'(a)')
" - This rank is the optimal ISDFNpoints to run the calculation with"
152 write(
message(2),
'(a)')
" - This suggests that ISDFNpoints is either optimal, or could be larger"
167 write(
message(1),
'(a)')
"ISDF Serial: Inverting [CC^T]"
175 safe_allocate(isdf_vectors(1:mesh%np, 1:n_int))
177 call lalg_gemm(mesh%np, n_int, n_int, 1.0_real64, p_r_mu, cct, 0.0_real64, isdf_vectors)
179 safe_deallocate_a(cct)
183 safe_deallocate_a(phi)
195 class(
mesh_t),
intent(in ) :: mesh
197 integer,
intent(in ) :: max_state
198 real(real64),
allocatable,
intent(out) :: psi(:, :)
200 integer :: istate, ib, ist, minst, maxst, block_end
204 assert(max_state > 0 .and. max_state <= st%nst)
206 safe_allocate(psi(1:max_state, 1:mesh%np))
207 block_end = st%group%iblock(max_state)
215 do ist = minst, maxst
228 real(real64),
contiguous,
intent(in ) :: phi_r(:, :)
229 integer(int64),
contiguous,
intent(in ) :: indices(:)
230 real(real64),
contiguous,
intent(out) :: phi_mu(:, :)
232 integer :: ic, is, nst, n_int
233 integer(int64) :: ipg
237 write(
message(1),
'(a)')
"ISDF Serial: Sampling phi(r) at mu"
241 assert(
size(phi_mu, 1) == nst)
243 n_int =
size(indices)
244 assert(
size(phi_mu, 2) == n_int)
249 phi_mu(is, ic) = phi_r(is, ipg)
269 real(real64),
contiguous,
intent(inout) :: zct(:, :)
276 write(
message(1),
'(a)')
"ISDF Serial: Constructing ZC^T"
280 do j = 1,
size(zct, 2)
281 do i = 1,
size(zct, 1)
282 zct(i, j) = zct(i, j)**2
295 integer(int64),
contiguous,
intent(in ) :: indices(:)
296 real(real64),
contiguous,
intent(in ) :: zct(:, :)
298 real(real64),
contiguous,
intent(out) :: cct(:, :)
300 integer(int64) :: ipg
301 integer :: i_mu, i_nu, n_int
305 write(
message(1),
'(a)')
"ISDF Serial: Constructing CC^T by sampling ZC^T"
308 n_int =
size(indices)
309 assert(all(shape(cct) == [n_int, n_int]))
310 assert(
size(zct, 1) > n_int)
311 assert(
size(zct, 2) == n_int)
317 cct(i_mu, i_nu) = zct(ipg, i_nu)
335 real(real64),
contiguous,
intent(in ) :: phi(:, :)
337 real(real64),
contiguous,
intent(in ) :: phi_mu(:, :)
339 real(real64),
contiguous,
intent(out) :: P_r_mu(:, :)
347 write(
message(1),
'(a)')
"ISDF Serial: Constructing P_r_mu"
350 m_states =
size(phi, 1)
352 n_int =
size(phi_mu, 2)
354 assert(
size(phi_mu, 1) == m_states)
355 assert(
size(p_r_mu, 1) == np)
356 assert(
size(p_r_mu, 2) == n_int)
359 call lalg_gemm(phi, phi_mu, p_r_mu, transa=
'T')
368 real(real64),
intent(in ) :: phi(:, :)
370 real(real64),
intent(in ) :: phi_mu(:, :)
372 real(real64),
intent(out) :: P_r_mu(:, :)
374 integer :: np, n_int, m_states, imu, ist
375 real(real64),
allocatable :: focc_phi_mu(:, :)
377 push_sub_with_profile(construct_p_with_occ_packed)
379 write(
message(1),
'(a)')
"ISDF Serial: Constructing P_r_mu with occupations"
382 m_states =
size(phi_mu, 1)
384 n_int =
size(phi_mu, 2)
386 assert(
size(phi, 1) == m_states)
387 assert(
size(p_r_mu, 1) == np)
388 assert(
size(p_r_mu, 2) == n_int)
391 safe_allocate(focc_phi_mu(1:m_states, 1:n_int))
394 focc_phi_mu(ist, imu) = st%kweights(ik) * st%occ(ist, ik) * phi_mu(ist, imu)
399 call lalg_gemm(phi, focc_phi_mu, p_r_mu, transa=
'T')
400 safe_deallocate_a(focc_phi_mu)
402 pop_sub_with_profile(construct_p_with_occ_packed)
412 class(
space_t),
intent(in) :: space
413 class(
mesh_t),
intent(in) :: mesh
414 class(
ions_t),
pointer,
intent(in) :: ions
415 integer(int64),
contiguous,
intent(in) :: indices(:)
416 real(real64),
allocatable,
intent(inout) :: isdf_vectors(:, :)
417 logical,
intent(in) :: output_cubes
419 real(real64),
allocatable :: product_basis(:, :), approx_product_basis(:, :)
420 real(real64),
allocatable :: phi(:, :), phi_mu(:, :)
421 real(real64),
allocatable :: product_error(:)
422 integer :: n_occ, n_products, n_int, i, j, ij, unit
423 real(real64) :: mean_error
427 write(
message(1),
'(a)')
"ISDF Serial: Computing exact pair products"
431 n_occ = isdf%n_ks_states
433 assert(
size(phi, 2) == mesh%np)
435 n_products = n_occ * n_occ
436 safe_allocate(product_basis(1:n_products, 1:mesh%np))
439 if (output_cubes)
then
444 write(
message(1),
'(a)')
"ISDF Serial Test: Computing approximate pair products"
448 n_int =
size(indices)
449 safe_allocate(phi_mu(1:n_occ, 1:n_int))
451 safe_deallocate_a(phi)
453 safe_allocate(approx_product_basis(1:n_products, 1:mesh%np))
457 safe_deallocate_a(phi_mu)
458 safe_deallocate_a(isdf_vectors)
460 if (output_cubes)
then
462 approx_product_basis)
466 safe_allocate(product_error(1:n_products))
468 safe_deallocate_a(product_basis)
469 safe_deallocate_a(approx_product_basis)
472 open(newunit=unit, file=
"isdf_error_serial.txt")
473 write(unit, *)
'Mean error', mean_error
478 write(unit, *) i, j, product_error(ij)
484 safe_deallocate_a(product_error)
502 real(real64),
contiguous,
intent(in ) :: psi_mu(:, :)
503 real(real64),
contiguous,
intent(in ) :: zeta(:, :)
504 real(real64),
contiguous,
intent(out) :: product_basis(:, :)
506 real(real64),
allocatable :: psi_ij_mu(:, :)
507 integer :: mn_states, n_int, np
511 mn_states =
size(psi_mu, 1)**2
513 n_int =
size(zeta, 2)
515 assert(
size(product_basis, 1) == mn_states)
516 assert(
size(product_basis, 2) == np)
518 safe_allocate(psi_ij_mu(1:mn_states, 1:n_int))
522 call lalg_gemm(psi_ij_mu, zeta, product_basis, transb=
'T')
524 safe_deallocate_a(psi_ij_mu)
539 class(
mesh_t),
intent(in ) :: mesh
540 real(real64),
contiguous,
intent(in ) :: product_basis(:, :)
541 real(real64),
contiguous,
intent(in ) :: approx_product_basis(:, :)
543 real(real64),
contiguous,
intent(out) :: error(:)
544 real(real64),
intent(out) :: mean_error
546 integer :: mn_states, np, ij, ip
550 mn_states =
size(product_basis, 1)
551 np =
size(product_basis, 2)
554 assert(mesh%np == np)
557 assert(all(shape(product_basis) == shape(approx_product_basis)))
560 assert(
size(error) == mn_states)
564 error(ij) = (product_basis(ij, 1) - approx_product_basis(ij, 1))**2
569 error(ij) = error(ij) + (product_basis(ij, ip) - approx_product_basis(ij, ip))**2
573 mean_error = 0.0_real64
575 error(ij) =
sqrt(mesh%volume_element * error(ij))
576 mean_error = mean_error + error(ij)
579 mean_error = mean_error / real(mn_states, real64)
589 class(
space_t),
intent(in) :: space
590 class(
mesh_t),
intent(in) :: mesh
591 class(
ions_t),
pointer,
intent(in) :: ions
592 character(len=*),
intent(in) :: file_prefix
593 real(real64),
contiguous,
intent(in) :: data(:, :)
594 integer,
optional,
intent(in) :: limits(2)
596 integer :: m_states, limit_j, limit_i, i, j, ij, ierr
597 real(real64) :: size_data
598 character(len=4) :: i_char, j_char
599 character(len=120) :: file_name
602 size_data = real(
size(
data, 1), real64)
603 m_states = int(
sqrt(size_data))
605 if (
present(limits))
then
615 ij = j + (i - 1) * m_states
616 write(i_char,
'(I4)') i
617 write(j_char,
'(I4)') j
618 file_name = trim(adjustl(file_prefix)) // trim(adjustl(i_char)) //
'_' // trim(adjustl(j_char))
619 call dio_function_output(option__outputformat__cube,
"./cubes", trim(adjustl(file_name)), namespace, space, mesh, &
620 data(ij,:) ,
unit_one, ierr, pos=ions%pos, atoms=ions%atom)
645 class(
space_t),
intent(in ) :: space
647 class(
mesh_t),
intent(in ) :: mesh
654 real(real64),
allocatable :: psi_mu(:, :), P_r_mu(:, :), W_ace(:, :), isdf_vectors(:, :)
655 integer(int64),
allocatable :: indices(:)
660 assert(kpoints%gamma_only())
661 assert(.not. space%is_periodic())
662 assert(st%d%nspin == 1)
664 indices = exxop%isdf%centroids%global_mesh_indices()
667 indices, psi_mu, p_r_mu, isdf_vectors)
669 safe_allocate(w_ace(1:mesh%np, exxop%isdf%n_ks_states))
671 safe_deallocate_a(psi_mu)
672 safe_deallocate_a(p_r_mu)
673 safe_deallocate_a(isdf_vectors)
676 safe_deallocate_a(w_ace)
692 real(real64),
intent(in ),
contiguous :: P_r_mu(:, :)
693 real(real64),
intent(in ),
contiguous :: isdf_vectors(:, :)
694 real(real64),
intent(in ),
contiguous :: psi_mu(:, :)
695 type(
poisson_t),
intent(in ) :: poisson_solver
699 real(real64),
intent(out),
contiguous :: W_ace(:, :)
701 integer :: ip, i_mu, ist, np, n_int, nst
702 real(real64) :: psi_ist_mu
703 real(real64),
allocatable :: V_r_nu(:, :)
704 logical :: use_external_kernel
705 real(real64) :: exx_weight
706 real(real64) :: weight
711 nst =
size(psi_mu, 1)
713 n_int =
size(p_r_mu, 2)
715 assert(all(shape(p_r_mu) == shape(isdf_vectors)))
716 assert(
size(psi_mu, 2) == n_int)
717 assert(
size(w_ace, 1) == np)
719 assert(
size(w_ace, 2) == nst)
721 use_external_kernel = (st%nik > st%d%spin_channels .or. cam%omega >
m_epsilon)
722 if (use_external_kernel)
then
723 message(1) =
"External kernel not supported in ISDF"
726 exx_weight = cam%alpha
727 weight = exx_weight / st%smear%el_per_state
729 safe_allocate(v_r_nu(1:np, 1:n_int))
732 call dpoisson_solve(poisson_solver, namespace, v_r_nu(:, i_mu), isdf_vectors(:, i_mu), all_nodes=.false.)
736 write(
message(1),
'(a)')
"ISDF: Writing V from isdf_ace_w_unpacked"
742 psi_ist_mu = weight * psi_mu(ist, i_mu)
744 w_ace(ip, ist) = - (p_r_mu(ip, i_mu) * v_r_nu(ip, i_mu) * psi_ist_mu)
751 psi_ist_mu = weight * psi_mu(ist, i_mu)
753 w_ace(ip, ist) = w_ace(ip, ist) - (p_r_mu(ip, i_mu) * v_r_nu(ip, i_mu) * psi_ist_mu)
758 safe_deallocate_a(v_r_nu)
771 real(real64),
intent(in ),
contiguous :: w_ace(:, :)
775 integer :: ist, ib, ist_b, np, max_state, minst, maxst, block_end, block_size
779 assert(
size(w_ace, 2) == isdf%n_ks_states)
780 assert(st%d%dim == 1)
788 max_state = min(isdf%n_ks_states, st%st_end)
789 block_end = min(st%group%block_end, st%group%iblock(max_state))
791 do ib = st%group%block_start, block_end
794 block_size = maxst - minst + 1
796 do ist_b = 1, block_size
798 ist = minst - 1 + ist_b
799 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.
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
subroutine, public dpoisson_solve(this, namespace, pot, rho, all_nodes, kernel, reset)
Calculates the Poisson equation. Given the density returns the corresponding potential.
subroutine, public profiling_out(label)
Increment out counter and sum up difference between entry and exit time.
subroutine, public profiling_in(label, exclude)
Increment in counter and save entry time.
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_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...