Branch data Line data Source code
1 : : !!****m* ABINIT/m_slk
2 : : !! NAME
3 : : !! m_slk
4 : : !!
5 : : !! FUNCTION
6 : : !! High-level objects and wrappers around the ScaLAPACK, ELPA and SLATE API.
7 : : !!
8 : : !! Object model:
9 : : !!
10 : : !! slk_grid_t 2D BLACS grid of nprocs = dims(1) * dims(2) processes (row-major ordering).
11 : : !! slk_processor_t One process of the grid: rank, coordinates, MPI communicator and a copy of the grid.
12 : : !! basemat_t Private base class: global and local shapes, block sizes, ScaLAPACK descriptor
13 : : !! and a POINTER to the slk_processor_t passed to init.
14 : : !! slkmat_dp_t Double precision matrix: local buffer in buffer_cplx (istwf_k /= 2) or buffer_real (istwf_k == 2).
15 : : !! slkmat_sp_t Single precision version (subset of the methods).
16 : : !!
17 : : !! The global matrix is stored with the 2D block-cyclic distribution of ScaLAPACK, the first block
18 : : !! belongs to the process with coordinates (0, 0) (RSRC = CSRC = 0 for all the matrices built by init).
19 : : !! Each process stores only its local buffer of shape size_local. Use loc2glob, loc2grow, loc2gcol
20 : : !! to get the global indices of the local entries and glob2loc to find the owner of a global entry.
21 : : !!
22 : : !! Typical usage:
23 : : !!
24 : : !! type(slk_processor_t) :: slkproc
25 : : !! type(slkmat_dp_t) :: mat, vec
26 : : !!
27 : : !! call slkproc%init(comm) ! BLACS grid built from the MPI communicator.
28 : : !! call mat%init(nn, nn, slkproc, istwf_k1) ! Complex nn x nn matrix, default block sizes.
29 : : !! do jloc=1,mat%size_local(2) ! Fill the local buffer.
30 : : !! do iloc=1,mat%size_local(1)
31 : : !! call mat%loc2glob(iloc, jloc, iglob, jglob)
32 : : !! mat%buffer_cplx(iloc, jloc) = ... ! Value of the (iglob, jglob) entry.
33 : : !! end do
34 : : !! end do
35 : : !! call vec%init(nn, nn, slkproc, istwf_k1)
36 : : !! call mat%heev("V", "U", vec, eigvals) ! Collective on the processes of the grid.
37 : : !! call slk_matrix_to_global_dpc_2D(vec, "A", glob_vec) ! Only the local entries are written:
38 : : !! call xmpi_sum(glob_vec, comm, ierr) ! glob_vec must be zero on entry, then MPI sum.
39 : : !! call mat%free(); call vec%free()
40 : : !! call slkproc%free() ! Free the processor after the matrices.
41 : : !!
42 : : !! A one-dimensional column distribution (used in m_gwr) is obtained with grid_dims=[1, nproc] and
43 : : !! size_blocs=[-1, col_bsize], where col_bsize is given by block_dist_1d.
44 : : !!
45 : : !! Backends:
46 : : !! HAVE_LINALG_SCALAPACK required by almost everything in this module.
47 : : !! HAVE_LINALG_ELPA compute_eigen_problem and compute_generalized_eigen_problem use ELPA.
48 : : !! HAVE_LINALG_SLATE compute_eigen_problem uses SLATE if ELPA is not available.
49 : : !! HAVE_OPENMP_OFFLOAD gpu_map, gpu_set_zero and is_gpu_mapped act on the GPU copy of the local buffer.
50 : : !!
51 : : !! NOTES
52 : : !! - Without HAVE_LINALG_SCALAPACK, init does not allocate the local buffer and most methods do nothing.
53 : : !! There is no sequential fallback: callers must check that ScaLAPACK is available.
54 : : !! - matrix%processor points to the slk_processor_t passed to init. The processor must outlive all the
55 : : !! matrices built with it: do not pass a local variable that goes out of scope before the matrix is freed.
56 : : !! - After init, slk_grid_t%comm contains the BLACS context returned by BLACS_GRIDINIT, not an MPI communicator.
57 : : !! Use slk_processor_t%comm for MPI calls.
58 : : !! - istwf_k selects the buffer: istwf_k == 2 means real matrix (buffer_real), any other value complex matrix
59 : : !! (buffer_cplx). Some methods (heev, invert, hpd_invert, collect_cplx) support only complex matrices.
60 : : !! - Default block size: 24 with ScaLAPACK, 16 on GPU, 1 if ELPA is available. Square blocks are enforced
61 : : !! if ELPA or SLATE are available.
62 : : !! - Routines based on p?gemr2d (take_from, cut, change_size_blocs, collect_cplx) must be called by all the
63 : : !! processes owning the input or the output matrix.
64 : : !!
65 : : !! COPYRIGHT
66 : : !! Copyright (C) 2004-2026 ABINIT group (CS,GZ,FB,MG,MT)
67 : : !! This file is distributed under the terms of the
68 : : !! GNU General Public License, see ~abinit/COPYING
69 : : !! or http://www.gnu.org/copyleft/gpl.txt .
70 : : !!
71 : : !! TODO
72 : : !! Provide fallbacks for sequential version (No scalapack) so that we can implement generic high-level algorithms.
73 : : !!
74 : : !! SOURCE
75 : :
76 : : #if defined HAVE_CONFIG_H
77 : : #include "config.h"
78 : : #endif
79 : :
80 : : #include "abi_common.h"
81 : :
82 : : module m_slk
83 : :
84 : : use, intrinsic :: iso_c_binding
85 : : use defs_basis
86 : : USE_MPI
87 : : use m_xmpi
88 : : use m_xomp
89 : : use m_errors
90 : : use m_abicore
91 : : use m_gputk
92 : : #ifdef HAVE_LINALG_ELPA
93 : : use m_elpa
94 : : #endif
95 : :
96 : : use m_fstrings, only : firstchar, toupper, itoa, sjoin, ltoa, string_in
97 : : use m_time, only : cwtime, cwtime_report
98 : : !use m_numeric_tools, only : blocked_loop !, print_arr
99 : :
100 : : implicit none
101 : :
102 : : #ifdef HAVE_MPI1
103 : : include 'mpif.h'
104 : : #endif
105 : :
106 : : private
107 : :
108 : : ! scaLAPACK array descriptor.
109 : : integer,private,parameter :: DLEN_ = 9 ! length
110 : : integer,private,parameter :: Dtype_ = 1 ! type
111 : : integer,private,parameter :: CTXT_ = 2 ! BLACS context
112 : : integer,private,parameter :: M_ = 3 ! nb global lines
113 : : integer,private,parameter :: N_ = 4 ! nb global columns
114 : : integer,private,parameter :: MB_ = 5 ! nb lines of a block
115 : : integer,private,parameter :: NB_ = 6 ! nb columns of a block
116 : : integer,private,parameter :: RSRC_ = 7 ! line of processors at the beginning
117 : : integer,private,parameter :: CSRC_ = 8 ! column of processors at the beginning
118 : : integer,private,parameter :: LLD_ = 9 ! local number of lines
119 : : !!***
120 : :
121 : : !----------------------------------------------------------------------
122 : :
123 : : !!****t* m_slk/slk_grid_t
124 : : !! NAME
125 : : !! slk_grid_t
126 : : !!
127 : : !! FUNCTION
128 : : !! Grid of ScaLAPACK processors.
129 : : !!
130 : : !! SOURCE
131 : :
132 : : type,public :: slk_grid_t
133 : :
134 : : integer :: nprocs = -1
135 : : ! Total number of processors
136 : :
137 : : integer :: dims(2) = -1
138 : : ! Number of procs for rows/columns
139 : :
140 : : integer :: comm = xmpi_comm_null
141 : : ! MPI communicator on input of init. BLACS_GRIDINIT replaces it with the BLACS context of the grid
142 : : ! so this is NOT an MPI communicator after init: use slk_processor_t%comm for MPI calls.
143 : :
144 : : logical :: use_gpu = .false.
145 : : ! Whether GPU is used, relevant for determining matrix block size.
146 : :
147 : : contains
148 : : procedure :: init => slk_grid_init ! Set up the processor grid for ScaLAPACK.
149 : : end type slk_grid_t
150 : : !!***
151 : :
152 : : !----------------------------------------------------------------------
153 : :
154 : : !!****t* m_slk/slk_processor_t
155 : : !! NAME
156 : : !! slk_processor_t
157 : : !!
158 : : !! FUNCTION
159 : : !! One processor in the grid.
160 : : !! Matrices store a pointer to this object so it must be freed after all the matrices built with it.
161 : : !!
162 : : !! SOURCE
163 : :
164 : : type,public :: slk_processor_t
165 : :
166 : : integer :: my_rank = -1
167 : : ! rank of the processor in comm
168 : :
169 : : integer :: comm = xmpi_comm_null
170 : : ! MPI communicator underlying the BLACS grid.
171 : :
172 : : integer :: coords(2) = -1
173 : : ! Coordinates (row, column) of the processor in the grid, starting from 0.
174 : :
175 : : type(slk_grid_t) :: grid
176 : : ! the grid to which the processor is associated to.
177 : :
178 : : contains
179 : : procedure :: init => slk_processor_init ! Initializes an instance of processor ScaLAPACK from a MPI communicator.
180 : : procedure :: free => slk_processor_free ! Free the object
181 : : end type slk_processor_t
182 : : !!***
183 : :
184 : : !----------------------------------------------------------------------
185 : :
186 : : !!****t* m_slk/basemat_t
187 : : !! NAME
188 : : !! basemat_t
189 : : !!
190 : : !! FUNCTION
191 : : !! Base class for scalapack matrices.
192 : : !! Methods that are agnostic about type and precision or methods that can handle both
193 : : !! single and double precision, real and complex arrays via `select type` should be defined here.
194 : : !!
195 : : !! SOURCE
196 : :
197 : : type, private :: basemat_t
198 : :
199 : : integer :: size_local(2) = -1
200 : : ! dimensions of the local buffer.
201 : :
202 : : integer(c_size_t) :: bufsize = -1
203 : : ! Number of elements in the local buffer: size_local(1) * size_local(2).
204 : :
205 : : integer :: size_global(2) = -1
206 : : ! dimensions of the global matrix. [-1, -1] before the first init, [0, 0] after free.
207 : :
208 : : integer :: size_blocs(2) = -1
209 : : ! Row and column block sizes of the block-cyclic distribution (MB and NB in ScaLAPACK).
210 : :
211 : : integer :: istwf_k = -1
212 : : ! 2 for a real matrix stored in buffer_real, any other value for a complex matrix stored in buffer_cplx.
213 : :
214 : : type(slk_processor_t),pointer :: processor => null()
215 : : ! Processor passed to init. Not owned by the matrix: free does not deallocate it.
216 : :
217 : : integer :: desc(DLEN_)
218 : : ! Scalapack descriptor. desc(CTXT_) = -1 tells p?gemr2d that this process does not own the matrix.
219 : :
220 : : contains
221 : :
222 : : procedure :: init => basemat_init
223 : : ! Basic Constructor
224 : :
225 : : procedure :: glob2loc => basemat_glob2loc
226 : : ! Determine the local indices of an element from its global indices and return haveit bool flag.
227 : :
228 : : procedure :: loc2glob => basemat_loc2glob
229 : : ! Return global indices of a matrix element from the local indices.
230 : :
231 : : procedure :: loc2grow => basemat_loc2grow
232 : : ! Determine the global row index from the local index
233 : :
234 : : procedure :: loc2gcol => basemat_loc2col
235 : : ! Determine the global column index from the local index
236 : :
237 : : procedure :: idx_loc => basemat_idx_loc
238 : : ! Number of rows (columns) among the first i (j) global rows (columns) that are stored on this processor.
239 : :
240 : : procedure :: locmem_mb => basemat_locmem_mb
241 : : ! Return memory allocated for the local buffer in Mb.
242 : :
243 : : procedure :: print => basemat_print
244 : : ! Print info on the object.
245 : :
246 : : procedure :: check_local_shape => basemat_check_local_shape
247 : : ! Debugging tool to test the local shape `lshape` of the local buffer.
248 : :
249 : : procedure :: is_gpu_mapped => basemat_is_gpu_mapped
250 : : ! True if the local buffer is mapped to the GPU.
251 : :
252 : : procedure :: gpu_map => basemat_gpu_map
253 : : ! Use OpenMP to allocate/delete the local buffer on the GPU.
254 : :
255 : : procedure :: gpu_set_zero => basemat_gpu_set_zero
256 : : ! Fill the local buffer on the GPU with zeros.
257 : :
258 : : procedure :: free => basemat_free
259 : : ! Free memory
260 : :
261 : : procedure :: change_size_blocs => basemat_change_size_blocs
262 : : ! Change the block sizes, return new object.
263 : :
264 : : procedure :: get_trace => basemat_get_trace
265 : : ! Compute the trace of an N-by-N distributed matrix.
266 : :
267 : : procedure :: set_imag_diago_to_zero => basemat_set_imag_diago_to_zero
268 : : ! Set the imaginary part of the diagonal to zero.
269 : :
270 : : procedure :: invert => basemat_invert
271 : : ! Inverse of a complex matrix (LU factorization). Requires square blocks.
272 : :
273 : : end type basemat_t
274 : : !!***
275 : :
276 : : !----------------------------------------------------------------------
277 : :
278 : : !!****t* m_slk/slkmat_dp_t
279 : : !! NAME
280 : : !! slkmat_dp_t
281 : : !!
282 : : !! FUNCTION
283 : : !! high-level interface to ScaLAPACK matrix (double precision version)
284 : : !!
285 : : !! SOURCE
286 : :
287 : : type, public, extends(basemat_t) :: slkmat_dp_t
288 : :
289 : : real(dp),allocatable :: buffer_real(:,:)
290 : : ! local part of the (real) matrix.
291 : : ! The istwf_k option passed to the constructor defines whether we have a real or complex matrix
292 : :
293 : : complex(dpc),allocatable :: buffer_cplx(:,:)
294 : : ! local part of the (complex) matrix
295 : :
296 : : contains
297 : :
298 : : procedure :: get_head_and_wings => slkmat_dp_get_head_and_wings
299 : : ! Return global arrays with the head and the wings of the matrix.
300 : :
301 : : procedure :: set_head_and_wings => slkmat_dp_set_head_and_wings
302 : : ! Set head and the wings of the matrix starting from global arrays.
303 : :
304 : : procedure :: copy => slkmat_dp_copy
305 : : ! Copy object
306 : :
307 : : procedure :: hpd_invert => slkmat_dp_hpd_invert
308 : : ! Inverse of a Hermitian positive definite matrix.
309 : :
310 : : procedure :: ptrans => slkmat_dp_ptrans
311 : : ! Transpose ("N") or conjugate transpose ("C") matrix, return new matrix.
312 : :
313 : : procedure :: cut => slkmat_dp_cut
314 : : ! Extract submatrix and create new matrix with `size_blocs` and `processor`
315 : :
316 : : procedure :: take_from => slkmat_dp_take_from
317 : : ! Copy values from source, possibly distributed with a different grid or block sizes.
318 : :
319 : : procedure :: collect_cplx => slkmat_dp_collect_cplx
320 : : ! Return on all processors the submatrix of shape (mm, nn) starting at position ija.
321 : :
322 : : procedure :: heev => slkmat_dp_heev
323 : : ! Compute eigenvalues and, optionally, eigenvectors of an Hermitian matrix A. A * X = lambda * X
324 : : ! (PZHEEV, complex matrices only)
325 : :
326 : : procedure :: pzheevx => slkmat_dp_pzheevx
327 : : ! Compute selected eigenvalues and, optionally, eigenvectors of a complex Hermitian matrix A. A * X = lambda * X
328 : :
329 : : procedure :: pzhegvx => slkmat_dp_pzhegvx
330 : : ! Eigenvalues and, optionally, eigenvectors of a complex
331 : : ! generalized Hermitian-definite eigenproblem, of the form
332 : : ! sub( A )*x=(lambda)*sub( B )*x, sub( A )*sub( B )x=(lambda)*x,
333 : : ! or sub( B )*sub( A )*x=(lambda)*x.
334 : :
335 : : procedure :: symmetrize => slkmat_dp_symmetrize
336 : : ! Symmetrizes a square scaLAPACK matrix.
337 : :
338 : : procedure :: bsize_and_type => slkmat_dp_bsize_and_type
339 : : ! Returns the byte size and the MPI datatype
340 : :
341 : : procedure :: svd => slkmat_dp_svd
342 : : ! Singular Value Decomposition (double precision version).
343 : :
344 : : ! Conversion between the distributed matrix and a global matrix replicated on all the processes.
345 : : ! from_*: each process copies the entries it owns from the global matrix.
346 : : ! to_*: each process writes only the entries it owns, the other entries of the global matrix are not changed.
347 : : ! Zero the global matrix before the call and sum over the processes after.
348 : :
349 : : procedure :: to_global => slkmat_dp_to_global
350 : : ! Insert the matrix into a full real(dp) array: (2*n, m) with real/imag pairs if complex, (n, m) if real.
351 : :
352 : : procedure :: from_real_glob => slkmat_dp_from_real_glob
353 : : ! Fill a real matrix from a full real(dp) array (n, m).
354 : :
355 : : procedure :: from_global_pack => slkmat_dp_from_global_pack
356 : : ! Fill the upper triangle of a matrix from a packed array (upper triangle, columnwise).
357 : :
358 : : procedure :: from_global_sym => slkmat_dp_from_global_sym
359 : : ! Fill a full Hermitian (symmetric) matrix from a packed array (upper triangle, columnwise).
360 : :
361 : : procedure :: to_global_pack => slkmat_dp_to_global_pack
362 : : ! Insert the upper triangle of the matrix into a packed array (upper triangle, columnwise).
363 : :
364 : : procedure :: to_real_glob => slkmat_dp_to_real_glob
365 : : ! Insert a real matrix into a full real(dp) array (n, m).
366 : :
367 : : procedure :: to_complex_glob => slkmat_dp_to_complex_glob
368 : : ! Insert a complex matrix into a full complex(dpc) array (n, m).
369 : :
370 : : procedure :: from_complex_glob => slkmat_dp_from_complex_glob
371 : : ! Fill a complex matrix from a full real(dp) array (2*n, m) with real/imag pairs.
372 : :
373 : : end type slkmat_dp_t
374 : : !!***
375 : :
376 : : !!****t* m_slk/slkmat_sp_t
377 : : !! NAME
378 : : !! slkmat_sp_t
379 : : !!
380 : : !! FUNCTION
381 : : !! High-level interface to ScaLAPACK matrix (single precision version).
382 : : !!
383 : : !! SOURCE
384 : :
385 : : type, public, extends(basemat_t) :: slkmat_sp_t
386 : :
387 : : real(sp),allocatable :: buffer_real(:,:)
388 : : ! local part of the (real) matrix.
389 : : ! The istwf_k option passed to the constructor defines whether we have a real or complex matrix
390 : :
391 : : complex(sp),allocatable :: buffer_cplx(:,:)
392 : : ! local part of the (complex) matrix
393 : :
394 : : contains
395 : :
396 : : procedure :: copy => slkmat_sp_copy
397 : : ! Copy object
398 : :
399 : : procedure :: take_from => slkmat_sp_take_from
400 : : ! Take values from source
401 : :
402 : : procedure :: ptrans => slkmat_sp_ptrans
403 : : ! Transpose matrix
404 : :
405 : : procedure :: set_head_and_wings => slkmat_sp_set_head_and_wings
406 : : ! Set head and the wings of the matrix starting from global arrays.
407 : :
408 : : procedure :: cut => slkmat_sp_cut
409 : : ! Extract submatrix and create new matrix with `size_blocs` and `processor`
410 : :
411 : : procedure :: collect_cplx => slkmat_sp_collect_cplx
412 : : ! Return on all processors the submatrix of shape (mm, nn) starting at position ija.
413 : :
414 : : procedure :: heev => slkmat_sp_heev
415 : : ! Compute eigenvalues and, optionally, eigenvectors of an Hermitian matrix A. A * X = lambda * X
416 : :
417 : : procedure :: hpd_invert => slkmat_sp_hpd_invert
418 : : ! Inverse of a Hermitian positive definite matrix.
419 : :
420 : : procedure :: svd => slkmat_sp_svd
421 : : ! Singular Value Decomposition (single precision version).
422 : :
423 : : end type slkmat_sp_t
424 : : !!***
425 : :
426 : : ! Utilities.
427 : : public :: block_dist_1d ! Return block size for one-dimensional block column/row distribution
428 : : public :: slk_has_elpa ! Return True if ELPA support is activated
429 : :
430 : : ! Access to the local buffer with local indices (equivalent to mat%buffer_cplx(i,j) and mat%buffer_real(i,j)).
431 : : public :: matrix_get_local_cplx ! Return a local matrix coefficient of complex type.
432 : : public :: matrix_get_local_real ! Return a local matrix coefficient of double precision type.
433 : : public :: matrix_set_local_cplx ! Sets a local matrix coefficient of complex type.
434 : : public :: matrix_set_local_real ! Sets a local matrix coefficient of double precision type.
435 : :
436 : : ! Conversion between distributed and global complex matrices (see also the from_* and to_* methods).
437 : : public :: slk_matrix_from_global_dpc_2D ! Fill a complex SCALAPACK matrix with respect to a global matrix.
438 : : public :: slk_matrix_from_global_dpc_1Dp ! Fill a complex SCALAPACK matrix with respect to a global matrix.
439 : : ! target: double precision complex matrix in packed form.
440 : : public :: slk_matrix_to_global_dpc_2D ! Fill a global matrix with respect to a SCALAPACK matrix.
441 : : ! target: Two-dimensional Double precision complex matrix.
442 : :
443 : : ! PBLAS.
444 : : public :: slk_pgemm ! Compute: C := alpha*op(A)*op(B) + beta*C
445 : : interface slk_pgemm
446 : : module procedure slk_pgemm_dp
447 : : module procedure slk_pgemm_sp
448 : : end interface slk_pgemm
449 : :
450 : : ! Eigensolvers using ELPA, SLATE or ScaLAPACK depending on the build (see also the heev, pzheevx, pzhegvx methods).
451 : : public :: compute_eigen_problem ! Compute eigenvalues and eigenvectors of: A * X = lambda * X.
452 : : ! complex and real cases.
453 : : public :: compute_generalized_eigen_problem ! Compute generalized eigenvalue problem
454 : : public :: compute_eigen1 ! Same as compute_eigen_problem but for a global matrix replicated on all procs.
455 : : ! complex and real cases.
456 : : public :: compute_eigen2 ! Same as compute_generalized_eigen_problem but for global matrices
457 : : ! replicated on all procs. A * X = lambda * B * X, complex and real cases.
458 : :
459 : : ! MPI-IO.
460 : :
461 : : public :: slk_write ! Writes a square scaLAPACK distributed matrix on an external file using MPI-IO.
462 : : public :: slk_read ! Read a square scaLAPACK distributed matrix from an external file using MPI-IO.
463 : : public :: slk_single_fview_read_mask ! Returns an MPI datatype that can be used to read a scaLAPACK matrix from
464 : : ! a binary file using MPI-IO.
465 : : ! The view is created using the user-defined mask function
466 : : public :: slk_single_fview_read ! Returns an MPI datatype to read a scaLAPACK distributed matrix
467 : : ! from a binary file using MPI-IO.
468 : : public :: slk_single_fview_write ! Returns an MPI datatype to write a scaLAPACK distributed matrix
469 : : ! to a binary file using MPI-IO.
470 : :
471 : : ! Arrays of matrices (slkmat_dp_t or slkmat_sp_t).
472 : : public :: slk_array_free ! Deallocate array of matrices
473 : : interface slk_array_free
474 : : module procedure slk_array1_free
475 : : module procedure slk_array2_free
476 : : module procedure slk_array3_free
477 : : module procedure slk_array4_free
478 : : module procedure slk_array5_free
479 : : end interface slk_array_free
480 : :
481 : : public :: slk_array_set_zero ! Elemental routine to zero the value of the local buffer.
482 : : public :: slk_array_locmem_mb ! Elemental function returning the memory allocated for the local buffer in Mb
483 : :
484 : : public :: slk_array_gpu_set_zero ! Zero the value of the local buffer on the GPU
485 : : interface slk_array_gpu_set_zero
486 : : module procedure slk_array1_gpu_set_zero
487 : : module procedure slk_array2_gpu_set_zero
488 : : module procedure slk_array3_gpu_set_zero
489 : : module procedure slk_array4_gpu_set_zero
490 : : module procedure slk_array5_gpu_set_zero
491 : : end interface slk_array_gpu_set_zero
492 : :
493 : : ! External functions.
494 : : #if defined(HAVE_LINALG_SCALAPACK) || defined(HAVE_LINALG_ELPA)
495 : : integer,external :: indxl2g, numroc
496 : : real(dp),external :: PDLAMCH
497 : : real(dp),external :: PDLATRA
498 : : real(sp),external :: PSLATRA
499 : : complex(sp),external :: PCLATRA
500 : : complex(dp),external :: PZLATRA
501 : : #endif
502 : :
503 : : #ifdef HAVE_LINALG_SLATE
504 : :
505 : : interface
506 : : ! Standard complex Hermitian: A * Z = Z * diag(w)
507 : : subroutine slate_zheev_c(n, nb, nprow, npcol, comm_f, &
508 : : lda, a_data, ldz, z_data, w, nev, use_gpu, info) &
509 : : bind(C, name="slate_zheev_c")
510 : : use, intrinsic :: iso_c_binding
511 : : integer(c_int), value :: n, nb, nprow, npcol, comm_f, lda, ldz, nev, use_gpu
512 : : complex(c_double_complex), intent(inout) :: a_data(*)
513 : : complex(c_double_complex), intent(out) :: z_data(*)
514 : : real(c_double), intent(out) :: w(*)
515 : : integer(c_int), intent(out) :: info
516 : : end subroutine slate_zheev_c
517 : :
518 : : ! Standard real symmetric: A * Z = Z * diag(w)
519 : : subroutine slate_dsyev_c(n, nb, nprow, npcol, comm_f, &
520 : : lda, a_data, ldz, z_data, w, nev, use_gpu, info) &
521 : : bind(C, name="slate_dsyev_c")
522 : : use, intrinsic :: iso_c_binding
523 : : integer(c_int), value :: n, nb, nprow, npcol, comm_f, lda, ldz, nev, use_gpu
524 : : real(c_double), intent(inout) :: a_data(*)
525 : : real(c_double), intent(out) :: z_data(*)
526 : : real(c_double), intent(out) :: w(*)
527 : : integer(c_int), intent(out) :: info
528 : : end subroutine slate_dsyev_c
529 : :
530 : : ! Generalised complex Hermitian: A * Z = B * Z * diag(w)
531 : : subroutine slate_zhegv_c(n, nb, nprow, npcol, comm_f, &
532 : : lda, a_data, ldb, b_data, ldz, z_data, &
533 : : w, nev, use_gpu, info) &
534 : : bind(C, name="slate_zhegv_c")
535 : : use, intrinsic :: iso_c_binding
536 : : integer(c_int), value :: n, nb, nprow, npcol, comm_f
537 : : integer(c_int), value :: lda, ldb, ldz, nev, use_gpu
538 : : complex(c_double_complex), intent(inout) :: a_data(*), b_data(*)
539 : : complex(c_double_complex), intent(out) :: z_data(*)
540 : : real(c_double), intent(out) :: w(*)
541 : : integer(c_int), intent(out) :: info
542 : : end subroutine slate_zhegv_c
543 : :
544 : : ! Generalised real symmetric: A * Z = B * Z * diag(w)
545 : : subroutine slate_dsygv_c(n, nb, nprow, npcol, comm_f, &
546 : : lda, a_data, ldb, b_data, ldz, z_data, &
547 : : w, nev, use_gpu, info) &
548 : : bind(C, name="slate_dsygv_c")
549 : : use, intrinsic :: iso_c_binding
550 : : integer(c_int), value :: n, nb, nprow, npcol, comm_f
551 : : integer(c_int), value :: lda, ldb, ldz, nev, use_gpu
552 : : real(c_double), intent(inout) :: a_data(*), b_data(*)
553 : : real(c_double), intent(out) :: z_data(*)
554 : : real(c_double), intent(out) :: w(*)
555 : : integer(c_int), intent(out) :: info
556 : : end subroutine slate_dsygv_c
557 : :
558 : : end interface
559 : :
560 : : #endif
561 : :
562 : : contains !==============================================================================
563 : : !!***
564 : :
565 : : !!****f* m_slk/slk_grid_init
566 : : !! NAME
567 : : !! slk_grid_init
568 : : !!
569 : : !! FUNCTION
570 : : !! Set up the ScaLAPACK grid given the total number of processors.
571 : : !! The processes are ordered row-major in the grid (BLACS_GRIDINIT with order 'R').
572 : : !!
573 : : !! INPUTS
574 : : !! nprocs= total number of processors
575 : : !! comm= MPI communicator
576 : : !! use_gpu= True if the matrices will be used on the GPU (selects the default block size).
577 : : !! [grid_dims]=Number of procs for each dimension. Default: the most square grid with
578 : : !! dims(1) <= dims(2) and dims(1) * dims(2) = nprocs (1 x nprocs if nprocs is prime).
579 : : !!
580 : : !! OUTPUT
581 : : !! grid= the grid of processors used by Scalapack
582 : : !!
583 : : !! SOURCE
584 : :
585 [ # # ]: 0 : subroutine slk_grid_init(grid, nprocs, comm, use_gpu, grid_dims)
586 : :
587 : : !Arguments ------------------------------------
588 : : class(slk_grid_t),intent(out) :: grid
589 : : integer,intent(in) :: nprocs,comm
590 : : logical,intent(in) :: use_gpu
591 : : integer,optional,intent(in) :: grid_dims(2)
592 : :
593 : : !Local variables-------------------------------
594 : : integer :: i
595 : : ! *********************************************************************
596 : :
597 : 0 : grid%nprocs = nprocs
598 : :
599 [ # # ]: 0 : if (.not. present(grid_dims)) then
600 : : ! Search for a rectangular grid of processors
601 : 0 : i=INT(SQRT(float(nprocs)))
602 [ # # ]: 0 : do while (MOD(nprocs,i) /= 0)
603 : 0 : i = i-1
604 : : end do
605 : 0 : i=max(i,1)
606 : :
607 : 0 : grid%dims(1) = i
608 : 0 : grid%dims(2) = INT(nprocs/i)
609 : :
610 : : else
611 [ # # ]: 0 : grid%dims = grid_dims
612 : : end if
613 : :
614 [ # # # # ]: 0 : ABI_CHECK(product(grid%dims) == nprocs, sjoin("grid%dims:", ltoa(grid%dims), "does not agree with nprocs:", itoa(nprocs)))
615 : :
616 : 0 : grid%comm = comm
617 : 0 : grid%use_gpu = use_gpu
618 : :
619 : : #ifdef HAVE_LINALG_SCALAPACK
620 : : ! 'R': Use row-major natural ordering
621 : : call BLACS_GRIDINIT(grid%comm, 'R', grid%dims(1), grid%dims(2))
622 : : #endif
623 : :
624 : 0 : end subroutine slk_grid_init
625 : : !!***
626 : :
627 : : !----------------------------------------------------------------------
628 : :
629 : : !!****f* m_slk/build_processor
630 : : !! NAME
631 : : !! build_processor
632 : : !!
633 : : !! FUNCTION
634 : : !! Builds a ScaLAPACK processor descriptor.
635 : : !! Build of the data related to one processor in a grid
636 : : !! The coordinates are computed from my_rank assuming the row-major ordering used in slk_grid_init:
637 : : !! coords = [my_rank / dims(2), mod(my_rank, dims(2))]. They overwrite the values returned by BLACS_GRIDINFO.
638 : : !!
639 : : !! INPUTS
640 : : !! grid= array representing the grid of processors.
641 : : !! my_rank= selected processor
642 : : !! comm= MPI communicator
643 : : !!
644 : : !! OUTPUT
645 : : !! processor= descriptor of a processor
646 : : !!
647 : : !! SOURCE
648 : :
649 : 0 : subroutine build_processor(processor, grid, my_rank, comm)
650 : :
651 : : !Arguments ------------------------------------
652 : : class(slk_processor_t),intent(inout) :: processor
653 : : class(slk_grid_t),intent(in) :: grid
654 : : integer,intent(in) :: my_rank, comm
655 : : ! *********************************************************************
656 : :
657 : 0 : processor%grid = grid
658 : 0 : processor%my_rank = my_rank
659 : 0 : processor%comm = comm
660 : :
661 : : #ifdef HAVE_LINALG_SCALAPACK
662 : : call BLACS_GRIDINFO(grid%comm, processor%grid%dims(1), processor%grid%dims(2), &
663 : : processor%coords(1), processor%coords(2))
664 : : #endif
665 : :
666 : : ! These values are the same as those computed by BLACS_GRIDINFO
667 : : ! except in the case where the my_rank argument is not the local proc
668 : 0 : processor%coords(1) = INT((my_rank) / grid%dims(2))
669 : 0 : processor%coords(2) = MOD((my_rank), grid%dims(2))
670 : :
671 : : end subroutine build_processor
672 : : !!***
673 : :
674 : : !----------------------------------------------------------------------
675 : :
676 : : !!****f* m_slk/slk_processor_init
677 : : !! NAME
678 : : !! slk_processor_init
679 : : !!
680 : : !! FUNCTION
681 : : !! Initializes an instance of processor ScaLAPACK from an MPI communicator.
682 : : !! All the processes in comm belong to the grid. use_gpu is set to False.
683 : : !!
684 : : !! INPUTS
685 : : !! comm= MPI communicator
686 : : !! [grid_dims]=Number of procs for each dimension. Default: see slk_grid_init.
687 : : !! Use [1, nprocs] for a one-dimensional column distribution.
688 : : !!
689 : : !! OUTPUT
690 : : !! processor= descriptor of a processor
691 : : !!
692 : : !! SOURCE
693 : :
694 : 0 : subroutine slk_processor_init(processor, comm, &
695 : : grid_dims) ! optional
696 : :
697 : : !Arguments ------------------------------------
698 : : class(slk_processor_t),intent(out) :: processor
699 : : integer, intent(in) :: comm
700 : : integer,optional,intent(in) :: grid_dims(2)
701 : :
702 : : !Local variables-------------------------------
703 : : type(slk_grid_t) :: grid
704 : : integer :: nprocs, my_rank
705 : : ! *********************************************************************
706 : :
707 : 0 : nprocs = xmpi_comm_size(comm)
708 : 0 : my_rank = xmpi_comm_rank(comm)
709 : :
710 [ # # ]: 0 : if (present(grid_dims)) then
711 : 0 : call grid%init(nprocs, comm, .false., grid_dims=grid_dims)
712 : : else
713 : 0 : call grid%init(nprocs, comm, .false.)
714 : : end if
715 : :
716 : 0 : call build_processor(processor, grid, my_rank, comm)
717 : :
718 [ # # # # ]: 0 : end subroutine slk_processor_init
719 : : !!***
720 : :
721 : : !----------------------------------------------------------------------
722 : :
723 : : !!****f* m_slk/slk_processor_free
724 : : !! NAME
725 : : !! slk_processor_free
726 : : !!
727 : : !! FUNCTION
728 : : !! Removes a processor from the ScaLAPACK grid.
729 : : !!
730 : : !! SOURCE
731 : :
732 : 0 : subroutine slk_processor_free(processor)
733 : :
734 : : !Arguments ------------------------------------
735 : : class(slk_processor_t),intent(inout) :: processor
736 : : ! *********************************************************************
737 : :
738 : : #ifdef HAVE_LINALG_SCALAPACK
739 : : if (processor%grid%comm /= xmpi_comm_null) then
740 : : call BLACS_GRIDEXIT(processor%grid%comm)
741 : : !call BLACS_EXIT(0)
742 : : end if
743 : : #endif
744 : :
745 : 0 : end subroutine slk_processor_free
746 : : !!***
747 : :
748 : : !----------------------------------------------------------------------
749 : :
750 : : !!****f* m_slk/basemat_init
751 : : !! NAME
752 : : !! basemat_init
753 : : !!
754 : : !! FUNCTION
755 : : !! Initialisation of a SCALAPACK matrix (each proc initializes its own part of the matrix)
756 : : !! The object is freed first, then the local buffer is allocated and set to zero.
757 : : !! Without HAVE_LINALG_SCALAPACK, the routine only frees the object and the local buffer is not allocated.
758 : : !!
759 : : !! INPUTS
760 : : !! processor= descriptor of a processor. The matrix stores a pointer to it.
761 : : !! nbli_global= total number of lines
762 : : !! nbco_global= total number of columns
763 : : !! istwf_k= 2 if we have a real matrix (buffer_real) else complex (buffer_cplx).
764 : : !! [size_blocs]= custom block sizes. Use -1 to use global size along that direction.
765 : : !! Useful to distribute only rows or columns. Obviously, [-1, -1] is not allowed.
766 : : !! Default: 24 (16 if processor%grid%use_gpu, 1 if ELPA is available), square blocks if ELPA or SLATE is available.
767 : : !! The block sizes are always <= the global sizes.
768 : : !! [gpu_action]= Action passed to gpu_map after allocation. See basemat_gpu_map.
769 : : !!
770 : : !! OUTPUT
771 : : !! matrix= the matrix to process
772 : : !!
773 : : !! SOURCE
774 : :
775 : 0 : subroutine basemat_init(matrix, nbli_global, nbco_global, processor, istwf_k, &
776 : : size_blocs, gpu_action) ! optional
777 : :
778 : : !Arguments ------------------------------------
779 : : class(basemat_t),intent(inout) :: matrix
780 : : integer,intent(in) :: nbli_global, nbco_global, istwf_k
781 : : type(slk_processor_t),target,intent(in) :: processor
782 : : integer,optional,intent(in) :: size_blocs(2)
783 : : character(len=*),optional,intent(in) :: gpu_action
784 : :
785 : : #ifdef HAVE_LINALG_SCALAPACK
786 : : !Local variables-------------------------------
787 : : #ifdef HAVE_LINALG_ELPA
788 : : integer, parameter :: DEFAULT_SIZE_BLOCS = 1
789 : : #else
790 : : ! As recommended by Intel MKL, a more sensible default than the previous value of 40
791 : : integer, parameter :: DEFAULT_SIZE_BLOCS = 24
792 : : #endif
793 : : ! As recommended in ELPA, which advises distributions as squared as possible using powers of 2
794 : : integer, parameter :: DEFAULT_SIZE_BLOCS_GPU = 16
795 : : integer :: info,sizeb
796 : : !character(len=500) :: msg
797 : : ! *********************************************************************
798 : :
799 : : call matrix%free()
800 : :
801 : : sizeb = DEFAULT_SIZE_BLOCS
802 : : if (processor%grid%use_gpu) sizeb = DEFAULT_SIZE_BLOCS_GPU
803 : :
804 : : !Records of the matrix type
805 : : matrix%processor => processor
806 : : matrix%size_blocs(1) = MIN(sizeb, nbli_global)
807 : : matrix%size_blocs(2) = MIN(sizeb, nbco_global)
808 : :
809 : : #if defined(HAVE_LINALG_ELPA) || defined(HAVE_LINALG_SLATE)
810 : : if(matrix%size_blocs(1) .ne. matrix%size_blocs(2)) then
811 : : matrix%size_blocs(1) = MIN(matrix%size_blocs(1), matrix%size_blocs(2))
812 : : matrix%size_blocs(2) = matrix%size_blocs(1)
813 : : end if
814 : : #endif
815 : :
816 : : ! Use custom block sizes.
817 : : if (present(size_blocs)) then
818 : : ABI_CHECK(.not. all(size_blocs == -1), "size_blocs [-1, -1] is not allowed")
819 : : if (size_blocs(1) == -1) then
820 : : matrix%size_blocs(1) = nbli_global
821 : : else
822 : : matrix%size_blocs(1) = MIN(size_blocs(1), nbli_global)
823 : : end if
824 : : if (size_blocs(2) == -1) then
825 : : matrix%size_blocs(2) = nbco_global
826 : : else
827 : : matrix%size_blocs(2) = MIN(size_blocs(2), nbco_global)
828 : : end if
829 : : end if
830 : :
831 : : matrix%size_global(1) = nbli_global
832 : : matrix%size_global(2) = nbco_global
833 : : !write(std_out, *)"matrix%size_global", matrix%size_global
834 : :
835 : : ! Size of the local buffer
836 : : ! NUMROC computes the NUMber of Rows Or Columns of a distributed matrix owned by the process indicated by IPROC.
837 : : ! NUMROC (n, nb, iproc, isrcproc, nprocs)
838 : : matrix%size_local(1) = NUMROC(nbli_global, matrix%size_blocs(1), &
839 : : processor%coords(1), 0, processor%grid%dims(1))
840 : :
841 : : matrix%size_local(2) = NUMROC(nbco_global,matrix%size_blocs(2), &
842 : : processor%coords(2), 0, processor%grid%dims(2))
843 : :
844 : : matrix%bufsize = int(matrix%size_local(1), c_size_t) * int(matrix%size_local(2), c_size_t)
845 : :
846 : : call matrix%idx_loc(matrix%size_global(1), matrix%size_global(2), &
847 : : matrix%size_local(1), matrix%size_local(2))
848 : :
849 : : ! Initialisation of the SCALAPACK description of the matrix
850 : : ! (desc, m, n, mb, nb, irsrc, icsrc, comm, lld, info)
851 : : call DESCINIT(matrix%desc, nbli_global, nbco_global, &
852 : : matrix%size_blocs(1), matrix%size_blocs(2), 0, 0, &
853 : : processor%grid%comm, MAX(1, matrix%size_local(1)), info)
854 : :
855 : : if (info /= 0) then
856 : : ABI_ERROR(sjoin("Error while initializing scalapack matrix. info:", itoa(info)))
857 : : end if
858 : :
859 : : ! Allocate local buffer.
860 : : matrix%istwf_k = istwf_k
861 : : select type (matrix)
862 : : class is (slkmat_dp_t)
863 : : if (istwf_k /= 2) then
864 : : ABI_MALLOC(matrix%buffer_cplx, (matrix%size_local(1), matrix%size_local(2)))
865 : : matrix%buffer_cplx = czero
866 : : else
867 : : ABI_MALLOC(matrix%buffer_real, (matrix%size_local(1), matrix%size_local(2)))
868 : : matrix%buffer_real = zero
869 : : end if
870 : :
871 : : class is (slkmat_sp_t)
872 : : if (istwf_k /= 2) then
873 : : ABI_MALLOC(matrix%buffer_cplx, (matrix%size_local(1), matrix%size_local(2)))
874 : : matrix%buffer_cplx = czero_sp
875 : : else
876 : : ABI_MALLOC(matrix%buffer_real, (matrix%size_local(1), matrix%size_local(2)))
877 : : matrix%buffer_real = zero_sp
878 : : end if
879 : :
880 : : class default
881 : : ABI_ERROR("Wrong class")
882 : : end select
883 : :
884 : : if (present(gpu_action)) call matrix%gpu_map(gpu_action)
885 : : #endif
886 : :
887 : 0 : end subroutine basemat_init
888 : : !!***
889 : :
890 : : !----------------------------------------------------------------------
891 : :
892 : : !!****f* m_slk/basemat_locmem_mb
893 : : !! NAME
894 : : !! basemat_locmem_mb
895 : : !!
896 : : !! FUNCTION
897 : : !! Returns memory allocated for the local buffer in Mb.
898 : : !!
899 : : !! SOURCE
900 : :
901 : 0 : pure real(dp) function basemat_locmem_mb(mat) result(locmem_mb)
902 : :
903 : : !Arguments ------------------------------------
904 : : class(basemat_t),intent(in) :: mat
905 : : ! *********************************************************************
906 : :
907 : 0 : locmem_mb = zero
908 : : select type (mat)
909 : : class is (slkmat_dp_t)
910 [ # # # # ]: 0 : if (allocated(mat%buffer_real)) locmem_mb = product(int(shape(mat%buffer_real))) * dp
911 [ # # # # ]: 0 : if (allocated(mat%buffer_cplx)) locmem_mb = product(int(shape(mat%buffer_cplx))) * two * dp
912 : : class is (slkmat_sp_t)
913 [ # # # # ]: 0 : if (allocated(mat%buffer_real)) locmem_mb = product(int(shape(mat%buffer_real))) * sp
914 [ # # # # ]: 0 : if (allocated(mat%buffer_cplx)) locmem_mb = product(int(shape(mat%buffer_cplx))) * two * sp
915 : : end select
916 : 0 : locmem_mb = locmem_mb * b2Mb
917 : :
918 : 0 : end function basemat_locmem_mb
919 : : !!***
920 : :
921 : : !----------------------------------------------------------------------
922 : :
923 : : !!****f* m_slk/basemat_print
924 : : !! NAME
925 : : !! basemat_print
926 : : !!
927 : : !! FUNCTION
928 : : !! Print info on scalapack matrix.
929 : : !!
930 : : !! INPUTS
931 : : !! [unit]=Unit number (default: std_out)
932 : : !! [header]=title for info
933 : : !! [prtvol]=Verbosity level (default: 0)
934 : : !!
935 : : !! SOURCE
936 : :
937 : 0 : subroutine basemat_print(mat, header, unit, prtvol)
938 : :
939 : : !Arguments ------------------------------------
940 : : class(basemat_t),intent(in) :: mat
941 : : character(len=*),optional,intent(in) :: header
942 : : integer,optional,intent(in) :: prtvol, unit
943 : :
944 : : !Local variables-------------------------------
945 : : integer :: unt, my_prtvol, grid_dims(2)
946 : : character(len=50) :: matrix_dtype
947 : : character(len=5000) :: msg
948 : : ! *********************************************************************
949 : :
950 : 0 : unt = std_out; if (present(unit)) unt =unit
951 : 0 : my_prtvol = 0; if (present(prtvol)) my_prtvol = prtvol
952 : :
953 : 0 : msg = ' ==== Info on scalapack matrix ==== '
954 [ # # # # : 0 : if (present(header)) msg=' ==== '//trim(adjustl(header))//' ==== '
# # ]
955 : 0 : call wrtout(unt, msg)
956 : :
957 : 0 : matrix_dtype = "undefined"
958 : : select type (mat)
959 : : class is (slkmat_dp_t)
960 [ # # ]: 0 : if (allocated(mat%buffer_real)) matrix_dtype = "real dp"
961 [ # # ]: 0 : if (allocated(mat%buffer_cplx)) matrix_dtype = "complex dp"
962 : :
963 : : class is (slkmat_sp_t)
964 [ # # ]: 0 : if (allocated(mat%buffer_real)) matrix_dtype = "real sp"
965 [ # # ]: 0 : if (allocated(mat%buffer_cplx)) matrix_dtype = "complex sp"
966 : :
967 : : class default
968 : 0 : ABI_ERROR("Wrong class")
969 : : end select
970 : :
971 : 0 : grid_dims = [-1, -1]
972 [ # # # # ]: 0 : if (associated(mat%processor)) grid_dims = mat%processor%grid%dims
973 : :
974 : : write(msg,'(5(3a),a,f8.1,a)') &
975 : 0 : " matrix_dtype ..... ", trim(matrix_dtype), ch10, &
976 [ # # ]: 0 : " size_global ...... ", trim(ltoa(mat%size_global)), ch10, &
977 [ # # ]: 0 : " size_local ...... ", trim(ltoa(mat%size_local)), ch10, &
978 [ # # ]: 0 : " size_blocs ...... ", trim(ltoa(mat%size_blocs)), ch10, &
979 [ # # ]: 0 : " processor grid ... ", trim(ltoa(grid_dims)), ch10, &
980 : 0 : " memory (Mb) ...... ", mat%locmem_mb(), ch10
981 : 0 : call wrtout(unt, msg)
982 : :
983 : : !if (prtvol > 10) call mat%write(unit)
984 : :
985 [ # # ]: 0 : end subroutine basemat_print
986 : : !!***
987 : :
988 : : !----------------------------------------------------------------------
989 : :
990 : : !!****f* m_slk/slkmat_check_shape
991 : : !! NAME
992 : : !! slkmat_check_shape
993 : : !!
994 : : !! FUNCTION
995 : : !! Debugging tool to test the local shape `lshape` of the local buffer.
996 : : !! Return exit status in `ok` and error message in `msg`.
997 : : !!
998 : : !! SOURCE
999 : :
1000 : 0 : logical function basemat_check_local_shape(mat, lshape, msg) result (ok)
1001 : :
1002 : : !Arguments ------------------------------------
1003 : : class(basemat_t),intent(in) :: mat
1004 : : integer,intent(in) :: lshape(2)
1005 : : character(len=*),intent(out) :: msg
1006 : : ! *********************************************************************
1007 : :
1008 : 0 : msg = ""
1009 [ # # # # ]: 0 : ok = all(mat%size_local == lshape)
1010 [ # # ]: 0 : if (.not. ok) then
1011 [ # # # # ]: 0 : msg = sjoin("mat%size_local:", ltoa(mat%size_local), " not equal to input local lshape ", ltoa(lshape))
1012 : 0 : return
1013 : : end if
1014 : :
1015 : : select type (mat)
1016 : : class is (slkmat_dp_t)
1017 [ # # ]: 0 : if (allocated(mat%buffer_cplx)) then
1018 [ # # # # ]: 0 : ok = all(shape(mat%buffer_cplx) == lshape)
1019 [ # # ]: 0 : if (.not. ok) then
1020 [ # # # # : 0 : msg = sjoin("shape(buffer_cplx):", ltoa(shape(mat%buffer_cplx)), " != input local lshape ", ltoa(lshape)); return
# # ]
1021 : : end if
1022 [ # # ]: 0 : else if (allocated(mat%buffer_real)) then
1023 [ # # # # ]: 0 : ok = all(shape(mat%buffer_real) == lshape)
1024 [ # # ]: 0 : if (.not. ok) then
1025 [ # # # # : 0 : msg = sjoin("shape(buffer_real):", ltoa(shape(mat%buffer_real)), " != input local lshape ", ltoa(lshape)); return
# # ]
1026 : : end if
1027 : : end if
1028 : :
1029 : : class is (slkmat_sp_t)
1030 : : ! Same piece of code as above. May use include file!
1031 [ # # ]: 0 : if (allocated(mat%buffer_cplx)) then
1032 [ # # # # ]: 0 : ok = all(shape(mat%buffer_cplx) == lshape)
1033 [ # # ]: 0 : if (.not. ok) then
1034 [ # # # # : 0 : msg = sjoin("shape(buffer_cplx):", ltoa(shape(mat%buffer_cplx)), " != input local lshape ", ltoa(lshape)); return
# # ]
1035 : : end if
1036 [ # # ]: 0 : else if (allocated(mat%buffer_real)) then
1037 [ # # # # ]: 0 : ok = all(shape(mat%buffer_real) == lshape)
1038 [ # # ]: 0 : if (.not. ok) then
1039 [ # # # # : 0 : msg = sjoin("shape(buffer_real):", ltoa(shape(mat%buffer_real)), " != input local lshape ", ltoa(lshape)); return
# # ]
1040 : : end if
1041 : : end if
1042 : :
1043 : : class default
1044 : 0 : ABI_ERROR("Wrong class")
1045 : : end select
1046 : :
1047 [ # # ]: 0 : end function basemat_check_local_shape
1048 : : !!***
1049 : :
1050 : : !----------------------------------------------------------------------
1051 : :
1052 : : !!****f* m_slk/slkmat_dp_get_head_and_wings
1053 : : !! NAME
1054 : : !! slkmat_dp_get_head_and_wings
1055 : : !!
1056 : : !! FUNCTION
1057 : : !! Return global arrays with head and wings of the matrix.
1058 : : !! If call_mpi if False, global MPI sum is postponed.
1059 : : !! Useful to reduce the number of MPI calls if one has to operate on multiple matrices.
1060 : : !!
1061 : : !! SOURCE
1062 : :
1063 : 0 : subroutine slkmat_dp_get_head_and_wings(mat, head, low_wing, up_wing, call_mpi)
1064 : :
1065 : : !Arguments ------------------------------------
1066 : : class(slkmat_dp_t),intent(in) :: mat
1067 : : complex(dp),intent(out) :: head, low_wing(mat%size_global(1)), up_wing(mat%size_global(2))
1068 : : logical,intent(in) :: call_mpi
1069 : :
1070 : : !Local variables-------------------------------
1071 : : integer :: ierr, il_g1, il_g2, iglob1, iglob2
1072 : : logical :: is_cplx
1073 : : ! *********************************************************************
1074 : :
1075 [ # # # # ]: 0 : head = zero; low_wing = zero; up_wing = zero
1076 : :
1077 : 0 : is_cplx = allocated(mat%buffer_cplx)
1078 : :
1079 [ # # ]: 0 : do il_g2=1,mat%size_local(2)
1080 : 0 : iglob2 = mat%loc2gcol(il_g2)
1081 [ # # ]: 0 : do il_g1=1,mat%size_local(1)
1082 : 0 : iglob1 = mat%loc2grow(il_g1)
1083 : :
1084 [ # # ]: 0 : if (iglob1 == 1 .or. iglob2 == 1) then
1085 [ # # ]: 0 : if (iglob1 == 1 .and. iglob2 == 1) then
1086 [ # # ]: 0 : if (is_cplx) then
1087 : 0 : head = mat%buffer_cplx(il_g1, il_g2)
1088 : : else
1089 : 0 : head = mat%buffer_real(il_g1, il_g2)
1090 : : end if
1091 [ # # ]: 0 : else if (iglob1 == 1) then
1092 [ # # ]: 0 : if (is_cplx) then
1093 : 0 : up_wing(iglob2) = mat%buffer_cplx(il_g1, il_g2)
1094 : : else
1095 : 0 : up_wing(iglob2) = mat%buffer_real(il_g1, il_g2)
1096 : : end if
1097 [ # # ]: 0 : else if (iglob2 == 1) then
1098 [ # # ]: 0 : if (is_cplx) then
1099 : 0 : low_wing(iglob1) = mat%buffer_cplx(il_g1, il_g2)
1100 : : else
1101 : 0 : low_wing(iglob1) = mat%buffer_real(il_g1, il_g2)
1102 : : end if
1103 : : end if
1104 : : end if
1105 : :
1106 : : end do
1107 : : end do
1108 : :
1109 [ # # ]: 0 : if (call_mpi) then
1110 : 0 : call xmpi_sum(head, mat%processor%comm, ierr)
1111 : 0 : call xmpi_sum(low_wing, mat%processor%comm, ierr)
1112 : 0 : call xmpi_sum(up_wing, mat%processor%comm, ierr)
1113 : : end if
1114 : :
1115 : 0 : end subroutine slkmat_dp_get_head_and_wings
1116 : : !!***
1117 : :
1118 : : !----------------------------------------------------------------------
1119 : :
1120 : : !!****f* m_slk/slkmat_dp_set_head_and_wings
1121 : : !! NAME
1122 : : !! slkmat_dp_set_head_and_wings
1123 : : !!
1124 : : !! FUNCTION
1125 : : !! Set head and wings of the matrix starting from global arrays.
1126 : : !!
1127 : : !! SOURCE
1128 : :
1129 : 0 : subroutine slkmat_dp_set_head_and_wings(mat, head, low_wing, up_wing)
1130 : :
1131 : : !Arguments ------------------------------------
1132 : : class(slkmat_dp_t),intent(inout) :: mat
1133 : : complex(dp),intent(in) :: head, low_wing(mat%size_global(1)), up_wing(mat%size_global(2))
1134 : :
1135 : : !Local variables-------------------------------
1136 : : integer :: il_g1, il_g2, iglob1, iglob2
1137 : : logical :: is_cplx
1138 : : ! *********************************************************************
1139 : :
1140 : 0 : is_cplx = allocated(mat%buffer_cplx)
1141 : :
1142 [ # # ]: 0 : do il_g2=1,mat%size_local(2)
1143 : 0 : iglob2 = mat%loc2gcol(il_g2)
1144 [ # # ]: 0 : do il_g1=1,mat%size_local(1)
1145 : 0 : iglob1 = mat%loc2grow(il_g1)
1146 : :
1147 [ # # ]: 0 : if (iglob1 == 1 .or. iglob2 == 1) then
1148 [ # # ]: 0 : if (iglob1 == 1 .and. iglob2 == 1) then
1149 [ # # ]: 0 : if (is_cplx) then
1150 : 0 : mat%buffer_cplx(il_g1, il_g2) = head
1151 : : else
1152 : 0 : mat%buffer_real(il_g1, il_g2) = real(head)
1153 : : end if
1154 [ # # ]: 0 : else if (iglob1 == 1) then
1155 [ # # ]: 0 : if (is_cplx) then
1156 : 0 : mat%buffer_cplx(il_g1, il_g2) = up_wing(iglob2)
1157 : : else
1158 : 0 : mat%buffer_real(il_g1, il_g2) = real(up_wing(iglob2))
1159 : : end if
1160 [ # # ]: 0 : else if (iglob2 == 1) then
1161 [ # # ]: 0 : if (is_cplx) then
1162 : 0 : mat%buffer_cplx(il_g1, il_g2) = low_wing(iglob1)
1163 : : else
1164 : 0 : mat%buffer_real(il_g1, il_g2) = real(low_wing(iglob1))
1165 : : end if
1166 : : end if
1167 : : end if
1168 : :
1169 : : end do
1170 : : end do
1171 : :
1172 : 0 : end subroutine slkmat_dp_set_head_and_wings
1173 : : !!***
1174 : :
1175 : : !----------------------------------------------------------------------
1176 : :
1177 : : !!****f* m_slk/slkmat_sp_set_head_and_wings
1178 : : !! NAME
1179 : : !! slkmat_sp_set_head_and_wings
1180 : : !!
1181 : : !! FUNCTION
1182 : : !! Set head and the wings of the matrix starting from global arrays.
1183 : : !!
1184 : : !! SOURCE
1185 : :
1186 : 0 : subroutine slkmat_sp_set_head_and_wings(mat, head, low_wing, up_wing)
1187 : :
1188 : : !Arguments ------------------------------------
1189 : : class(slkmat_sp_t),intent(inout) :: mat
1190 : : complex(sp),intent(in) :: head, low_wing(mat%size_global(1)), up_wing(mat%size_global(2))
1191 : :
1192 : : !Local variables-------------------------------
1193 : : integer :: il_g1, il_g2, iglob1, iglob2
1194 : : logical :: is_cplx
1195 : : ! *********************************************************************
1196 : :
1197 : 0 : is_cplx = allocated(mat%buffer_cplx)
1198 : :
1199 [ # # ]: 0 : do il_g2=1,mat%size_local(2)
1200 : 0 : iglob2 = mat%loc2gcol(il_g2)
1201 [ # # ]: 0 : do il_g1=1,mat%size_local(1)
1202 : 0 : iglob1 = mat%loc2grow(il_g1)
1203 : :
1204 [ # # ]: 0 : if (iglob1 == 1 .or. iglob2 == 1) then
1205 [ # # ]: 0 : if (iglob1 == 1 .and. iglob2 == 1) then
1206 [ # # ]: 0 : if (is_cplx) then
1207 : 0 : mat%buffer_cplx(il_g1, il_g2) = head
1208 : : else
1209 : 0 : mat%buffer_real(il_g1, il_g2) = real(head)
1210 : : end if
1211 [ # # ]: 0 : else if (iglob1 == 1) then
1212 [ # # ]: 0 : if (is_cplx) then
1213 : 0 : mat%buffer_cplx(il_g1, il_g2) = up_wing(iglob2)
1214 : : else
1215 : 0 : mat%buffer_real(il_g1, il_g2) = real(up_wing(iglob2))
1216 : : end if
1217 [ # # ]: 0 : else if (iglob2 == 1) then
1218 [ # # ]: 0 : if (is_cplx) then
1219 : 0 : mat%buffer_cplx(il_g1, il_g2) = low_wing(iglob1)
1220 : : else
1221 : 0 : mat%buffer_real(il_g1, il_g2) = real(low_wing(iglob1))
1222 : : end if
1223 : : end if
1224 : : end if
1225 : :
1226 : : end do
1227 : : end do
1228 : :
1229 : 0 : end subroutine slkmat_sp_set_head_and_wings
1230 : : !!***
1231 : :
1232 : : !----------------------------------------------------------------------
1233 : :
1234 : : !!****f* m_slk/slkmat_dp_copy
1235 : : !! NAME
1236 : : !! slkmat_dp_copy
1237 : : !!
1238 : : !! FUNCTION
1239 : : !! Copy in_mat to out_mat. If empty is True, the values in the local buffer are not copied. Default: False
1240 : : !!
1241 : : !! SOURCE
1242 : :
1243 [ # # ]: 0 : subroutine slkmat_dp_copy(in_mat, out_mat, empty)
1244 : :
1245 : : !Arguments ------------------------------------
1246 : : class(slkmat_dp_t),intent(in) :: in_mat
1247 : : class(slkmat_dp_t),intent(out) :: out_mat
1248 : : logical,optional,intent(in) :: empty
1249 : :
1250 : : !Local variables-------------------------------
1251 : : logical :: empty__
1252 : : type(c_ptr) :: gpu_ptr
1253 : : ! *********************************************************************
1254 : :
1255 : : call out_mat%init(in_mat%size_global(1), in_mat%size_global(2), in_mat%processor, in_mat%istwf_k, &
1256 : 0 : size_blocs=in_mat%size_blocs)
1257 : :
1258 [ # # ]: 0 : empty__ = .False.; if (present(empty)) empty__ = empty
1259 [ # # ]: 0 : if (.not. empty__) then
1260 [ # # ]: 0 : if (in_mat%istwf_k == 1) then
1261 [ # # # # : 0 : out_mat%buffer_cplx = in_mat%buffer_cplx
# # # # #
# # # # #
# # # # #
# ]
1262 : : else
1263 [ # # # # : 0 : out_mat%buffer_real = in_mat%buffer_real
# # # # #
# # # # #
# # # # #
# ]
1264 : : end if
1265 : : end if
1266 : :
1267 [ # # ]: 0 : if (in_mat%is_gpu_mapped(gpu_ptr)) then
1268 : 0 : call out_mat%gpu_map("alloc")
1269 [ # # ]: 0 : if (in_mat%istwf_k == 1) then
1270 : 0 : call gpu_copy_complex(out_mat%buffer_cplx, in_mat%buffer_cplx, in_mat%bufsize)
1271 : : else
1272 : 0 : call gpu_copy(out_mat%buffer_real, in_mat%buffer_real, in_mat%bufsize)
1273 : : end if
1274 : : end if
1275 : :
1276 : 0 : end subroutine slkmat_dp_copy
1277 : : !!***
1278 : :
1279 : : !----------------------------------------------------------------------
1280 : :
1281 : : !!****f* m_slk/slkmat_sp_copy
1282 : : !! NAME
1283 : : !! slkmat_sp_copy
1284 : : !!
1285 : : !! FUNCTION
1286 : : !! Copy in_mat to out_mat. If empty is True, the values in the local buffer are not copied. Default: False
1287 : : !!
1288 : : !! SOURCE
1289 : :
1290 [ # # ]: 0 : subroutine slkmat_sp_copy(in_mat, out_mat, empty)
1291 : :
1292 : : !Arguments ------------------------------------
1293 : : class(slkmat_sp_t),intent(in) :: in_mat
1294 : : class(slkmat_sp_t),intent(out) :: out_mat
1295 : : logical,optional,intent(in) :: empty
1296 : :
1297 : : !Local variables-------------------------------
1298 : : logical :: empty__
1299 : : type(c_ptr) :: gpu_ptr
1300 : : ! *********************************************************************
1301 : :
1302 : : call out_mat%init(in_mat%size_global(1), in_mat%size_global(2), in_mat%processor, in_mat%istwf_k, &
1303 : 0 : size_blocs=in_mat%size_blocs)
1304 : :
1305 [ # # ]: 0 : empty__ = .False.; if (present(empty)) empty__ = empty
1306 [ # # ]: 0 : if (.not. empty__) then
1307 [ # # ]: 0 : if (in_mat%istwf_k == 1) then
1308 [ # # # # : 0 : out_mat%buffer_cplx = in_mat%buffer_cplx
# # # # #
# # # # #
# # # # #
# ]
1309 : : else
1310 [ # # # # : 0 : out_mat%buffer_real = in_mat%buffer_real
# # # # #
# # # # #
# # # # #
# ]
1311 : : end if
1312 : : end if
1313 : :
1314 [ # # ]: 0 : if (in_mat%is_gpu_mapped(gpu_ptr)) then
1315 : 0 : call out_mat%gpu_map("alloc")
1316 [ # # ]: 0 : if (in_mat%istwf_k == 1) then
1317 : 0 : call gpu_copy_complex_sp(out_mat%buffer_cplx, in_mat%buffer_cplx, in_mat%bufsize)
1318 : : else
1319 : 0 : call gpu_copy_sp(out_mat%buffer_real, in_mat%buffer_real, in_mat%bufsize)
1320 : : end if
1321 : : end if
1322 : :
1323 : 0 : end subroutine slkmat_sp_copy
1324 : : !!***
1325 : :
1326 : : !----------------------------------------------------------------------
1327 : :
1328 : : !!****f* m_slk/basemat_free
1329 : : !! NAME
1330 : : !! basemat_free
1331 : : !!
1332 : : !! FUNCTION
1333 : : !! Free dynamic memory
1334 : : !!
1335 : : !! SOURCE
1336 : :
1337 : 0 : subroutine basemat_free(mat)
1338 : :
1339 : : !Arguments ------------------------------------
1340 : : class(basemat_t),intent(inout) :: mat
1341 : :
1342 : : !Local variables-------------------------------
1343 : : type(c_ptr) :: gpu_ptr
1344 : : ! *********************************************************************
1345 : :
1346 : : ! Don't free the grid. Just nullify the pointer as there might be other objects keeping a ref to processor.
1347 : 0 : mat%processor => null()
1348 : :
1349 [ # # ]: 0 : mat%size_global = 0
1350 [ # # ]: 0 : mat%size_blocs = 0
1351 [ # # ]: 0 : mat%size_local = 0
1352 [ # # ]: 0 : mat%desc = 0
1353 : :
1354 [ # # ]: 0 : if (mat%is_gpu_mapped(gpu_ptr)) then
1355 : 0 : call wrtout(std_out, "Deallocating mat%buffer on the GPU")
1356 : 0 : call mat%gpu_map("delete")
1357 : : end if
1358 : :
1359 : : select type (mat)
1360 : : class is (slkmat_dp_t)
1361 [ # # ]: 0 : ABI_SFREE(mat%buffer_cplx)
1362 [ # # ]: 0 : ABI_SFREE(mat%buffer_real)
1363 : :
1364 : : class is (slkmat_sp_t)
1365 [ # # ]: 0 : ABI_SFREE(mat%buffer_cplx)
1366 [ # # ]: 0 : ABI_SFREE(mat%buffer_real)
1367 : :
1368 : : class default
1369 : 0 : ABI_ERROR("Wrong class")
1370 : : end select
1371 : :
1372 : 0 : end subroutine basemat_free
1373 : : !!***
1374 : :
1375 : : !----------------------------------------------------------------------
1376 : :
1377 : : !!****f* m_slk/slk_array1_free
1378 : : !! NAME
1379 : : !! slk_array1_free
1380 : : !!
1381 : : !! FUNCTION
1382 : : !! Deallocate 1d array of slkmat_dp_t elements
1383 : : !!
1384 : : !! SOURCE
1385 : :
1386 : 0 : subroutine slk_array1_free(slk_arr1)
1387 : : class(basemat_t),intent(inout) :: slk_arr1(:)
1388 : : integer :: i1
1389 [ # # ]: 0 : do i1=1,size(slk_arr1, dim=1)
1390 : 0 : call slk_arr1(i1)%free()
1391 : : end do
1392 : 0 : end subroutine slk_array1_free
1393 : : !!***
1394 : :
1395 : : !----------------------------------------------------------------------
1396 : :
1397 : : !!****f* m_slk/slk_array2_free
1398 : : !! NAME
1399 : : !! slk_array2_free
1400 : : !!
1401 : : !! FUNCTION
1402 : : !! Deallocate 2d array of slkmat_dp_t elements
1403 : : !!
1404 : : !! SOURCE
1405 : :
1406 : 0 : subroutine slk_array2_free(slk_arr2)
1407 : : class(basemat_t),intent(inout) :: slk_arr2(:,:)
1408 : : integer :: i1, i2
1409 [ # # ]: 0 : do i2=1,size(slk_arr2, dim=2)
1410 [ # # ]: 0 : do i1=1,size(slk_arr2, dim=1)
1411 : 0 : call slk_arr2(i1, i2)%free()
1412 : : end do
1413 : : end do
1414 : 0 : end subroutine slk_array2_free
1415 : : !!***
1416 : :
1417 : : !----------------------------------------------------------------------
1418 : :
1419 : : !!****f* m_slk/slk_array3_free
1420 : : !! NAME
1421 : : !! slk_array3_free
1422 : : !!
1423 : : !! FUNCTION
1424 : : !! Deallocate 3d array of slkmat_dp_t elements
1425 : : !!
1426 : : !! SOURCE
1427 : :
1428 : 0 : subroutine slk_array3_free(slk_arr3)
1429 : : class(basemat_t),intent(inout) :: slk_arr3(:,:,:)
1430 : : integer :: i1, i2, i3
1431 [ # # ]: 0 : do i3=1,size(slk_arr3, dim=3)
1432 [ # # ]: 0 : do i2=1,size(slk_arr3, dim=2)
1433 [ # # ]: 0 : do i1=1,size(slk_arr3, dim=1)
1434 : 0 : call slk_arr3(i1, i2, i3)%free()
1435 : : end do
1436 : : end do
1437 : : end do
1438 : 0 : end subroutine slk_array3_free
1439 : : !!***
1440 : :
1441 : : !----------------------------------------------------------------------
1442 : :
1443 : : !!****f* m_slk/slk_array4_free
1444 : : !! NAME
1445 : : !! slk_array4_free
1446 : : !!
1447 : : !! FUNCTION
1448 : : !! Deallocate 4d array of slkmat_dp_t elements
1449 : : !!
1450 : : !! SOURCE
1451 : :
1452 : 0 : subroutine slk_array4_free(slk_arr4)
1453 : : class(basemat_t),intent(inout) :: slk_arr4(:,:,:,:)
1454 : : integer :: i1, i2, i3, i4
1455 [ # # ]: 0 : do i4=1,size(slk_arr4, dim=4)
1456 [ # # ]: 0 : do i3=1,size(slk_arr4, dim=3)
1457 [ # # ]: 0 : do i2=1,size(slk_arr4, dim=2)
1458 [ # # ]: 0 : do i1=1,size(slk_arr4, dim=1)
1459 : 0 : call slk_arr4(i1, i2, i3, i4)%free()
1460 : : end do
1461 : : end do
1462 : : end do
1463 : : end do
1464 : 0 : end subroutine slk_array4_free
1465 : : !!***
1466 : :
1467 : : !----------------------------------------------------------------------
1468 : :
1469 : : !!****f* m_slk/slk_array5_free
1470 : : !! NAME
1471 : : !! slk_array5_free
1472 : : !!
1473 : : !! FUNCTION
1474 : : !! Deallocate 5d array of slkmat_dp_t elements
1475 : : !!
1476 : : !! SOURCE
1477 : :
1478 : 0 : subroutine slk_array5_free(slk_arr5)
1479 : : class(basemat_t),intent(inout) :: slk_arr5(:,:,:,:,:)
1480 : : integer :: i1, i2, i3, i4, i5
1481 [ # # ]: 0 : do i5=1,size(slk_arr5, dim=5)
1482 [ # # ]: 0 : do i4=1,size(slk_arr5, dim=4)
1483 [ # # ]: 0 : do i3=1,size(slk_arr5, dim=3)
1484 [ # # ]: 0 : do i2=1,size(slk_arr5, dim=2)
1485 [ # # ]: 0 : do i1=1,size(slk_arr5, dim=1)
1486 : 0 : call slk_arr5(i1, i2, i3, i4, i5)%free()
1487 : : end do
1488 : : end do
1489 : : end do
1490 : : end do
1491 : : end do
1492 : 0 : end subroutine slk_array5_free
1493 : : !!***
1494 : :
1495 : : !----------------------------------------------------------------------
1496 : :
1497 : : !!****f* m_slk/slk_array_set_zero
1498 : : !! NAME
1499 : : !! slk_array_set_zero
1500 : : !!
1501 : : !! FUNCTION
1502 : : !! Elemental routine to set the value of the PBLAS buffer to zero
1503 : : !!
1504 : : !! SOURCE
1505 : :
1506 : 0 : elemental subroutine slk_array_set_zero(mat)
1507 : :
1508 : : !Arguments ------------------------------------
1509 : : class(basemat_t),intent(inout) :: mat
1510 : :
1511 : : select type (mat)
1512 : : class is (slkmat_dp_t)
1513 [ # # # # : 0 : if (allocated(mat%buffer_cplx)) mat%buffer_cplx = zero
# # ]
1514 [ # # # # : 0 : if (allocated(mat%buffer_real)) mat%buffer_real = zero
# # ]
1515 : : class is (slkmat_sp_t)
1516 [ # # # # : 0 : if (allocated(mat%buffer_cplx)) mat%buffer_cplx = cmplx(zero, kind=sp)
# # ]
1517 [ # # # # : 0 : if (allocated(mat%buffer_real)) mat%buffer_real = real(zero, kind=sp)
# # ]
1518 : : end select
1519 : :
1520 : 0 : end subroutine slk_array_set_zero
1521 : : !!***
1522 : :
1523 : : !----------------------------------------------------------------------
1524 : :
1525 : : !!****f* m_slk/basemat_gpu_set_zero
1526 : : !! NAME
1527 : : !! basemat_gpu_set_zero
1528 : : !!
1529 : : !! FUNCTION
1530 : : !! Elemental routine to set the value of the PBLAS buffer to zero
1531 : : !!
1532 : : !! SOURCE
1533 : :
1534 : 0 : subroutine basemat_gpu_set_zero(mat)
1535 : :
1536 : : !Arguments ------------------------------------
1537 : : class(basemat_t),target,intent(inout) :: mat
1538 : :
1539 : : #ifdef HAVE_OPENMP_OFFLOAD
1540 : : !Local variables-------------------------------
1541 : : type(c_ptr) :: gpu_ptr
1542 : : ! *********************************************************************
1543 : :
1544 : : select type (mat)
1545 : : class is (slkmat_dp_t)
1546 : : if (allocated(mat%buffer_cplx)) then
1547 : : gpu_ptr = xomp_get_mapped_ptr(c_loc(mat%buffer_cplx))
1548 : : ABI_CHECK_CNULL(gpu_ptr, "buffer_cplx not on GPU!")
1549 : : call gpu_memset(gpu_ptr, 0, mat%bufsize*dp*2)
1550 : : end if
1551 : : if (allocated(mat%buffer_real)) then
1552 : : gpu_ptr = xomp_get_mapped_ptr(c_loc(mat%buffer_real))
1553 : : ABI_CHECK_CNULL(gpu_ptr, "buffer_real not on GPU!")
1554 : : call gpu_memset(gpu_ptr, 0, mat%bufsize*dp)
1555 : : end if
1556 : : class is (slkmat_sp_t)
1557 : : if (allocated(mat%buffer_cplx)) then
1558 : : gpu_ptr = xomp_get_mapped_ptr(c_loc(mat%buffer_cplx))
1559 : : ABI_CHECK_CNULL(gpu_ptr, "buffer_cplx not on GPU!")
1560 : : call gpu_memset(gpu_ptr, 0, mat%bufsize*sp*2)
1561 : : end if
1562 : : if (allocated(mat%buffer_real)) then
1563 : : gpu_ptr = xomp_get_mapped_ptr(c_loc(mat%buffer_real))
1564 : : ABI_CHECK_CNULL(gpu_ptr, "buffer_real not on GPU!")
1565 : : call gpu_memset(gpu_ptr, 0, mat%bufsize*sp)
1566 : : end if
1567 : : end select
1568 : : #else
1569 : 0 : ABI_ERROR("basemat_gpu_set cannot be used if HAVE_OPENMP_OFFLOAD is not defined!")
1570 : : ABI_UNUSED(mat%size_local(1))
1571 : : #endif
1572 : :
1573 : 0 : end subroutine basemat_gpu_set_zero
1574 : : !!***
1575 : :
1576 : : !!****f* m_slk/slk_array1_gpu_set_zero
1577 : : !! NAME
1578 : : !! slk_array1_gpu_set_zero
1579 : : !!
1580 : : !! FUNCTION
1581 : : !!
1582 : : !! SOURCE
1583 : :
1584 : 0 : subroutine slk_array1_gpu_set_zero(mat1d)
1585 : :
1586 : : !Arguments ------------------------------------
1587 : : class(basemat_t),intent(inout) :: mat1d(:)
1588 : :
1589 : : !Local variables-------------------------------
1590 : : integer :: i1
1591 : : ! *********************************************************************
1592 : :
1593 [ # # ]: 0 : do i1=1,size(mat1d, dim=1)
1594 : 0 : call mat1d(i1)%gpu_set_zero()
1595 : : end do
1596 : :
1597 : 0 : end subroutine slk_array1_gpu_set_zero
1598 : : !!***
1599 : :
1600 : : !!****f* m_slk/slk_array2_gpu_set_zero
1601 : : !! NAME
1602 : : !! slk_array2_gpu_set_zero
1603 : : !!
1604 : : !! FUNCTION
1605 : : !!
1606 : : !! SOURCE
1607 : :
1608 : 0 : subroutine slk_array2_gpu_set_zero(mat2d)
1609 : :
1610 : : !Arguments ------------------------------------
1611 : : class(basemat_t),intent(inout) :: mat2d(:,:)
1612 : :
1613 : : !Local variables-------------------------------
1614 : : integer :: i1, i2
1615 : : ! *********************************************************************
1616 : :
1617 [ # # ]: 0 : do i2=1,size(mat2d, dim=2)
1618 [ # # ]: 0 : do i1=1,size(mat2d, dim=1)
1619 : 0 : call mat2d(i1, i2)%gpu_set_zero()
1620 : : end do
1621 : : end do
1622 : :
1623 : 0 : end subroutine slk_array2_gpu_set_zero
1624 : : !!***
1625 : :
1626 : : !!****f* m_slk/slk_array3_gpu_set_zero
1627 : : !! NAME
1628 : : !! slk_array3_gpu_set_zero
1629 : : !!
1630 : : !! FUNCTION
1631 : : !!
1632 : : !! SOURCE
1633 : :
1634 : 0 : subroutine slk_array3_gpu_set_zero(mat3d)
1635 : :
1636 : : !Arguments ------------------------------------
1637 : : class(basemat_t),intent(inout) :: mat3d(:,:,:)
1638 : :
1639 : : !Local variables-------------------------------
1640 : : integer :: i1,i2,i3
1641 : : ! *********************************************************************
1642 : :
1643 [ # # ]: 0 : do i3=1,size(mat3d, dim=3)
1644 [ # # ]: 0 : do i2=1,size(mat3d, dim=2)
1645 [ # # ]: 0 : do i1=1,size(mat3d, dim=1)
1646 : 0 : call mat3d(i1,i2,i3)%gpu_set_zero()
1647 : : end do
1648 : : end do
1649 : : end do
1650 : :
1651 : 0 : end subroutine slk_array3_gpu_set_zero
1652 : : !!***
1653 : :
1654 : : !!****f* m_slk/slk_array4_gpu_set_zero
1655 : : !! NAME
1656 : : !! slk_array4_gpu_set_zero
1657 : : !!
1658 : : !! FUNCTION
1659 : : !!
1660 : : !! SOURCE
1661 : :
1662 : 0 : subroutine slk_array4_gpu_set_zero(mat4d)
1663 : :
1664 : : !Arguments ------------------------------------
1665 : : class(basemat_t),intent(inout) :: mat4d(:,:,:,:)
1666 : :
1667 : : !Local variables-------------------------------
1668 : : integer :: i1, i2, i3, i4
1669 : : ! *********************************************************************
1670 : :
1671 [ # # ]: 0 : do i4=1,size(mat4d, dim=4)
1672 [ # # ]: 0 : do i3=1,size(mat4d, dim=3)
1673 [ # # ]: 0 : do i2=1,size(mat4d, dim=2)
1674 [ # # ]: 0 : do i1=1,size(mat4d, dim=1)
1675 : 0 : call mat4d(i1, i2, i3, i4)%gpu_set_zero()
1676 : : end do
1677 : : end do
1678 : : end do
1679 : : end do
1680 : :
1681 : 0 : end subroutine slk_array4_gpu_set_zero
1682 : : !!***
1683 : :
1684 : : !!****f* m_slk/slk_array5_gpu_set_zero
1685 : : !! NAME
1686 : : !! slk_array5_gpu_set_zero
1687 : : !!
1688 : : !! FUNCTION
1689 : : !!
1690 : : !! SOURCE
1691 : :
1692 : 0 : subroutine slk_array5_gpu_set_zero(mat5d)
1693 : :
1694 : : !Arguments ------------------------------------
1695 : : class(basemat_t),intent(inout) :: mat5d(:,:,:,:,:)
1696 : :
1697 : : !Local variables-------------------------------
1698 : : integer :: i1,i2,i3,i4,i5
1699 : : ! *********************************************************************
1700 : :
1701 [ # # ]: 0 : do i5=1,size(mat5d, dim=5)
1702 [ # # ]: 0 : do i4=1,size(mat5d, dim=4)
1703 [ # # ]: 0 : do i3=1,size(mat5d, dim=3)
1704 [ # # ]: 0 : do i2=1,size(mat5d, dim=2)
1705 [ # # ]: 0 : do i1=1,size(mat5d, dim=1)
1706 : 0 : call mat5d(i1,i2,i3,i4,i5)%gpu_set_zero()
1707 : : end do
1708 : : end do
1709 : : end do
1710 : : end do
1711 : : end do
1712 : :
1713 : 0 : end subroutine slk_array5_gpu_set_zero
1714 : : !!***
1715 : :
1716 : : !----------------------------------------------------------------------
1717 : :
1718 : : !!****f* m_slk/basemat_is_gpu_mapped
1719 : : !! NAME
1720 : : !! basemat_is_gpu_mapped
1721 : : !!
1722 : : !! FUNCTION
1723 : : !! Return True if the local buffer is mapped to the GPU and the pointer on the gpu.
1724 : : !!
1725 : : !! SOURCE
1726 : :
1727 : 0 : logical function basemat_is_gpu_mapped(mat, gpu_ptr) result(is_gpu_mapped)
1728 : :
1729 : : !Arguments ------------------------------------
1730 : : class(basemat_t),target,intent(in) :: mat
1731 : : type(c_ptr),intent(out) :: gpu_ptr
1732 : :
1733 : : !Local variables-------------------------------
1734 : : #ifdef HAVE_OPENMP_OFFLOAD
1735 : : real(sp), contiguous, pointer :: buf_real_sp(:,:)
1736 : : real(dp), contiguous, pointer :: buf_real_dp(:,:)
1737 : : complex(sp), contiguous, pointer :: buf_cplx_sp(:,:)
1738 : : complex(dp), contiguous, pointer :: buf_cplx_dp(:,:)
1739 : : #endif
1740 : : ! *********************************************************************
1741 : :
1742 : 0 : is_gpu_mapped = .False.; gpu_ptr = c_null_ptr
1743 : : #ifdef HAVE_OPENMP_OFFLOAD
1744 : : select type (mat)
1745 : : class is (slkmat_dp_t)
1746 : : if (allocated(mat%buffer_cplx)) then
1747 : : buf_cplx_dp => mat%buffer_cplx
1748 : : gpu_ptr = xomp_get_mapped_ptr(c_loc(buf_cplx_dp))
1749 : : is_gpu_mapped = c_associated(gpu_ptr)
1750 : : end if
1751 : : if (allocated(mat%buffer_real)) then
1752 : : buf_real_dp => mat%buffer_real
1753 : : gpu_ptr = xomp_get_mapped_ptr(c_loc(buf_real_dp))
1754 : : is_gpu_mapped = c_associated(gpu_ptr)
1755 : : end if
1756 : :
1757 : : class is (slkmat_sp_t)
1758 : : if (allocated(mat%buffer_cplx)) then
1759 : : buf_cplx_sp => mat%buffer_cplx
1760 : : gpu_ptr= xomp_get_mapped_ptr(c_loc(buf_cplx_sp))
1761 : : is_gpu_mapped = c_associated(gpu_ptr)
1762 : : end if
1763 : : if (allocated(mat%buffer_real)) then
1764 : : buf_real_sp => mat%buffer_real
1765 : : gpu_ptr = xomp_get_mapped_ptr(c_loc(buf_real_sp))
1766 : : is_gpu_mapped = c_associated(gpu_ptr)
1767 : : end if
1768 : : end select
1769 : : #else
1770 : : ABI_UNUSED(mat%size_local(1))
1771 : : #endif
1772 : :
1773 : 0 : end function basemat_is_gpu_mapped
1774 : : !!***
1775 : :
1776 : : !----------------------------------------------------------------------
1777 : :
1778 : : !!****f* m_slk/basemat_gpu_map
1779 : : !! NAME
1780 : : !! basemat_gpu_map
1781 : : !!
1782 : : !! FUNCTION
1783 : : !! Use OpenMP to allocate/delete the local buffer on the GPU.
1784 : : !! Does nothing if HAVE_OPENMP_OFFLOAD is not defined.
1785 : : !!
1786 : : !! INPUTS
1787 : : !! gpu_action= "None": do nothing.
1788 : : !! "alloc": map the local buffer on the GPU (target enter data map(alloc)).
1789 : : !! "alloc_zero": as "alloc", then set the GPU buffer to zero.
1790 : : !! "delete": unmap the local buffer (target exit data map(delete)).
1791 : : !! "update_from": copy the GPU buffer to the host (target update from).
1792 : : !! "update_to": copy the host buffer to the GPU (target update to).
1793 : : !!
1794 : : !! SOURCE
1795 : :
1796 : 0 : subroutine basemat_gpu_map(mat, gpu_action)
1797 : :
1798 : : !Arguments ------------------------------------
1799 : : class(basemat_t),target,intent(inout) :: mat
1800 : : character(len=*), intent(in) :: gpu_action
1801 : :
1802 : : !Local variables-------------------------------
1803 : : #ifdef HAVE_OPENMP_OFFLOAD
1804 : : real(sp), contiguous, pointer :: buf_real_sp(:,:)
1805 : : real(dp), contiguous, pointer :: buf_real_dp(:,:)
1806 : : complex(sp), contiguous, pointer :: buf_cplx_sp(:,:)
1807 : : complex(dp), contiguous, pointer :: buf_cplx_dp(:,:)
1808 : : #endif
1809 : : ! *********************************************************************
1810 : :
1811 [ # # ]: 0 : if (.not. string_in(gpu_action, "None, alloc, alloc_zero, delete, update_from, update_to")) then
1812 : 0 : ABI_ERROR(sjoin("Invalid gpu_action", gpu_action))
1813 : : ABI_UNUSED(mat%size_local(1))
1814 : : end if
1815 : :
1816 : : if (gpu_action == "None") return
1817 : :
1818 : : #ifdef HAVE_OPENMP_OFFLOAD
1819 : : select type (mat)
1820 : : class is (slkmat_dp_t)
1821 : : if (allocated(mat%buffer_cplx)) then
1822 : : buf_cplx_dp => mat%buffer_cplx
1823 : : if (string_in(gpu_action, "alloc, alloc_zero")) then
1824 : : !$OMP TARGET ENTER DATA MAP(alloc:buf_cplx_dp)
1825 : : if (gpu_action == "alloc_zero") call gpu_set_to_zero_complex(mat%buffer_cplx, mat%bufsize)
1826 : : else if (gpu_action == "delete") then !.and. c_associated(xomp_get_mapped_ptr(c_loc(buf_cplx_dp))
1827 : : !$OMP TARGET EXIT DATA MAP(delete:buf_cplx_dp)
1828 : : else if (gpu_action == "update_from") then
1829 : : !$OMP TARGET UPDATE FROM(buf_cplx_dp)
1830 : : else if (gpu_action == "update_to") then
1831 : : !$OMP TARGET UPDATE TO(buf_cplx_dp)
1832 : : end if
1833 : : end if
1834 : : if (allocated(mat%buffer_real)) then
1835 : : buf_real_dp => mat%buffer_real
1836 : : if (string_in(gpu_action, "alloc, alloc_zero")) then
1837 : : !$OMP TARGET ENTER DATA MAP(alloc:buf_real_dp)
1838 : : if (gpu_action == "alloc_zero") call gpu_set_to_zero(mat%buffer_real, mat%bufsize)
1839 : : else if (gpu_action == "delete") then !.and. c_associated(xomp_get_mapped_ptr(c_loc(buf_real_dp))
1840 : : !$OMP TARGET EXIT DATA MAP(delete:buf_real_dp)
1841 : : else if (gpu_action == "update_from") then
1842 : : !$OMP TARGET UPDATE FROM(buf_real_dp)
1843 : : else if (gpu_action == "update_to") then
1844 : : !$OMP TARGET UPDATE TO(buf_real_dp)
1845 : : end if
1846 : : end if
1847 : :
1848 : : class is (slkmat_sp_t)
1849 : : if (allocated(mat%buffer_cplx)) then
1850 : : buf_cplx_sp => mat%buffer_cplx
1851 : : if (string_in(gpu_action, "alloc, alloc_zero")) then
1852 : : !$OMP TARGET ENTER DATA MAP(alloc:buf_cplx_sp)
1853 : : if (gpu_action == "alloc_zero") call gpu_set_to_zero_complex_sp(mat%buffer_cplx, mat%bufsize)
1854 : : else if (gpu_action == "delete") then !.and. c_associated(xomp_get_mapped_ptr(c_loc(buf_cplx_sp))
1855 : : !$OMP TARGET EXIT DATA MAP(delete:buf_cplx_sp)
1856 : : else if (gpu_action == "update_from") then
1857 : : !$OMP TARGET UPDATE FROM(buf_cplx_sp)
1858 : : else if (gpu_action == "update_to") then
1859 : : !$OMP TARGET UPDATE TO(buf_cplx_sp)
1860 : : end if
1861 : : end if
1862 : : if (allocated(mat%buffer_real)) then
1863 : : buf_real_sp => mat%buffer_real
1864 : : if (string_in(gpu_action, "alloc, alloc_zero")) then
1865 : : !$OMP TARGET ENTER DATA MAP(alloc:buf_real_sp)
1866 : : if (gpu_action == "alloc_zero") call gpu_set_to_zero_sp(mat%buffer_real, mat%bufsize)
1867 : : else if (gpu_action == "delete") then !.and. c_associated(xomp_get_mapped_ptr(c_loc(buf_real_sp))
1868 : : !$OMP TARGET EXIT DATA MAP(delete:buf_real_sp)
1869 : : else if (gpu_action == "update_from") then
1870 : : !$OMP TARGET UPDATE FROM(buf_real_sp)
1871 : : else if (gpu_action == "update_to") then
1872 : : !$OMP TARGET UPDATE TO(buf_real_sp)
1873 : : end if
1874 : : end if
1875 : : end select
1876 : : #endif
1877 : :
1878 : : end subroutine basemat_gpu_map
1879 : : !!***
1880 : :
1881 : : !----------------------------------------------------------------------
1882 : :
1883 : : !!****f* m_slk/slk_array_locmem_mb
1884 : : !! NAME
1885 : : !! slk_array_locmem_mb
1886 : : !!
1887 : : !! FUNCTION
1888 : : !! Elemental function to compute the memory allocated for an array of slkmat_dp_t elements
1889 : : !! Usage: mem_mb = sum(mat_array)
1890 : : !!
1891 : : !! SOURCE
1892 : :
1893 : 0 : elemental real(dp) function slk_array_locmem_mb(mat) result(mem_mb)
1894 : : class(basemat_t),intent(in) :: mat
1895 : 0 : mem_mb = mat%locmem_mb()
1896 : 0 : end function slk_array_locmem_mb
1897 : : !!***
1898 : :
1899 : : !----------------------------------------------------------------------
1900 : :
1901 : : !!****f* m_slk/block_dist_1d
1902 : : !! NAME
1903 : : !! block_dist_1d
1904 : : !!
1905 : : !! FUNCTION
1906 : : !! Return block size for one-dimensional block column (row) distribution.
1907 : : !! Mainly used to assign blocks of contiguous columns (rows) of a matrix to successive processes
1908 : : !! when a 1d grid is employed.
1909 : : !!
1910 : : !! It is usually interfaced with CPP macros, e.g:
1911 : : !!
1912 : : !! ABI_CHECK(block_dist_1d(mat_size, nproc, block_size, msg), msg)
1913 : : !!
1914 : : !! INPUTS
1915 : : !! mat_size=Size of the matrix (either number of rows or number of columns)
1916 : : !! nproc=Number of processes in the 1D scalapack grid
1917 : : !!
1918 : : !! OUTPUT
1919 : : !! ok= Boolean flag with exit status (idle processes are not allowed).
1920 : : !! block_size=Size of the block along this axis needed for one-dimensional block distribution
1921 : : !! msg=Error message (if not ok)
1922 : : !!
1923 : : !! SOURCE
1924 : :
1925 : 0 : logical function block_dist_1d(mat_size, nproc, block_size, msg) result (ok)
1926 : :
1927 : : !Arguments ------------------------------------
1928 : : integer, intent(in) :: mat_size, nproc
1929 : : integer,intent(out) :: block_size
1930 : : character(len=*),intent(out) :: msg
1931 : : ! *********************************************************************
1932 : :
1933 : 0 : ok = .True.; msg = ""
1934 : : !block_size = 1; return
1935 : :
1936 : 0 : block_size = mat_size / nproc
1937 [ # # ]: 0 : if (block_size == 0) then
1938 : 0 : ok = .False.
1939 : : write(msg, "(2(a,i0), 2a)") &
1940 : 0 : "The number of MPI processors: ", nproc, " exceeds the number of rows (columns) of the matrix: ", mat_size, ch10, &
1941 : 0 : "Decrease the number of MPI processes for the scalapack level."
1942 : 0 : return
1943 : : end if
1944 : :
1945 [ # # ]: 0 : if (mod(mat_size, nproc) /= 0) block_size = block_size + 1
1946 : :
1947 [ # # ]: 0 : end function block_dist_1d
1948 : : !!***
1949 : :
1950 : : !----------------------------------------------------------------------
1951 : :
1952 : : !!****f* m_slk/slk_has_elpa
1953 : : !! NAME
1954 : : !! slk_has_elpa
1955 : : !!
1956 : : !! FUNCTION
1957 : : !! Return True if ELPA support is activated
1958 : : !!
1959 : : !! SOURCE
1960 : :
1961 : 0 : pure logical function slk_has_elpa() result (ans)
1962 : :
1963 : 0 : ans = .False.
1964 : : #ifdef HAVE_LINALG_ELPA
1965 : : ans = .True.
1966 : : #endif
1967 : :
1968 : 0 : end function slk_has_elpa
1969 : : !!***
1970 : :
1971 : : !----------------------------------------------------------------------
1972 : :
1973 : : !!****f* m_slk/matrix_get_local_cplx
1974 : : !! NAME
1975 : : !! matrix_get_local_cplx
1976 : : !!
1977 : : !! FUNCTION
1978 : : !! Returns a local matrix coefficient of complex type.
1979 : : !! The coefficient is selected by its local indices.
1980 : : !!
1981 : : !! INPUTS
1982 : : !! matrix= the matrix to process
1983 : : !! i= row in the matrix
1984 : : !! j= column in the matrix
1985 : : !!
1986 : : !! OUTPUT
1987 : : !! The value of the local matrix.
1988 : : !!
1989 : : !! SOURCE
1990 : :
1991 : 0 : pure complex(dpc) function matrix_get_local_cplx(matrix, i, j)
1992 : :
1993 : : !Arguments ------------------------------------
1994 : : class(slkmat_dp_t),intent(in) :: matrix
1995 : : integer, intent(in) :: i,j
1996 : : ! *********************************************************************
1997 : :
1998 : 0 : matrix_get_local_cplx = matrix%buffer_cplx(i,j)
1999 : :
2000 : 0 : end function matrix_get_local_cplx
2001 : : !!***
2002 : :
2003 : : !----------------------------------------------------------------------
2004 : :
2005 : : !!****f* m_slk/matrix_get_local_real
2006 : : !! NAME
2007 : : !! matrix_get_local_real
2008 : : !!
2009 : : !! FUNCTION
2010 : : !! Returns a local matrix coefficient of double precision type.
2011 : : !!
2012 : : !! INPUTS
2013 : : !! matrix= the matrix to process
2014 : : !! i= row in the matrix
2015 : : !! j= column in the matrix
2016 : : !!
2017 : : !! SOURCE
2018 : :
2019 : 0 : pure real(dp) function matrix_get_local_real(matrix,i,j)
2020 : :
2021 : : !Arguments ------------------------------------
2022 : : class(slkmat_dp_t),intent(in) :: matrix
2023 : : integer, intent(in) :: i,j
2024 : : ! *********************************************************************
2025 : :
2026 : 0 : matrix_get_local_real = matrix%buffer_real(i,j)
2027 : :
2028 : 0 : end function matrix_get_local_real
2029 : : !!***
2030 : :
2031 : : !----------------------------------------------------------------------
2032 : :
2033 : : !!****f* m_slk/matrix_set_local_cplx
2034 : : !! NAME
2035 : : !! matrix_set_local_cplx
2036 : : !!
2037 : : !! FUNCTION
2038 : : !! Sets a local matrix coefficient of complex type.
2039 : : !! The coefficient is selected by its local indices.
2040 : : !!
2041 : : !! INPUTS
2042 : : !! i= row in the matrix
2043 : : !! j= column in the matrix
2044 : : !! value= the value to set
2045 : : !!
2046 : : !! SIDE EFFECTS
2047 : : !! matrix%buffer_cplx(i,j) filled with value
2048 : : !!
2049 : : !! SOURCE
2050 : :
2051 : 0 : pure subroutine matrix_set_local_cplx(matrix,i,j,value)
2052 : :
2053 : : !Arguments ------------------------------------
2054 : : class(slkmat_dp_t),intent(inout) :: matrix
2055 : : integer, intent(in) :: i,j
2056 : : complex(dp), intent(in) :: value
2057 : : ! *********************************************************************
2058 : :
2059 : 0 : matrix%buffer_cplx(i,j) = value
2060 : :
2061 : 0 : end subroutine matrix_set_local_cplx
2062 : : !!***
2063 : :
2064 : : !----------------------------------------------------------------------
2065 : :
2066 : : !!****f* m_slk/matrix_set_local_real
2067 : : !! NAME
2068 : : !! matrix_set_local_real
2069 : : !!
2070 : : !! FUNCTION
2071 : : !! Sets a local matrix coefficient of double precision type.
2072 : : !!
2073 : : !! INPUTS
2074 : : !! i= row in the matrix
2075 : : !! j= column in the matrix
2076 : : !! value= the value to set
2077 : : !!
2078 : : !! SIDE EFFECTS
2079 : : !! matrix%buffer_real(i,j) set to value
2080 : : !!
2081 : : !! SOURCE
2082 : :
2083 : 0 : pure subroutine matrix_set_local_real(matrix, i, j, value)
2084 : :
2085 : : !Arguments ------------------------------------
2086 : : class(slkmat_dp_t),intent(inout) :: matrix
2087 : : integer, intent(in) :: i,j
2088 : : real(dp), intent(in) :: value
2089 : : ! *********************************************************************
2090 : :
2091 : 0 : matrix%buffer_real(i,j) = value
2092 : :
2093 : 0 : end subroutine matrix_set_local_real
2094 : : !!***
2095 : :
2096 : : !----------------------------------------------------------------------
2097 : :
2098 : : !!****f* m_slk/idx_loc
2099 : : !! NAME
2100 : : !! idx_loc
2101 : : !!
2102 : : !! FUNCTION
2103 : : !! Return the number of rows (columns) among the first i (j) global rows (columns) that are stored
2104 : : !! on this processor (NUMROC). This is the local index of (i, j) only if this processor owns the entry,
2105 : : !! use glob2loc to find the owner. Called with the global sizes, it returns the local sizes.
2106 : : !!
2107 : : !! INPUTS
2108 : : !! matrix= the matrix to process
2109 : : !! i= global row index
2110 : : !! j= global column index
2111 : : !!
2112 : : !! OUTPUT
2113 : : !! iloc= number of local rows with global index <= i
2114 : : !! jloc= number of local columns with global index <= j
2115 : : !!
2116 : : !! SOURCE
2117 : :
2118 : 0 : subroutine basemat_idx_loc(matrix, i, j, iloc, jloc)
2119 : :
2120 : : !Arguments ------------------------------------
2121 : : class(basemat_t),intent(in) :: matrix
2122 : : integer, intent(in) :: i,j
2123 : : integer, intent(out) :: iloc,jloc
2124 : : ! *********************************************************************
2125 : :
2126 : 0 : iloc = glob_loc__(matrix, i, 1)
2127 : 0 : jloc = glob_loc__(matrix, j, 2)
2128 : :
2129 : 0 : end subroutine basemat_idx_loc
2130 : : !!***
2131 : :
2132 : : !----------------------------------------------------------------------
2133 : :
2134 : : !!****f* m_slk/glob_loc__
2135 : : !! NAME
2136 : : !! glob_loc__
2137 : : !!
2138 : : !! FUNCTION
2139 : : !! Return the number of rows (lico=1) or columns (lico=2) among the first idx global ones
2140 : : !! that are stored on this processor (NUMROC). See basemat_idx_loc.
2141 : : !!
2142 : : !! INPUTS
2143 : : !! matrix= the matrix to process
2144 : : !! idx= global row (column) index
2145 : : !! lico= 1 for rows, 2 for columns
2146 : : !!
2147 : : !! SOURCE
2148 : :
2149 : : integer function glob_loc__(matrix, idx, lico)
2150 : :
2151 : : !Arguments ------------------------------------
2152 : : class(basemat_t),intent(in) :: matrix
2153 : : integer, intent(in) :: idx, lico
2154 : : ! *********************************************************************
2155 : :
2156 : : #ifdef HAVE_LINALG_SCALAPACK
2157 : : glob_loc__ = NUMROC(idx, matrix%size_blocs(lico), &
2158 : : matrix%processor%coords(lico), 0, matrix%processor%grid%dims(lico))
2159 : : #endif
2160 : :
2161 : : end function glob_loc__
2162 : : !!***
2163 : :
2164 : : !----------------------------------------------------------------------
2165 : :
2166 : : !!****f* m_slk/basemat_glob2loc
2167 : : !! NAME
2168 : : !! basemat_glob2loc
2169 : : !!
2170 : : !! FUNCTION
2171 : : !! Determine the local indices of an element from its global indices and return haveit bool flag.
2172 : : !!
2173 : : !! INPUTS
2174 : : !! iglob= global row index.
2175 : : !! jglob= global column index.
2176 : : !!
2177 : : !! OUTPUT
2178 : : !! iloc= local row index in the buffer of the process owning (iglob, jglob)
2179 : : !! jloc= local column index in the buffer of the process owning (iglob, jglob)
2180 : : !! haveit= True if (iglob, jglob) is stored on this proc. iloc and jloc are valid on this proc only if haveit.
2181 : : !!
2182 : : !! SOURCE
2183 : :
2184 : 0 : subroutine basemat_glob2loc(mat, iglob, jglob, iloc, jloc, haveit)
2185 : :
2186 : : !Arguments ------------------------------------
2187 : : class(basemat_t),intent(in) :: mat
2188 : : integer, intent(in) :: iglob, jglob
2189 : : integer, intent(out) :: iloc, jloc
2190 : : logical,intent(out) :: haveit
2191 : :
2192 : : !Local variables-------------------------------
2193 : : integer :: row_src, col_src
2194 : : ! *********************************************************************
2195 : :
2196 : : #ifdef HAVE_LINALG_SCALAPACK
2197 : : ! SUBROUTINE INFOG2L( GRINDX, GCINDX, DESC, NPROW, NPCOL, MYROW, MYCOL, LRINDX, LCINDX, RSRC, CSRC)
2198 : :
2199 : : call INFOG2L(iglob, jglob, mat%desc, mat%processor%grid%dims(1), mat%processor%grid%dims(2), &
2200 : : mat%processor%coords(1), mat%processor%coords(2), iloc, jloc, row_src, col_src)
2201 : :
2202 : : haveit = all(mat%processor%coords == [row_src, col_src])
2203 : : #endif
2204 : :
2205 : 0 : end subroutine basemat_glob2loc
2206 : : !!***
2207 : :
2208 : : !----------------------------------------------------------------------
2209 : :
2210 : : !!****f* m_slk/basemat_loc2glob
2211 : : !! NAME
2212 : : !! basemat_loc2glob
2213 : : !!
2214 : : !! FUNCTION
2215 : : !! Determine the global indices of an element from its local indices.
2216 : : !!
2217 : : !! INPUTS
2218 : : !! matrix= the matrix to process.
2219 : : !! iloc= local row index.
2220 : : !! jloc= local column index.
2221 : : !!
2222 : : !! OUTPUT
2223 : : !! i= row in the matrix
2224 : : !! j= column in the matrix
2225 : : !!
2226 : : !! SOURCE
2227 : :
2228 : 0 : pure subroutine basemat_loc2glob(matrix, iloc, jloc, i, j)
2229 : :
2230 : : !Arguments ------------------------------------
2231 : : class(basemat_t),intent(in) :: matrix
2232 : : integer, intent(in) :: iloc,jloc
2233 : : integer, intent(out) :: i,j
2234 : : ! *********************************************************************
2235 : :
2236 : 0 : i = loc_glob__(matrix, matrix%processor, iloc, 1)
2237 : 0 : j = loc_glob__(matrix, matrix%processor, jloc, 2)
2238 : :
2239 : 0 : end subroutine basemat_loc2glob
2240 : : !!***
2241 : :
2242 : : !!****f* m_slk/basemat_loc2grow
2243 : : !! NAME
2244 : : !! basemat_loc2grow
2245 : : !!
2246 : : !! FUNCTION
2247 : : !! Determine the global row index from the local index
2248 : : !!
2249 : : !! INPUTS
2250 : : !! matrix= the matrix to process.
2251 : : !! iloc= local row index.
2252 : : !!
2253 : : !! SOURCE
2254 : :
2255 : 0 : integer pure function basemat_loc2grow(matrix, iloc) result(iglob)
2256 : :
2257 : : !Arguments ------------------------------------
2258 : : class(basemat_t),intent(in) :: matrix
2259 : : integer, intent(in) :: iloc
2260 : : ! *********************************************************************
2261 : :
2262 : 0 : iglob = loc_glob__(matrix, matrix%processor, iloc, 1)
2263 : :
2264 : 0 : end function basemat_loc2grow
2265 : : !!***
2266 : :
2267 : : !!****f* m_slk/basemat_loc2col
2268 : : !! NAME
2269 : : !! basemat_loc2col
2270 : : !!
2271 : : !! FUNCTION
2272 : : !! Determine the global column index of an element from the local index
2273 : : !!
2274 : : !! INPUTS
2275 : : !! matrix= the matrix to process.
2276 : : !! jloc= local column index.
2277 : : !!
2278 : : !! SOURCE
2279 : :
2280 : 0 : integer pure function basemat_loc2col(matrix, jloc) result(jglob)
2281 : :
2282 : : !Arguments ------------------------------------
2283 : : class(basemat_t),intent(in) :: matrix
2284 : : integer, intent(in) :: jloc
2285 : : ! *********************************************************************
2286 : :
2287 : 0 : jglob = loc_glob__(matrix, matrix%processor, jloc, 2)
2288 : :
2289 : 0 : end function basemat_loc2col
2290 : : !!***
2291 : :
2292 : : !----------------------------------------------------------------------
2293 : :
2294 : : !!****f* m_slk/loc_glob__
2295 : : !! NAME
2296 : : !! loc_glob__
2297 : : !!
2298 : : !! FUNCTION
2299 : : !! Determine the global index from a local index (row or column) as a function of a given processor
2300 : : !! Assumes that the first block is stored on the process with coordinate 0 (RSRC = CSRC = 0 as in basemat_init).
2301 : : !!
2302 : : !! INPUTS
2303 : : !! matrix= the matrix to process
2304 : : !! proc= descriptor of a processor
2305 : : !! idx= local row (column) index
2306 : : !! lico= block size index. 1 for rows. 2 for columns
2307 : : !!
2308 : : !! SOURCE
2309 : :
2310 : 0 : integer pure function loc_glob__(matrix, proc, idx, lico)
2311 : :
2312 : : !Arguments ------------------------------------
2313 : : class(basemat_t),intent(in) :: matrix
2314 : : class(slk_processor_t),intent(in) :: proc
2315 : : integer, intent(in) :: idx,lico
2316 : :
2317 : : !Local variables-------------------------------
2318 : : integer :: nbcyc, rest, nblocs
2319 : : ! *********************************************************************
2320 : :
2321 : 0 : nbcyc = INT((idx-1) / matrix%size_blocs(lico))
2322 : 0 : rest = MOD(idx-1, matrix%size_blocs(lico))
2323 : 0 : nblocs = nbcyc * proc%grid%dims(lico) + proc%coords(lico)
2324 : :
2325 : 0 : loc_glob__ = nblocs * matrix%size_blocs(lico) + rest + 1
2326 : :
2327 : 0 : end function loc_glob__
2328 : : !!***
2329 : :
2330 : : !----------------------------------------------------------------------
2331 : :
2332 : : !!****f* m_slk/slkmat_dp_from_global_pack
2333 : : !! NAME
2334 : : !! slkmat_dp_from_global_pack
2335 : : !!
2336 : : !! FUNCTION
2337 : : !! Routine to fill a SCALAPACK matrix from a global PACKED matrix.
2338 : : !! Only the upper triangle (iglob <= jglob) of the distributed matrix is meaningful:
2339 : : !! the local entries of the lower triangle are filled with unrelated values of the packed array.
2340 : : !! Use from_global_sym to get the full Hermitian matrix.
2341 : : !!
2342 : : !! INPUTS
2343 : : !! istwf_k= 2 if we have a real matrix else complex.
2344 : : !! glob_mat_pack= one-dimensional array with the upper triangle packed columnwise, replicated on all the processes.
2345 : : !! Complex case: real and imaginary parts of A(i,j) in glob_mat_pack(j*(j-1)+2*i-1:j*(j-1)+2*i), i <= j.
2346 : : !! Real case: A(i,j) in glob_mat_pack(j*(j-1)/2+i), i <= j.
2347 : : !!
2348 : : !! SIDE EFFECTS
2349 : : !! matrix= the matrix to process
2350 : : !!
2351 : : !! SOURCE
2352 : :
2353 : 0 : subroutine slkmat_dp_from_global_pack(matrix, glob_mat_pack, istwf_k)
2354 : :
2355 : : !Arguments ------------------------------------
2356 : : class(slkmat_dp_t),intent(inout) :: matrix
2357 : : integer,intent(in) :: istwf_k
2358 : : real(dp),intent(in) :: glob_mat_pack(*)
2359 : :
2360 : : !Local variables-------------------------------
2361 : : integer :: i,j,iglob,jglob,ind
2362 : : real(dp) :: val_real
2363 : : complex(dp) :: val_cplx
2364 : : ! *********************************************************************
2365 : :
2366 [ # # ]: 0 : do i=1,matrix%size_local(1)
2367 [ # # ]: 0 : do j=1,matrix%size_local(2)
2368 : 0 : call matrix%loc2glob(i, j, iglob, jglob)
2369 : :
2370 [ # # ]: 0 : if (istwf_k/=2) then
2371 : 0 : ind = jglob*(jglob-1)+2*iglob-1
2372 : 0 : val_cplx = dcmplx(glob_mat_pack(ind),glob_mat_pack(ind+1))
2373 : 0 : call matrix_set_local_cplx(matrix,i,j,val_cplx)
2374 : : else
2375 : 0 : ind = (jglob*(jglob-1))/2 + iglob
2376 : 0 : val_real = glob_mat_pack(ind)
2377 : 0 : call matrix_set_local_real(matrix,i,j,val_real)
2378 : : end if
2379 : :
2380 : : end do
2381 : : end do
2382 : :
2383 : 0 : end subroutine slkmat_dp_from_global_pack
2384 : : !!***
2385 : :
2386 : : !----------------------------------------------------------------------
2387 : :
2388 : : !!****f* m_slk/slkmat_dp_from_global_sym
2389 : : !! NAME
2390 : : !! slkmat_dp_from_global_sym
2391 : : !!
2392 : : !! FUNCTION
2393 : : !! Fill the full distributed matrix from the upper triangle of a Hermitian (real symmetric) matrix in packed form.
2394 : : !! The entries of the lower triangle are set to the complex conjugate of the upper triangle.
2395 : : !!
2396 : : !! INPUTS
2397 : : !! istwf_k= 2 if we have a real matrix else complex.
2398 : : !! glob_vec_pack= one-dimensional array with the upper triangle packed columnwise, replicated on all the processes.
2399 : : !! Same layout as in from_global_pack.
2400 : : !!
2401 : : !! SIDE EFFECTS
2402 : : !! matrix= the matrix to process
2403 : : !!
2404 : : !! SOURCE
2405 : :
2406 [ # # ]: 0 : subroutine slkmat_dp_from_global_sym(matrix, glob_vec_pack, istwf_k)
2407 : :
2408 : : !Arguments ------------------------------------
2409 : : class(slkmat_dp_t),intent(inout) :: matrix
2410 : : real(dp),intent(in) :: glob_vec_pack(:)
2411 : : integer,intent(in) :: istwf_k
2412 : :
2413 : : !Local variables-------------------------------
2414 : : integer :: i,j,iglob,jglob,ind
2415 : : complex(dp):: val_cplx
2416 : : real(dp) ::val_real
2417 : : ! *********************************************************************
2418 : :
2419 [ # # ]: 0 : do i=1,matrix%size_local(1)
2420 [ # # ]: 0 : do j=1,matrix%size_local(2)
2421 : 0 : call matrix%loc2glob(i,j,iglob,jglob)
2422 [ # # ]: 0 : if (jglob < iglob) then
2423 : 0 : ind = iglob*(iglob-1)+2*jglob-1
2424 : : else
2425 : 0 : ind = jglob*(jglob-1)+2*iglob-1
2426 : : end if
2427 [ # # ]: 0 : if (istwf_k /= 2) then
2428 : 0 : val_cplx = dcmplx(glob_vec_pack(ind),glob_vec_pack(ind+1))
2429 [ # # ]: 0 : if (jglob < iglob) then
2430 : 0 : call matrix_set_local_cplx(matrix,i,j,conjg(val_cplx))
2431 : : else
2432 : 0 : call matrix_set_local_cplx(matrix,i,j,val_cplx)
2433 : : end if
2434 : : else
2435 : 0 : ind = (ind + 1) / 2
2436 : 0 : val_real = glob_vec_pack(ind)
2437 : 0 : call matrix_set_local_real(matrix,i,j,val_real)
2438 : : end if
2439 : : end do
2440 : : end do
2441 : :
2442 : 0 : end subroutine slkmat_dp_from_global_sym
2443 : : !!***
2444 : :
2445 : : !----------------------------------------------------------------------
2446 : :
2447 : : !!****f* m_slk/slkmat_dp_from_real_glob
2448 : : !! NAME
2449 : : !! slkmat_dp_from_real_glob
2450 : : !!
2451 : : !! FUNCTION
2452 : : !! Routine to fill a SCALAPACK matrix from a real global matrix (FULL STORAGE MODE)
2453 : : !! The matrix must have been initialized with istwf_k = 2 (buffer_real).
2454 : : !!
2455 : : !! INPUTS
2456 : : !! istwf_k= Not used.
2457 : : !! glob_mat(n, m)= a real matrix, replicated on all the processes.
2458 : : !!
2459 : : !! SIDE EFFECTS
2460 : : !! matrix= the matrix to process
2461 : : !!
2462 : : !! SOURCE
2463 : :
2464 [ # # ]: 0 : subroutine slkmat_dp_from_real_glob(matrix, glob_mat, istwf_k)
2465 : :
2466 : : !Arguments ------------------------------------
2467 : : class(slkmat_dp_t),intent(inout) :: matrix
2468 : : integer,intent(in) :: istwf_k
2469 : : !arrays
2470 : : real(dp),intent(in) :: glob_mat(:,:)
2471 : :
2472 : : !Local variables-------------------------------
2473 : : integer :: i,j,iglob,jglob
2474 : : real(dp) :: val
2475 : : ! *********************************************************************
2476 : :
2477 : : ABI_UNUSED(istwf_k)
2478 : :
2479 [ # # ]: 0 : do i=1,matrix%size_local(1)
2480 [ # # ]: 0 : do j=1,matrix%size_local(2)
2481 : 0 : call matrix%loc2glob(i, j, iglob, jglob)
2482 : 0 : val = glob_mat(iglob, jglob)
2483 : 0 : call matrix_set_local_real(matrix,i,j,val)
2484 : : end do
2485 : : end do
2486 : :
2487 : 0 : end subroutine slkmat_dp_from_real_glob
2488 : : !!***
2489 : :
2490 : : !----------------------------------------------------------------------
2491 : :
2492 : : !!****f* m_slk/slkmat_dp_from_complex_glob
2493 : : !! NAME
2494 : : !! slkmat_dp_from_complex_glob
2495 : : !!
2496 : : !! FUNCTION
2497 : : !! Routine to fill a SCALAPACK matrix from a global matrix (FULL STORAGE MODE)
2498 : : !! The matrix must have been initialized with istwf_k /= 2 (buffer_cplx).
2499 : : !!
2500 : : !! INPUTS
2501 : : !! istwf_k= Not used.
2502 : : !! glob_mat(2*n, m)= a complex matrix stored as real/imag pairs, replicated on all the processes.
2503 : : !!
2504 : : !! SIDE EFFECTS
2505 : : !! matrix= the matrix to process
2506 : : !!
2507 : : !! SOURCE
2508 : :
2509 [ # # ]: 0 : subroutine slkmat_dp_from_complex_glob(matrix, glob_mat, istwf_k)
2510 : :
2511 : : !Arguments ------------------------------------
2512 : : class(slkmat_dp_t),intent(inout) :: matrix
2513 : : integer,intent(in) :: istwf_k
2514 : : !arrays
2515 : : real(dp),intent(in) :: glob_mat(:,:)
2516 : :
2517 : : !Local variables-------------------------------
2518 : : integer :: i,j,iglob,jglob
2519 : : complex(dpc) :: val
2520 : : ! *********************************************************************
2521 : :
2522 : : ABI_UNUSED(istwf_k)
2523 : :
2524 [ # # ]: 0 : do i=1,matrix%size_local(1)
2525 [ # # ]: 0 : do j=1,matrix%size_local(2)
2526 : 0 : call matrix%loc2glob(i, j, iglob, jglob)
2527 : 0 : val = dcmplx(glob_mat(2*iglob-1, jglob),glob_mat(2*iglob, jglob))
2528 : 0 : call matrix_set_local_cplx(matrix,i,j,val)
2529 : : end do
2530 : : end do
2531 : :
2532 : 0 : end subroutine slkmat_dp_from_complex_glob
2533 : : !!***
2534 : :
2535 : : !----------------------------------------------------------------------
2536 : :
2537 : : !!****f* m_slk/slkmat_dp_to_global_pack
2538 : : !! NAME
2539 : : !! slkmat_dp_to_global_pack
2540 : : !!
2541 : : !! FUNCTION
2542 : : !! Inserts the upper triangle of a ScaLAPACK matrix into a global one in PACKED storage mode (columnwise,
2543 : : !! same layout as in from_global_pack). Only the entries owned by this process are written,
2544 : : !! the lower triangle (iglob > jglob) is ignored.
2545 : : !!
2546 : : !! INPUTS
2547 : : !! matrix= the matrix to process
2548 : : !! istwf_k= 2 if we have a real matrix else complex.
2549 : : !!
2550 : : !! SIDE EFFECTS
2551 : : !! glob_pack(n*(n+1))= one-dimensional array (n*(n+1)/2 elements are used in the real case)
2552 : : !!
2553 : : !! SOURCE
2554 : :
2555 : 0 : subroutine slkmat_dp_to_global_pack(matrix, glob_pack, istwf_k)
2556 : :
2557 : : !Arguments ------------------------------------
2558 : : class(slkmat_dp_t),intent(in) :: matrix
2559 : : integer,intent(in) :: istwf_k !,nband_k
2560 : : real(dp),intent(inout) :: glob_pack(*) !(nband_k*(nband_k+1))
2561 : :
2562 : : !Local variables-------------------------------
2563 : : integer :: i,j,iglob,jglob,ind
2564 : : ! *********************************************************************
2565 : :
2566 [ # # ]: 0 : do i=1,matrix%size_local(1)
2567 [ # # ]: 0 : do j=1,matrix%size_local(2)
2568 : 0 : call matrix%loc2glob(i, j, iglob, jglob)
2569 [ # # ]: 0 : if (iglob > jglob) cycle ! Only the upper triangle is stored.
2570 : :
2571 : 0 : ind = jglob*(jglob-1)+2*iglob-1
2572 [ # # ]: 0 : if (istwf_k/=2) then
2573 : 0 : glob_pack(ind) = real(matrix_get_local_cplx(matrix,i,j))
2574 : 0 : glob_pack(ind+1) = aimag(matrix_get_local_cplx(matrix,i,j))
2575 : : else
2576 : 0 : ind=(ind+1)/2 !real packed storage
2577 : 0 : glob_pack(ind) = matrix_get_local_real(matrix,i,j)
2578 : : end if
2579 : : end do
2580 : : end do
2581 : :
2582 : 0 : end subroutine slkmat_dp_to_global_pack
2583 : : !!***
2584 : :
2585 : : !----------------------------------------------------------------------
2586 : :
2587 : : !!****f* m_slk/slkmat_dp_to_real_glob
2588 : : !! NAME
2589 : : !! slkmat_dp_to_real_glob
2590 : : !!
2591 : : !! FUNCTION
2592 : : !! Inserts a ScaLAPACK matrix into a real matrix in FULL STORAGE MODE.
2593 : : !! The matrix must have been initialized with istwf_k = 2 (buffer_real).
2594 : : !!
2595 : : !! INPUTS
2596 : : !! matrix= the matrix to process
2597 : : !! istwf_k= Not used.
2598 : : !!
2599 : : !! SIDE EFFECTS
2600 : : !! glob_mat(n, m)= the matrix to fill. Only the entries owned by this process are written.
2601 : : !!
2602 : : !! SOURCE
2603 : :
2604 [ # # ]: 0 : subroutine slkmat_dp_to_real_glob(matrix, glob_mat, istwf_k)
2605 : :
2606 : : !Arguments ------------------------------------
2607 : : class(slkmat_dp_t),intent(in) :: matrix
2608 : : integer,intent(in) :: istwf_k
2609 : : !arrays
2610 : : real(dp),intent(inout) :: glob_mat(:,:)
2611 : :
2612 : : !Local variables-------------------------------
2613 : : integer :: i,j,iglob,jglob
2614 : : ! *********************************************************************
2615 : :
2616 : : ABI_UNUSED(istwf_k)
2617 : :
2618 [ # # ]: 0 : do i=1,matrix%size_local(1)
2619 [ # # ]: 0 : do j=1,matrix%size_local(2)
2620 : 0 : call matrix%loc2glob(i, j, iglob, jglob)
2621 : 0 : glob_mat(iglob,jglob) = matrix_get_local_real(matrix,i,j)
2622 : : end do
2623 : : end do
2624 : :
2625 : 0 : end subroutine slkmat_dp_to_real_glob
2626 : : !!***
2627 : :
2628 : : !----------------------------------------------------------------------
2629 : :
2630 : : !!****f* m_slk/slkmat_dp_to_complex_glob
2631 : : !! NAME
2632 : : !! slkmat_dp_to_complex_glob
2633 : : !!
2634 : : !! FUNCTION
2635 : : !! Inserts a ScaLAPACK matrix into a complex matrix in FULL STORAGE MODE.
2636 : : !! The matrix must have been initialized with istwf_k /= 2 (buffer_cplx).
2637 : : !!
2638 : : !! INPUTS
2639 : : !! matrix= the matrix to process
2640 : : !! istwf_k= Not used.
2641 : : !!
2642 : : !! SIDE EFFECTS
2643 : : !! glob_cmat(n, m)= the matrix to fill. Only the entries owned by this process are written.
2644 : : !!
2645 : : !! SOURCE
2646 : :
2647 [ # # ]: 0 : subroutine slkmat_dp_to_complex_glob(matrix, glob_cmat, istwf_k)
2648 : :
2649 : : !Arguments ------------------------------------
2650 : : integer,intent(in) :: istwf_k
2651 : : class(slkmat_dp_t),intent(in) :: matrix
2652 : : !arrays
2653 : : complex(dpc),intent(inout) :: glob_cmat(:,:)
2654 : :
2655 : : !Local variables-------------------------------
2656 : : integer :: i,j,iglob,jglob
2657 : : ! *********************************************************************
2658 : :
2659 : : ABI_UNUSED(istwf_k)
2660 : :
2661 [ # # ]: 0 : do i=1,matrix%size_local(1)
2662 [ # # ]: 0 : do j=1,matrix%size_local(2)
2663 : 0 : call matrix%loc2glob(i, j, iglob, jglob)
2664 : 0 : glob_cmat(iglob,jglob) = matrix_get_local_cplx(matrix,i,j)
2665 : : end do
2666 : : end do
2667 : :
2668 : 0 : end subroutine slkmat_dp_to_complex_glob
2669 : : !!***
2670 : :
2671 : : !----------------------------------------------------------------------
2672 : :
2673 : : !!****f* m_slk/slkmat_dp_to_global
2674 : : !! NAME
2675 : : !! slkmat_dp_to_global
2676 : : !!
2677 : : !! FUNCTION
2678 : : !! Routine to fill a full matrix with respect to a SCALAPACK matrix.
2679 : : !!
2680 : : !! INPUTS
2681 : : !! matrix= the matrix to process
2682 : : !! istwf_k= 2 if we have a real matrix else complex.
2683 : : !!
2684 : : !! SIDE EFFECTS
2685 : : !! glob_mat= two-dimensional real(dp) array: (2*n, m) with real/imag pairs if istwf_k /= 2, (n, m) if istwf_k == 2.
2686 : : !! Only the entries owned by this process are written.
2687 : : !!
2688 : : !! SOURCE
2689 : :
2690 [ # # ]: 0 : subroutine slkmat_dp_to_global(matrix, glob_mat, istwf_k)
2691 : :
2692 : : !Arguments ------------------------------------
2693 : : class(slkmat_dp_t),intent(in) :: matrix
2694 : : integer,intent(in) :: istwf_k
2695 : : !arrays
2696 : : real(dp),intent(inout) :: glob_mat(:,:)
2697 : :
2698 : : !Local variables-------------------------------
2699 : : integer :: i,j,iglob,jglob,ind
2700 : : ! *********************************************************************
2701 : :
2702 [ # # ]: 0 : do i=1,matrix%size_local(1)
2703 [ # # ]: 0 : do j=1,matrix%size_local(2)
2704 : 0 : call matrix%loc2glob(i, j, iglob, jglob)
2705 : :
2706 [ # # ]: 0 : if (istwf_k/=2) then
2707 : 0 : ind=(iglob-1)*2+1
2708 : 0 : glob_mat(ind, jglob) = real(matrix_get_local_cplx(matrix,i,j))
2709 : 0 : glob_mat(ind+1,jglob) = aimag(matrix_get_local_cplx(matrix,i,j))
2710 : : else
2711 : 0 : ind=iglob
2712 : 0 : glob_mat(ind,jglob) = matrix_get_local_real(matrix,i,j)
2713 : : !glob_mat(ind+1,jglob) = 0._dp
2714 : : end if
2715 : :
2716 : : end do
2717 : : end do
2718 : :
2719 : 0 : end subroutine slkmat_dp_to_global
2720 : : !!***
2721 : :
2722 : : !----------------------------------------------------------------------
2723 : :
2724 : : !!****f* m_slk/slk_matrix_from_global_dpc_2D
2725 : : !! NAME
2726 : : !! slk_matrix_from_global_dpc_2D
2727 : : !!
2728 : : !! FUNCTION
2729 : : !! Routine to fill a complex SCALAPACK matrix with respect to a global matrix.
2730 : : !! target: Two-dimensional double precision complex matrix
2731 : : !!
2732 : : !! INPUTS
2733 : : !! glob_cmat=Two-dimensional array containing the global matrix.
2734 : : !! uplo=String specifying whether only the upper or lower triangular part of the global matrix is used:
2735 : : !! = "U": Upper triangular
2736 : : !! = "L": Lower triangular
2737 : : !! = "A": Full matrix (used for general complex matrices)
2738 : : !!
2739 : : !! SIDE EFFECTS
2740 : : !! mat<slkmat_dp_t>=The distributed matrix.
2741 : : !! %buffer_cplx=Local buffer containing the value this node is dealing with.
2742 : : !!
2743 : : !! SOURCE
2744 : :
2745 [ # # ]: 0 : subroutine slk_matrix_from_global_dpc_2D(mat, uplo, glob_cmat)
2746 : :
2747 : : !Arguments ------------------------------------
2748 : : !scalars
2749 : : class(slkmat_dp_t),intent(inout) :: mat
2750 : : character(len=*),intent(in) :: uplo
2751 : : !array
2752 : : complex(dpc),intent(in) :: glob_cmat(:,:)
2753 : :
2754 : : !Local variables-------------------------------
2755 : : integer :: ii, jj, iglob, jglob
2756 : : !************************************************************************
2757 : :
2758 [ # # ]: 0 : ABI_CHECK(allocated(mat%buffer_cplx), "%buffer_cplx not allocated")
2759 : :
2760 : 0 : select case (uplo(1:1))
2761 : :
2762 : : case ("A", "a")
2763 : : ! Full global matrix is used.
2764 [ # # ]: 0 : do jj=1,mat%size_local(2)
2765 [ # # ]: 0 : do ii=1,mat%size_local(1)
2766 : 0 : call mat%loc2glob(ii, jj, iglob, jglob)
2767 : 0 : mat%buffer_cplx(ii,jj) = glob_cmat(iglob,jglob)
2768 : : end do
2769 : : end do
2770 : :
2771 : : case ("U", "u")
2772 : : ! Only the upper triangle of the global matrix is used.
2773 [ # # ]: 0 : do jj=1,mat%size_local(2)
2774 [ # # ]: 0 : do ii=1,mat%size_local(1)
2775 : 0 : call mat%loc2glob(ii, jj, iglob, jglob)
2776 [ # # ]: 0 : if (jglob>=iglob) then
2777 : 0 : mat%buffer_cplx(ii,jj) = glob_cmat(iglob,jglob)
2778 : : else
2779 : 0 : mat%buffer_cplx(ii,jj) = DCONJG(glob_cmat(jglob,iglob))
2780 : : end if
2781 : : end do
2782 : : end do
2783 : :
2784 : : case ("L", "l")
2785 : : ! Only the lower triangle of the global matrix is used.
2786 [ # # ]: 0 : do jj=1,mat%size_local(2)
2787 [ # # ]: 0 : do ii=1,mat%size_local(1)
2788 : 0 : call mat%loc2glob(ii, jj, iglob, jglob)
2789 [ # # ]: 0 : if (jglob<=iglob) then
2790 : 0 : mat%buffer_cplx(ii,jj) = glob_cmat(iglob,jglob)
2791 : : else
2792 : 0 : mat%buffer_cplx(ii,jj) = DCONJG(glob_cmat(jglob,iglob))
2793 : : end if
2794 : : end do
2795 : : end do
2796 : :
2797 : : case default
2798 [ # # # # : 0 : ABI_BUG(" Wrong uplo: "//TRIM(uplo))
# # ]
2799 : : end select
2800 : :
2801 : 0 : end subroutine slk_matrix_from_global_dpc_2D
2802 : : !!***
2803 : :
2804 : : !----------------------------------------------------------------------
2805 : :
2806 : : !!****f* m_slk/slk_matrix_from_global_dpc_1Dp
2807 : : !! NAME
2808 : : !! slk_matrix_from_global_dpc_1Dp
2809 : : !!
2810 : : !! FUNCTION
2811 : : !! Routine to fill a complex SCALAPACK matrix with respect to a global matrix.
2812 : : !! target: double precision complex matrix in packed form.
2813 : : !!
2814 : : !! INPUTS
2815 : : !! glob_cmat_pack(n*(n+1)/2)=One-dimensional array containing the global matrix A packed columnwise in a linear array.
2816 : : !! The j-th column of A is stored in the array glob_cmat_pack as follows:
2817 : : !! if uplo = "U", glob_cmat_pack(i + (j-1)*j/2) = A(i,j) for 1<=i<=j;
2818 : : !! if uplo = "L", glob_cmat_pack(i + (j-1)*(2*n-j)/2) = A(i,j) for j<=i<=n.
2819 : : !! where n is the number of rows or columns in the global matrix.
2820 : : !! uplo=String specifying whether only the upper or lower triangular part of the global matrix is used:
2821 : : !! = "U": Upper triangular
2822 : : !! = "L": Lower triangular
2823 : : !!
2824 : : !! SIDE EFFECTS
2825 : : !! mat<slkmat_dp_t>=The distributed matrix.
2826 : : !! %buffer_cplx=Local buffer containing the value this node is dealing with.
2827 : : !!
2828 : : !! SOURCE
2829 : :
2830 [ # # ]: 0 : subroutine slk_matrix_from_global_dpc_1Dp(mat,uplo, glob_cmat_pack)
2831 : :
2832 : : !Arguments ------------------------------------
2833 : : !scalars
2834 : : class(slkmat_dp_t),intent(inout) :: mat
2835 : : character(len=*),intent(in) :: uplo
2836 : : !array
2837 : : complex(dpc),intent(in) :: glob_cmat_pack(:)
2838 : :
2839 : : !Local variables-------------------------------
2840 : : integer :: ii,jj,iglob,jglob,ind,n
2841 : : real(dp) :: szm
2842 : : !************************************************************************
2843 : :
2844 [ # # ]: 0 : ABI_CHECK(allocated(mat%buffer_cplx), "%buffer_cplx not allocated")
2845 : :
2846 : 0 : szm = SIZE(glob_cmat_pack)
2847 : 0 : n = NINT( (-1 + SQRT(one+8*szm) )*half )
2848 [ # # ]: 0 : if (n*(n+1)/2 /= SIZE(glob_cmat_pack)) then
2849 : 0 : ABI_ERROR("Buggy compiler")
2850 : : end if
2851 : :
2852 : 0 : select case (uplo(1:1))
2853 : :
2854 : : case ("U", "u")
2855 : : ! Only the upper triangle of the global matrix is used.
2856 [ # # ]: 0 : do jj=1,mat%size_local(2)
2857 [ # # ]: 0 : do ii=1,mat%size_local(1)
2858 : 0 : call mat%loc2glob(ii, jj, iglob, jglob)
2859 : :
2860 [ # # ]: 0 : if (jglob>=iglob) then
2861 : 0 : ind = iglob + jglob*(jglob-1)/2
2862 : 0 : mat%buffer_cplx(ii,jj) = glob_cmat_pack(ind)
2863 : : else
2864 : 0 : ind = jglob + iglob*(iglob-1)/2
2865 : 0 : mat%buffer_cplx(ii,jj) = DCONJG( glob_cmat_pack(ind) )
2866 : : end if
2867 : :
2868 : : end do
2869 : : end do
2870 : :
2871 : : case ("L", "l")
2872 : : ! Only the lower triangle of the global matrix is used.
2873 [ # # ]: 0 : do jj=1,mat%size_local(2)
2874 [ # # ]: 0 : do ii=1,mat%size_local(1)
2875 : 0 : call mat%loc2glob(ii, jj, iglob, jglob)
2876 : :
2877 [ # # ]: 0 : if (jglob<=iglob) then
2878 : 0 : ind = iglob + (jglob-1)*(2*n-jglob)/2
2879 : 0 : mat%buffer_cplx(ii,jj) = glob_cmat_pack(ind)
2880 : : else
2881 : 0 : ind = jglob + (iglob-1)*(2*n-iglob)/2
2882 : 0 : mat%buffer_cplx(ii,jj) = DCONJG( glob_cmat_pack(ind) )
2883 : : end if
2884 : : end do
2885 : : end do
2886 : :
2887 : : case default
2888 [ # # # # : 0 : ABI_BUG(" Wrong uplo: "//TRIM(uplo))
# ]
2889 : : end select
2890 : :
2891 : 0 : end subroutine slk_matrix_from_global_dpc_1Dp
2892 : : !!***
2893 : :
2894 : : !----------------------------------------------------------------------
2895 : :
2896 : : !!****f* m_slk/slk_matrix_to_global_dpc_2D
2897 : : !! NAME
2898 : : !! slk_matrix_to_global_dpc_2D
2899 : : !!
2900 : : !! FUNCTION
2901 : : !! Fill a global matrix with respect to a SCALAPACK matrix.
2902 : : !! target: Two-dimensional double precision complex matrix.
2903 : : !!
2904 : : !! INPUTS
2905 : : !! Slk_mat<slkmat_dp_t>=The distributed matrix.
2906 : : !! uplo=String specifying whether the upper or lower triangular part of the global matrix has to be filled:
2907 : : !! = "U": Upper triangular
2908 : : !! = "L": Lower triangular
2909 : : !! = "A": Full matrix is filled (used for general complex matrices)
2910 : : !!
2911 : : !! SIDE EFFECTS
2912 : : !! glob_cmat=The global matrix where the entries owned by this processors have been overwritten.
2913 : : !! Note that the remaining entries not treated by this node are not changed.
2914 : : !!
2915 : : !! SOURCE
2916 : :
2917 [ # # ]: 0 : subroutine slk_matrix_to_global_dpc_2D(mat, uplo, glob_cmat)
2918 : :
2919 : : !Arguments ------------------------------------
2920 : : !scalars
2921 : : class(slkmat_dp_t),intent(in) :: mat
2922 : : character(len=*),intent(in) :: uplo
2923 : : !arrays
2924 : : complex(dpc),intent(inout) :: glob_cmat(:,:)
2925 : :
2926 : : !Local variables-------------------------------
2927 : : integer :: ii,jj,iglob,jglob
2928 : : !************************************************************************
2929 : :
2930 : 0 : select case (uplo(1:1))
2931 : : case ("A", "a")
2932 : : ! Full global matrix has to be filled.
2933 [ # # ]: 0 : do jj=1,mat%size_local(2)
2934 [ # # ]: 0 : do ii=1,mat%size_local(1)
2935 : 0 : call mat%loc2glob(ii, jj, iglob, jglob)
2936 : 0 : glob_cmat(iglob,jglob) = mat%buffer_cplx(ii,jj)
2937 : : end do
2938 : : end do
2939 : :
2940 : : case ("U", "u")
2941 : : ! Only the upper triangle of the global matrix is filled.
2942 [ # # ]: 0 : do jj=1,mat%size_local(2)
2943 [ # # ]: 0 : do ii=1,mat%size_local(1)
2944 : 0 : call mat%loc2glob(ii, jj, iglob, jglob)
2945 [ # # ]: 0 : if (jglob>=iglob) glob_cmat(iglob,jglob) = mat%buffer_cplx(ii,jj)
2946 : : end do
2947 : : end do
2948 : :
2949 : : case ("L", "l")
2950 : : ! Only the lower triangle of the global matrix is filled.
2951 [ # # ]: 0 : do jj=1,mat%size_local(2)
2952 [ # # ]: 0 : do ii=1,mat%size_local(1)
2953 : 0 : call mat%loc2glob(ii, jj, iglob, jglob)
2954 [ # # ]: 0 : if (jglob<=iglob) glob_cmat(iglob,jglob) = mat%buffer_cplx(ii,jj)
2955 : : end do
2956 : : end do
2957 : :
2958 : : case default
2959 [ # # # # : 0 : ABI_BUG(" Wrong uplo: "//TRIM(uplo))
# # ]
2960 : : end select
2961 : :
2962 : 0 : end subroutine slk_matrix_to_global_dpc_2D
2963 : : !!***
2964 : :
2965 : : !----------------------------------------------------------------------
2966 : :
2967 : : !!****f* m_slk/my_locr
2968 : : !! NAME
2969 : : !! my_locr
2970 : : !!
2971 : : !! FUNCTION
2972 : : !! Method of slkmat_dp_t wrapping the scaLAPACK tool LOCr.
2973 : : !!
2974 : : !! OUTPUT
2975 : : !! my_locr= For the meaning see NOTES below.
2976 : : !!
2977 : : !! NOTES
2978 : : !! Let K be the number of rows or columns of a distributed matrix, and assume that its process grid has dimension p x q.
2979 : : !! LOCr( K ) denotes the number of elements of K that a process would receive if K were distributed over the p
2980 : : !! processes of its process column.
2981 : : !! Similarly, LOCc( K ) denotes the number of elements of K that a process would receive if K were distributed over
2982 : : !! the q processes of its process row.
2983 : : !! The values of LOCr() and LOCc() may be determined via a call to the ScaLAPACK tool function, NUMROC:
2984 : : !! LOCr( M ) = NUMROC( M, MB_A, MYROW, RSRC_A, NPROW ),
2985 : : !! LOCc( N ) = NUMROC( N, NB_A, MYCOL, CSRC_A, NPCOL ). An upper bound for these quantities may be computed
2986 : : !! by:
2987 : : !! LOCr( M ) <= ceil( ceil(M/MB_A)/NPROW )*MB_A
2988 : : !! LOCc( N ) <= ceil( ceil(N/NB_A)/NPCOL )*NB_A
2989 : : !!
2990 : : !! SOURCE
2991 : :
2992 : : integer function my_locr(mat)
2993 : :
2994 : : !Arguments ------------------------------------
2995 : : class(basemat_t),intent(in) :: mat
2996 : :
2997 : : !Local variables-------------------------------
2998 : : #ifdef HAVE_LINALG_SCALAPACK
2999 : : integer :: M, MB_A, MYROW, RSRC_A, NPROW
3000 : : ! *************************************************************************
3001 : :
3002 : : M = mat%desc(M_ ) ! The number of rows in the global matrix.
3003 : : MB_A = mat%desc(MB_) ! The number of rows in a block.
3004 : : MYROW = mat%processor%coords(1) ! The row index of my processor
3005 : : RSRC_A = mat%desc(RSRC_) ! The row of the processors at the beginning.
3006 : : NPROW = mat%processor%grid%dims(1) ! The number of processors per row in the Scalapack grid.
3007 : :
3008 : : my_locr = NUMROC( M, MB_A, MYROW, RSRC_A, NPROW )
3009 : : #endif
3010 : :
3011 : : end function my_locr
3012 : : !!***
3013 : :
3014 : : !----------------------------------------------------------------------
3015 : :
3016 : : !!****f* m_slk/my_locc
3017 : : !! NAME
3018 : : !! my_locc
3019 : : !!
3020 : : !! FUNCTION
3021 : : !! Method of slkmat_dp_t wrapping the scaLAPACK tool LOCc.
3022 : : !!
3023 : : !! OUTPUT
3024 : : !! my_locc= For the meaning see NOTES below.
3025 : : !!
3026 : : !! NOTES
3027 : : !! Let K be the number of rows or columns of a distributed matrix, and assume that its process grid has dimension p x q.
3028 : : !! LOCr( K ) denotes the number of elements of K that a process would receive if K were distributed over the p
3029 : : !! processes of its process column.
3030 : : !! Similarly, LOCc( K ) denotes the number of elements of K that a process would receive if K were distributed over
3031 : : !! the q processes of its process row.
3032 : : !! The values of LOCr() and LOCc() may be determined via a call to the ScaLAPACK tool function, NUMROC:
3033 : : !! LOCr( M ) = NUMROC( M, MB_A, MYROW, RSRC_A, NPROW ),
3034 : : !! LOCc( N ) = NUMROC( N, NB_A, MYCOL, CSRC_A, NPCOL ). An upper bound for these quantities may be computed
3035 : : !! by:
3036 : : !! LOCr( M ) <= ceil( ceil(M/MB_A)/NPROW )*MB_A
3037 : : !! LOCc( N ) <= ceil( ceil(N/NB_A)/NPCOL )*NB_A
3038 : : !!
3039 : : !! SOURCE
3040 : :
3041 : : integer function my_locc(mat)
3042 : :
3043 : : !Arguments ------------------------------------
3044 : : class(basemat_t),intent(in) :: mat
3045 : :
3046 : : #ifdef HAVE_LINALG_SCALAPACK
3047 : : !Local variables-------------------------------
3048 : : integer :: N, NB_A, MYCOL, CSRC_A, NPCOL
3049 : : ! *************************************************************************
3050 : :
3051 : : N = mat%desc(N_ ) ! The number of columns in the global matrix.
3052 : : NB_A = mat%desc(NB_) ! The number of columns in a block.
3053 : : MYCOL = mat%processor%coords(2) ! The column index of my processor
3054 : : CSRC_A = mat%desc(CSRC_) ! The column of the processors at the beginning.
3055 : : NPCOL = mat%processor%grid%dims(2) ! The number of processors per column in the Scalapack grid.
3056 : :
3057 : : my_locc = NUMROC( N, NB_A, MYCOL, CSRC_A, NPCOL )
3058 : : #endif
3059 : :
3060 : : end function my_locc
3061 : : !!***
3062 : :
3063 : : !----------------------------------------------------------------------
3064 : :
3065 : : !!****f* m_slk/slk_pgemm_dp
3066 : : !! NAME
3067 : : !! slk_pgemm_dp
3068 : : !!
3069 : : !! FUNCTION
3070 : : !! Extended matrix * matrix product: C := alpha*op(A)*op(B) + beta*C
3071 : : !! For a simple matrix matrix product, one can simply pass alpha = cone and beta = czero
3072 : : !! The global sizes m, n, k of the product are taken from the global shapes of A and B
3073 : : !! so the (sub)matrices defined by ija, ijb, ijc must have these sizes.
3074 : : !!
3075 : : !! INPUTS
3076 : : !! transa, transb= "N" for op(X) = X, "T" for op(X) = X^T, "C" for op(X) = X^H.
3077 : : !! matrix1= first ScaLAPACK matrix (matrix A)
3078 : : !! matrix2= second ScaLAPACK matrix (matrix B)
3079 : : !! alpha= scalar multiplier for the A*B product
3080 : : !! beta= scalar multiplier for the C matrix
3081 : : !! [ija, ijb, ijc]: (global). The row and column indices in the distributed matrix A/B/C
3082 : : !! indicating the first row and the first column of the submatrix, respectively. Default [1, 1]
3083 : : !!
3084 : : !! OUTPUT
3085 : : !! results= ScaLAPACK matrix coming out of the operation
3086 : : !!
3087 : : !! NOTES
3088 : : !! The ESSL manual says that
3089 : : !! "matrices matrix1 and matrix2 must have no common elements otherwise, results are unpredictable."
3090 : : !! However the official scaLAPACK documentation does not report this (severe) limitation.
3091 : : !!
3092 : : !! The real case (istwf_k == 2) passes buffer_cplx instead of buffer_real to P?GEMM and does not work.
3093 : : !! Only complex matrices are used by the callers.
3094 : : !!
3095 : : !! SOURCE
3096 : :
3097 : 0 : subroutine slk_pgemm_dp(transa, transb, matrix1, alpha, matrix2, beta, results, &
3098 : : ija, ijb, ijc) ! optional
3099 : :
3100 : : !Arguments ------------------------------------
3101 : : character(len=1),intent(in) :: transa, transb
3102 : : class(slkmat_dp_t),intent(in) :: matrix1, matrix2
3103 : : class(slkmat_dp_t),intent(inout) :: results
3104 : : complex(dpc),intent(in) :: alpha, beta
3105 : : integer,optional,intent(in) :: ija(2), ijb(2), ijc(2)
3106 : :
3107 : : !Local variables-------------------------------
3108 : : integer :: mm, nn, kk, ija__(2), ijb__(2), ijc__(2)
3109 : : !************************************************************************
3110 : :
3111 : 0 : ija__ = [1, 1]; if (present(ija)) ija__ = ija
3112 : 0 : ijb__ = [1, 1]; if (present(ijb)) ijb__ = ijb
3113 : 0 : ijc__ = [1, 1]; if (present(ijc)) ijc__ = ijc
3114 : :
3115 : 0 : mm = matrix1%size_global(1)
3116 : 0 : nn = matrix2%size_global(2)
3117 : 0 : kk = matrix1%size_global(2)
3118 : :
3119 : 0 : if (toupper(transa) /= 'N') then
3120 : : mm = matrix1%size_global(2)
3121 : 0 : kk = matrix1%size_global(1)
3122 : : end if
3123 : 0 : if (toupper(transb) /= 'N') nn = matrix2%size_global(1)
3124 : :
3125 : : #ifdef HAVE_LINALG_SCALAPACK
3126 : : ! pzgemm(transa, transb, m, n, k, alpha, a, ia, ja, desca, b, ib, jb, descb, beta, c, ic, jc, descc)
3127 : : if (matrix1%istwf_k /= 2) then
3128 : : call PZGEMM(transa, transb, mm, nn, kk, alpha, &
3129 : : matrix1%buffer_cplx, ija__(1), ija__(2), matrix1%desc, &
3130 : : matrix2%buffer_cplx, ijb__(1), ijb__(2), matrix2%desc, &
3131 : : beta, results%buffer_cplx, ijc__(1), ijc__(2), results%desc)
3132 : : else
3133 : : ! FIXME: should use buffer_real for real matrices.
3134 : : call PDGEMM(transa, transb, mm, nn, kk, real(alpha, kind=dp), &
3135 : : matrix1%buffer_cplx, ija__(1), ija__(2), matrix1%desc, &
3136 : : matrix2%buffer_cplx, ijb__(1), ijb__(2), matrix2%desc, &
3137 : : real(beta, kind=dp), results%buffer_cplx, ijc__(1), ijc__(2), results%desc)
3138 : : end if
3139 : : #endif
3140 : :
3141 : 0 : end subroutine slk_pgemm_dp
3142 : : !!***
3143 : :
3144 : : !----------------------------------------------------------------------
3145 : :
3146 : : !!****f* m_slk/slk_pgemm_sp
3147 : : !! NAME
3148 : : !! slk_pgemm_sp
3149 : : !!
3150 : : !! FUNCTION
3151 : : !! Extended matrix * matrix product: C := alpha*op(A)*op(B) + beta*C
3152 : : !! For a simple matrix matrix product, one can simply pass alpha = cone and beta = czero
3153 : : !! The global sizes m, n, k of the product are taken from the global shapes of A and B
3154 : : !! so the (sub)matrices defined by ija, ijb, ijc must have these sizes.
3155 : : !!
3156 : : !! INPUTS
3157 : : !! transa, transb= "N" for op(X) = X, "T" for op(X) = X^T, "C" for op(X) = X^H.
3158 : : !! matrix1= first ScaLAPACK matrix (matrix A)
3159 : : !! matrix2= second ScaLAPACK matrix (matrix B)
3160 : : !! alpha= scalar multiplier for the A*B product
3161 : : !! beta= scalar multiplier for the C matrix
3162 : : !! [ija, ijb, ijc]: (global). The row and column indices in the distributed matrix A/B/C
3163 : : !! indicating the first row and the first column of the submatrix, respectively. Default [1, 1]
3164 : : !!
3165 : : !! OUTPUT
3166 : : !! results= ScaLAPACK matrix coming out of the operation
3167 : : !!
3168 : : !! NOTES
3169 : : !! The ESSL manual says that
3170 : : !! "matrices matrix1 and matrix2 must have no common elements otherwise, results are unpredictable."
3171 : : !! However the official scaLAPACK documentation does not report this (severe) limitation.
3172 : : !!
3173 : : !! The real case (istwf_k == 2) passes buffer_cplx instead of buffer_real to P?GEMM and does not work.
3174 : : !! Only complex matrices are used by the callers.
3175 : : !!
3176 : : !! SOURCE
3177 : :
3178 : 0 : subroutine slk_pgemm_sp(transa, transb, matrix1, alpha, matrix2, beta, results, &
3179 : : ija, ijb, ijc) ! optional
3180 : :
3181 : : !Arguments ------------------------------------
3182 : : character(len=1),intent(in) :: transa, transb
3183 : : class(slkmat_sp_t),intent(in) :: matrix1, matrix2
3184 : : class(slkmat_sp_t),intent(inout) :: results
3185 : : complex(sp),intent(in) :: alpha, beta
3186 : : integer,optional,intent(in) :: ija(2), ijb(2), ijc(2)
3187 : :
3188 : : !Local variables-------------------------------
3189 : : integer :: mm, nn, kk, ija__(2), ijb__(2), ijc__(2)
3190 : : !************************************************************************
3191 : :
3192 : 0 : ija__ = [1, 1]; if (present(ija)) ija__ = ija
3193 : 0 : ijb__ = [1, 1]; if (present(ijb)) ijb__ = ijb
3194 : 0 : ijc__ = [1, 1]; if (present(ijc)) ijc__ = ijc
3195 : :
3196 : 0 : mm = matrix1%size_global(1)
3197 : 0 : nn = matrix2%size_global(2)
3198 : 0 : kk = matrix1%size_global(2)
3199 : :
3200 : 0 : if (toupper(transa) /= 'N') then
3201 : : mm = matrix1%size_global(2)
3202 : 0 : kk = matrix1%size_global(1)
3203 : : end if
3204 : 0 : if (toupper(transb) /= 'N') nn = matrix2%size_global(1)
3205 : :
3206 : : #ifdef HAVE_LINALG_SCALAPACK
3207 : : ! pzgemm(transa, transb, m, n, k, alpha, a, ia, ja, desca, b, ib, jb, descb, beta, c, ic, jc, descc)
3208 : : if (matrix1%istwf_k /= 2) then
3209 : : call PCGEMM(transa, transb, mm, nn, kk, alpha, &
3210 : : matrix1%buffer_cplx, ija__(1), ija__(2), matrix1%desc, &
3211 : : matrix2%buffer_cplx, ijb__(1), ijb__(2), matrix2%desc, &
3212 : : beta, results%buffer_cplx, ijc__(1), ijc__(2), results%desc)
3213 : : else
3214 : : ! FIXME: should use buffer_real for real matrices.
3215 : : call PSGEMM(transa, transb, mm, nn, kk, real(alpha, kind=sp), &
3216 : : matrix1%buffer_cplx, ija__(1), ija__(2), matrix1%desc, &
3217 : : matrix2%buffer_cplx, ijb__(1), ijb__(2), matrix2%desc, &
3218 : : real(beta, kind=sp), results%buffer_cplx, ijc__(1), ijc__(2), results%desc)
3219 : : end if
3220 : : #endif
3221 : :
3222 : 0 : end subroutine slk_pgemm_sp
3223 : : !!***
3224 : :
3225 : : !----------------------------------------------------------------------
3226 : :
3227 : : !!****f* m_slk/compute_eigen_problem
3228 : : !! NAME
3229 : : !! compute_eigen_problem
3230 : : !!
3231 : : !! FUNCTION
3232 : : !! Calculation of eigenvalues and eigenvectors: A * X = lambda * X, complex and real cases.
3233 : : !! Backend: ELPA (1-stage solver) if available, else SLATE, else ScaLAPACK (PZHEEVX or PDSYEVX).
3234 : : !! ScaLAPACK uses only the upper triangle of A while ELPA and SLATE need the full matrix.
3235 : : !!
3236 : : !! INPUTS
3237 : : !! processor= descriptor of a processor
3238 : : !! matrix= the matrix to process. Destroyed on exit.
3239 : : !! comm= MPI communicator of the processes in the grid
3240 : : !! istwf_k= 2 if we have a real matrix else complex.
3241 : : !! [nev]= Number of eigenvalues needed. Default: full set
3242 : : !! [use_gpu_elpa]= Flag to activate the use of GPU (ELPA only)
3243 : : !!
3244 : : !! OUTPUT
3245 : : !! results= ScaLAPACK matrix coming out of the operation (global dimensions must be equal to matrix
3246 : : !! even if only a part of the eigenvectors is needed.
3247 : : !! eigen= eigenvalues of the matrix dimensioned as the global size of the square matrix
3248 : : !! even if only a part of the eigenvalues is needed.
3249 : : !!
3250 : : !! SOURCE
3251 : :
3252 : 0 : subroutine compute_eigen_problem(processor, matrix, results, eigen, comm, istwf_k, &
3253 : : nev, use_gpu_elpa) ! Optional arguments
3254 : :
3255 : : !Arguments ------------------------------------
3256 : : class(slk_processor_t),intent(in) :: processor
3257 : : class(slkmat_dp_t),intent(inout) :: matrix
3258 : : class(slkmat_dp_t),intent(inout) :: results
3259 : : DOUBLE PRECISION,intent(inout) :: eigen(:)
3260 : : integer,intent(in) :: comm,istwf_k
3261 : : integer,optional,intent(in) :: nev
3262 : : integer,optional,intent(in) :: use_gpu_elpa
3263 : :
3264 : : #if defined(HAVE_LINALG_ELPA)
3265 : : !Local variables ------------------------------
3266 : : type(elpa_hdl_t) :: elpa_hdl
3267 : : integer :: nev__,use_gpu_elpa_
3268 : : !************************************************************************
3269 : :
3270 : : nev__ = matrix%size_global(1); if (present(nev)) nev__ = nev
3271 : : use_gpu_elpa_=0; if (present(use_gpu_elpa)) use_gpu_elpa_=use_gpu_elpa
3272 : :
3273 : : call elpa_func_allocate(elpa_hdl,gpu=use_gpu_elpa_)
3274 : : call elpa_func_set_matrix(elpa_hdl,matrix%size_global(1),matrix%size_blocs(1),nev__,&
3275 : : matrix%size_local(1),matrix%size_local(2))
3276 : : call elpa_func_get_communicators(elpa_hdl,processor%comm,processor%coords(1),processor%coords(2))
3277 : :
3278 : : if (istwf_k/=2) then
3279 : : call elpa_func_solve_evp_1stage(elpa_hdl,matrix%buffer_cplx,results%buffer_cplx,eigen,nev__)
3280 : : else
3281 : : call elpa_func_solve_evp_1stage(elpa_hdl,matrix%buffer_real,results%buffer_real,eigen,nev__)
3282 : : end if
3283 : :
3284 : : call elpa_func_deallocate(elpa_hdl)
3285 : :
3286 : :
3287 : : #elif defined(HAVE_LINALG_SLATE)
3288 : : !Local variables-------------------------------
3289 : : integer :: nev__,use_gpu_elpa_, info_slate
3290 : : ! *************************************************************************
3291 : :
3292 : : if (istwf_k /= 2) then
3293 : : call slate_zheev_c( &
3294 : : matrix%size_global(1), & ! n
3295 : : matrix%size_blocs(1), & ! nb (mb==nb required)
3296 : : processor%grid%dims(1), & ! nprow
3297 : : processor%grid%dims(2), & ! npcol
3298 : : processor%comm, & ! Fortran MPI comm handle
3299 : : matrix%size_local(1), & ! lda
3300 : : matrix%buffer_cplx, & ! a_data (destroyed on exit)
3301 : : results%size_local(1), & ! ldz
3302 : : results%buffer_cplx, & ! z_data (eigenvectors out)
3303 : : eigen, & ! w (eigenvalues out)
3304 : : nev__, & ! nev
3305 : : use_gpu_elpa_, & ! use_gpu (reuse existing flag)
3306 : : info_slate)
3307 : : ABI_CHECK(info_slate == 0, "slate_zheev_c failed")
3308 : : else
3309 : : call slate_dsyev_c( &
3310 : : matrix%size_global(1), & ! n
3311 : : matrix%size_blocs(1), & ! nb (mb==nb required)
3312 : : processor%grid%dims(1), & ! nprow
3313 : : processor%grid%dims(2), & ! npcol
3314 : : processor%comm, & ! Fortran MPI comm handle
3315 : : matrix%size_local(1), & ! lda
3316 : : matrix%buffer_real, & ! a_data (destroyed on exit)
3317 : : results%size_local(1), & ! ldz
3318 : : results%buffer_real, & ! z_data (eigenvectors out)
3319 : : eigen, & ! w (eigenvalues out)
3320 : : nev__, & ! nev
3321 : : use_gpu_elpa_, & ! use_gpu (reuse existing flag)
3322 : : info_slate)
3323 : : ABI_CHECK(info_slate == 0, "slate_dsyev_c failed")
3324 : : end if
3325 : :
3326 : :
3327 : : #elif defined(HAVE_LINALG_SCALAPACK)
3328 : : !Local variables-------------------------------
3329 : : integer :: LRWORK,LIWORK,LCWORK,INFO
3330 : : !character(len=500) :: msg
3331 : : integer , dimension(1) :: IWORK_tmp
3332 : : DOUBLE PRECISION, dimension(1) :: RWORK_tmp
3333 : : complex(dpc) , dimension(1) :: CWORK_tmp
3334 : : integer , allocatable :: IWORK(:)
3335 : : DOUBLE PRECISION, allocatable :: RWORK(:)
3336 : : complex(dpc) , allocatable :: CWORK(:)
3337 : : integer, allocatable :: ICLUSTR(:)
3338 : : integer, allocatable :: IFAIL(:)
3339 : : DOUBLE PRECISION, allocatable :: GAP(:)
3340 : : DOUBLE PRECISION :: ABSTOL,ORFAC
3341 : : integer, parameter :: IZERO=0
3342 : : integer :: M,NZ,ierr,TWORK_tmp(3),TWORK(3) ! IA,JA,IZ,JZ,
3343 : : integer :: nev__, il, iu, use_gpu_elpa_
3344 : : character(len=1) :: range
3345 : : ! *************************************************************************
3346 : :
3347 : : ABI_UNUSED(use_gpu_elpa) ! No GPU implementation if using scaLAPACK
3348 : : use_gpu_elpa_=0; if (present(use_gpu_elpa)) use_gpu_elpa_=use_gpu_elpa
3349 : : nev__ = matrix%size_global(1); range = "A"; il = 0; iu = 0
3350 : : if (present(nev)) then
3351 : : nev__ = nev; range = "I"; il = 1; iu = nev
3352 : : end if
3353 : :
3354 : : ! Initialisation
3355 : : INFO = 0
3356 : : ABSTOL = zero
3357 : : ORFAC = -1.D+0
3358 : :
3359 : : ! Allocation of the variables for the results of the calculations
3360 : : ABI_MALLOC(IFAIL,(matrix%size_global(2)))
3361 : : ABI_MALLOC(ICLUSTR,(2*processor%grid%dims(1)*processor%grid%dims(2)))
3362 : : ABI_MALLOC(GAP,(processor%grid%dims(1)*processor%grid%dims(2)))
3363 : :
3364 : : CWORK_tmp = (0.0_DP,0.0_DP)
3365 : : RWORK_tmp = 0.0_DP
3366 : : ! Get the size of the work arrays
3367 : : if (istwf_k/=2) then
3368 : : call PZHEEVX('V', range, 'U',&
3369 : : matrix%size_global(2),&
3370 : : matrix%buffer_cplx,1,1,matrix%desc, &
3371 : : ZERO,ZERO,il,iu,ABSTOL,&
3372 : : m,nz,eigen,ORFAC, &
3373 : : results%buffer_cplx,1,1,results%desc, &
3374 : : CWORK_tmp,-1,RWORK_tmp,-1,IWORK_tmp,-1,&
3375 : : IFAIL,ICLUSTR,GAP,INFO)
3376 : : else
3377 : : call PDSYEVX('V', range, 'U',&
3378 : : matrix%size_global(2),&
3379 : : matrix%buffer_real,1,1,matrix%desc, &
3380 : : ZERO,ZERO,il,iu,ABSTOL,&
3381 : : m,nz,eigen,ORFAC, &
3382 : : results%buffer_real,1,1,results%desc, &
3383 : : RWORK_tmp,-1,IWORK_tmp,-1,&
3384 : : IFAIL,ICLUSTR,GAP,INFO)
3385 : : end if
3386 : :
3387 : : if (INFO/=0) then
3388 : : ABI_ERROR(sjoin("Problem to compute workspace to use ScaLAPACK, INFO: ", itoa(INFO)))
3389 : : endif
3390 : : !write(std_out, *)"First call to compute workspace OK"
3391 : :
3392 : : TWORK_tmp(1) = IWORK_tmp(1)
3393 : : TWORK_tmp(2) = INT(RWORK_tmp(1))
3394 : : TWORK_tmp(3) = INT(real(CWORK_tmp(1)))
3395 : :
3396 : : ! Get the maximum of the size of the work arrays processor%comm
3397 : : call MPI_ALLREDUCE(TWORK_tmp,TWORK,3,MPI_integer,MPI_MAX,comm,ierr)
3398 : :
3399 : : LIWORK = TWORK(1)
3400 : : LRWORK = TWORK(2) + matrix%size_global(2) *(matrix%size_global(2)-1)
3401 : : LCWORK = TWORK(3)
3402 : :
3403 : : ! Allocation of the work arrays
3404 : : if (LIWORK>0) then
3405 : : ABI_MALLOC(IWORK,(LIWORK))
3406 : : IWORK(:) = 0
3407 : : else
3408 : : ABI_MALLOC(IWORK,(1))
3409 : : end if
3410 : : if (LRWORK>0) then
3411 : : ! This workspace scales with the number of processors but not always in a simple linear way.
3412 : : ! and the optimal LRWORK might be pretty big (~1 Gb) even for small systems.
3413 : : ABI_MALLOC(RWORK,(LRWORK))
3414 : : RWORK(:) = 0._dp
3415 : : else
3416 : : ABI_MALLOC(RWORK,(1))
3417 : : end if
3418 : : if (LCWORK>0) then
3419 : : ABI_MALLOC(CWORK,(LCWORK))
3420 : : CWORK(:) = (0._dp,0._dp)
3421 : : else
3422 : : ABI_MALLOC(CWORK,(1))
3423 : : end if
3424 : :
3425 : : ! prototype
3426 : : !call pzheevx(jobz, range, uplo, n, a, ia, ja, desca, vl, vu, il, iu, abstol, m, nz, w,
3427 : : ! orfac, z, iz, jz, descz, work, lwork, rwork, lrwork, iwork, liwork, ifail, iclustr, gap, info)
3428 : :
3429 : : ! Call the calculation routine
3430 : : if (istwf_k/=2) then
3431 : : ! write(std_out,*) 'I am using PZHEEVX'
3432 : : call PZHEEVX('V', range, 'U',&
3433 : : matrix%size_global(2),&
3434 : : matrix%buffer_cplx,1,1,matrix%desc, &
3435 : : ZERO,ZERO,il,iu,ABSTOL,&
3436 : : m,nz,eigen,ORFAC, &
3437 : : results%buffer_cplx,1,1,results%desc, &
3438 : : CWORK,LCWORK,RWORK,LRWORK,IWORK,LIWORK,&
3439 : : IFAIL,ICLUSTR,GAP,INFO)
3440 : : else
3441 : : ! write(std_out,*) ' I am using PDSYEVX'
3442 : : call PDSYEVX('V', range, 'U',&
3443 : : matrix%size_global(2),&
3444 : : matrix%buffer_real,1,1,matrix%desc, &
3445 : : ZERO,ZERO,il,iu,ABSTOL,&
3446 : : m,nz,eigen,ORFAC, &
3447 : : results%buffer_real,1,1,results%desc, &
3448 : : RWORK,LRWORK,IWORK,LIWORK,&
3449 : : IFAIL,ICLUSTR,GAP,INFO)
3450 : : endif
3451 : :
3452 : : ! MG: TODO: Recheck the computation of the workspace as I got INFO 2 with a 5x5x5 si supercell.
3453 : : if (INFO/=0) then
3454 : : ABI_ERROR(sjoin("Problem to compute eigenvalues and eigenvectors with ScaLAPACK, INFO:", itoa(INFO)))
3455 : : endif
3456 : :
3457 : : ABI_FREE(IFAIl)
3458 : : ABI_FREE(ICLUSTR)
3459 : : ABI_FREE(GAP)
3460 : : ABI_SFREE(IWORK)
3461 : : ABI_SFREE(RWORK)
3462 : : ABI_SFREE(CWORK)
3463 : : #endif
3464 : 0 : return
3465 : :
3466 : : end subroutine compute_eigen_problem
3467 : : !!***
3468 : :
3469 : : !----------------------------------------------------------------------
3470 : :
3471 : : !!****f* m_slk/solve_gevp_complex
3472 : : !! NAME
3473 : : !! solve_gevp_complex
3474 : : !!
3475 : : !! FUNCTION
3476 : : !! Calculation of eigenvalues and eigenvectors: A * X = lambda * B * X
3477 : : !! complex and real cases.
3478 : : !!
3479 : : !! INPUTS
3480 : : !! processor= descriptor of a processor
3481 : : !! matrix1= first ScaLAPACK matrix (matrix A)
3482 : : !! matrix2= second ScaLAPACK matrix (matrix B)
3483 : : !! comm= MPI communicator
3484 : : !! istwf_k= 2 if we have a real matrix else complex.
3485 : : !! [use_gpu_elpa]= Flag to activate the use of GPU (ELPA only)
3486 : : !!
3487 : : !! SIDE EFFECTS
3488 : : !! results= ScaLAPACK matrix coming out of the operation
3489 : : !! eigen= eigenvalues of the matrix
3490 : : !!
3491 : : !! SOURCE
3492 : :
3493 : : #ifdef HAVE_LINALG_ELPA
3494 : :
3495 : : subroutine solve_gevp_complex(na,nev,na_rows,na_cols,nblk,a,b,ev,z,tmp1,tmp2, &
3496 : : my_prow,my_pcol,np_rows,np_cols,sc_desc,comm,&
3497 : : use_gpu_elpa) ! Optional parameter
3498 : :
3499 : : !-Arguments
3500 : : integer,intent(in) :: na
3501 : : integer,intent(in) :: nev
3502 : : integer,intent(in) :: na_rows,na_cols
3503 : : integer,intent(in) :: nblk
3504 : : integer,intent(in) :: my_pcol,my_prow
3505 : : integer,intent(in) :: np_cols,np_rows
3506 : : integer,intent(in) :: sc_desc(9)
3507 : : integer,intent(in) :: comm
3508 : : integer,optional,intent(in) :: use_gpu_elpa
3509 : : real*8 :: ev(na)
3510 : : complex*16 :: a(na_rows,na_cols),b(na_rows,na_cols),z(na_rows,na_cols)
3511 : : complex*16 :: tmp1(na_rows,na_cols),tmp2(na_rows,na_cols)
3512 : : !-Local variables
3513 : : integer :: i, n_col, n_row, use_gpu_elpa_
3514 : : complex*16, parameter :: CZERO = (0.d0,0.d0), CONE = (1.d0,0.d0)
3515 : : type(elpa_hdl_t) :: elpa_hdl
3516 : : ! *************************************************************************
3517 : :
3518 : : use_gpu_elpa_=0
3519 : : #ifdef HAVE_LINALG_ELPA
3520 : : if (present(use_gpu_elpa)) use_gpu_elpa_=use_gpu_elpa
3521 : : #endif
3522 : :
3523 : : ! Allocate ELPA handle
3524 : : call elpa_func_allocate(elpa_hdl,blacs_ctx=sc_desc(CTXT_),gpu=use_gpu_elpa_)
3525 : : call elpa_func_set_matrix(elpa_hdl,na,nblk,nev,na_rows,na_cols)
3526 : : call elpa_func_get_communicators(elpa_hdl,comm,my_prow,my_pcol)
3527 : : call elpa_func_solve_gevp_2stage(elpa_hdl,a,b,z,ev,nev)
3528 : : call elpa_func_deallocate(elpa_hdl)
3529 : :
3530 : : end subroutine solve_gevp_complex
3531 : :
3532 : : !----------------------------------------------------------------------
3533 : :
3534 : : subroutine solve_gevp_real(na,nev,na_rows,na_cols,nblk,a,b,ev,z,tmp1,tmp2, &
3535 : : my_prow,my_pcol,np_rows,np_cols,sc_desc,comm, &
3536 : : use_gpu_elpa) ! Optional argument
3537 : :
3538 : : !-Arguments
3539 : : integer,intent(in) :: na
3540 : : integer,intent(in) :: nev
3541 : : integer,intent(in) :: na_rows,na_cols
3542 : : integer,intent(in) :: nblk
3543 : : integer,intent(in) :: my_pcol,my_prow
3544 : : integer,intent(in) :: np_cols,np_rows
3545 : : integer,intent(in) :: sc_desc(9)
3546 : : integer,intent(in) :: comm
3547 : : integer,optional,intent(in) :: use_gpu_elpa
3548 : : real*8 :: ev(na)
3549 : : real*8 :: a(na_rows,na_cols),b(na_rows,na_cols),z(na_rows,na_cols)
3550 : : real*8 :: tmp1(na_rows,na_cols),tmp2(na_rows,na_cols)
3551 : : !-Local variables
3552 : : integer :: i, n_col, n_row, use_gpu_elpa_
3553 : : type(elpa_hdl_t) :: elpa_hdl
3554 : : ! *************************************************************************
3555 : :
3556 : : use_gpu_elpa_=0
3557 : : #ifdef HAVE_LINALG_ELPA
3558 : : if (present(use_gpu_elpa)) use_gpu_elpa_=use_gpu_elpa
3559 : : #endif
3560 : :
3561 : : ! Allocate ELPA handle
3562 : : call elpa_func_allocate(elpa_hdl,blacs_ctx=sc_desc(CTXT_),gpu=use_gpu_elpa_)
3563 : : call elpa_func_set_matrix(elpa_hdl,na,nblk,nev,na_rows,na_cols)
3564 : : call elpa_func_get_communicators(elpa_hdl,comm,my_prow,my_pcol)
3565 : :
3566 : : !FIXME Need to figure out why generalized_eigenvectors doesn't work in this real case
3567 : : ! while it is fine with complex case
3568 : : if(.false.) then
3569 : : call elpa_func_solve_gevp_2stage(elpa_hdl,a,b,tmp1,ev,nev)
3570 : : else
3571 : : ! 1. Calculate Cholesky factorization of Matrix B = U**T * U
3572 : : ! and invert triangular matrix U
3573 : : call elpa_func_cholesky(elpa_hdl,b)
3574 : : call elpa_func_invert_triangular(elpa_hdl,b)
3575 : : ! 2. Calculate U**-T * A * U**-1
3576 : : ! 2a. tmp1 = U**-T * A
3577 : : call elpa_func_hermitian_multiply(elpa_hdl,'U','L',na,b,a,na_rows,na_cols,tmp1,na_rows,na_cols)
3578 : : ! 2b. tmp2 = tmp1**T
3579 : : call pdtran(na,na,1.d0,tmp1,1,1,sc_desc,0.d0,tmp2,1,1,sc_desc)
3580 : : ! 2c. A = U**-T * tmp2 ( = U**-T * Aorig * U**-1 )
3581 : : call elpa_func_hermitian_multiply(elpa_hdl,'U','U',na,b,tmp2,na_rows,na_cols,a,na_rows,na_cols)
3582 : : ! A is only set in the upper half, solve_evp_real needs a full matrix
3583 : : ! Set lower half from upper half
3584 : : call pdtran(na,na,1.d0,a,1,1,sc_desc,0.d0,tmp1,1,1,sc_desc)
3585 : : do i=1,na_cols
3586 : : ! Get global column corresponding to i and number of local rows up to
3587 : : ! and including the diagonal, these are unchanged in A
3588 : : n_col = indxl2g(i, nblk, my_pcol, 0, np_cols)
3589 : : n_row = numroc (n_col, nblk, my_prow, 0, np_rows)
3590 : : a(n_row+1:na_rows,i) = tmp1(n_row+1:na_rows,i)
3591 : : enddo
3592 : : ! 3. Calculate eigenvalues/eigenvectors of U**-T * A * U**-1
3593 : : ! Eigenvectors go to tmp1
3594 : : call elpa_func_solve_evp_1stage(elpa_hdl,a,tmp1,ev,nev)
3595 : : ! 4. Backtransform eigenvectors: Z = U**-1 * tmp1
3596 : : ! hermitian_multiply needs the transpose of U**-1, thus tmp2 = (U**-1)**T
3597 : : call pdtran(na,na,1.d0,b,1,1,sc_desc,0.d0,tmp2,1,1,sc_desc)
3598 : : call elpa_func_hermitian_multiply(elpa_hdl,'L','N',nev,tmp2,tmp1,na_rows,na_cols,z,na_rows,na_cols)
3599 : : end if
3600 : :
3601 : : call elpa_func_deallocate(elpa_hdl)
3602 : :
3603 : : end subroutine solve_gevp_real
3604 : : !!***
3605 : : #endif
3606 : :
3607 : : !----------------------------------------------------------------------
3608 : :
3609 : : !!****f* m_slk/compute_generalized_eigen_problem
3610 : : !! NAME
3611 : : !! compute_generalized_eigen_problem
3612 : : !!
3613 : : !! FUNCTION
3614 : : !! Calculation of eigenvalues and eigenvectors of the generalized eigenvalue problem: A * X = lambda * B X
3615 : : !! complex and real cases. B must be Hermitian (symmetric) positive definite.
3616 : : !! Backend: ELPA if available, else ScaLAPACK (PZHEGVX or PDSYGVX).
3617 : : !! The SLATE version is disabled (it does not work), SLATE builds use ScaLAPACK here.
3618 : : !!
3619 : : !! INPUTS
3620 : : !! processor= descriptor of a processor
3621 : : !! matrix1= A matrix
3622 : : !! matrix2= B matrix
3623 : : !! comm= MPI communicator
3624 : : !! istwf_k= 2 if we have a real matrix else complex.
3625 : : !! [nev]= Number of eigenvalues needed. Default: full set
3626 : : !! [use_gpu_elpa]= Flag to activate the use of GPU (ELPA only)
3627 : : !!
3628 : : !! OUTPUT
3629 : : !! results= ScaLAPACK matrix coming out of the operation (global dimensions must be equal to matrix
3630 : : !! even if only a part of the eigenvectors is needed.
3631 : : !! eigen= eigenvalues of the matrix dimensioned as the global size of the square matrix
3632 : : !! even if only a part of the eigenvalues is needed.
3633 : : !!
3634 : : !! SOURCE
3635 : :
3636 : 0 : subroutine compute_generalized_eigen_problem(processor,matrix1,matrix2,results,eigen,comm,istwf_k,&
3637 : : nev,use_gpu_elpa) ! Optional arguments
3638 : :
3639 : : !Arguments ------------------------------------
3640 : : class(slk_processor_t),intent(in) :: processor
3641 : : class(slkmat_dp_t),intent(inout) :: matrix1,matrix2
3642 : : class(slkmat_dp_t),intent(inout) :: results
3643 : : DOUBLE PRECISION,intent(inout) :: eigen(:)
3644 : : integer,intent(in) :: comm,istwf_k
3645 : : integer,optional,intent(in) :: nev
3646 : : integer,optional,intent(in) :: use_gpu_elpa
3647 : : #ifdef HAVE_LINALG_ELPA
3648 : : !Local
3649 : : type(slkmat_dp_t) :: tmp1, tmp2
3650 : : integer :: i,n_col, n_row, nev__,use_gpu_elpa__
3651 : :
3652 : : nev__ = matrix1%size_global(2); if (present(nev)) nev__ = nev
3653 : : use_gpu_elpa__ = 0; if (present(use_gpu_elpa)) use_gpu_elpa__ = use_gpu_elpa
3654 : :
3655 : : call tmp1%init(matrix1%size_global(1),matrix1%size_global(2),processor,istwf_k)
3656 : : call tmp2%init(matrix1%size_global(1),matrix1%size_global(2),processor,istwf_k)
3657 : :
3658 : : if (istwf_k/=2) then
3659 : : call solve_gevp_complex(matrix1%size_global(1), nev__, &
3660 : : matrix1%size_local(1),matrix1%size_local(2),matrix1%size_blocs(1), &
3661 : : matrix1%buffer_cplx,matrix2%buffer_cplx,eigen,results%buffer_cplx, &
3662 : : tmp1%buffer_cplx,tmp2%buffer_cplx, &
3663 : : processor%coords(1),processor%coords(2), &
3664 : : processor%grid%dims(1),processor%grid%dims(2), &
3665 : : matrix1%desc,processor%comm,use_gpu_elpa=use_gpu_elpa__)
3666 : : else
3667 : : call solve_gevp_real(matrix1%size_global(1), nev__, &
3668 : : matrix1%size_local(1),matrix1%size_local(2),matrix1%size_blocs(1), &
3669 : : matrix1%buffer_real,matrix2%buffer_real,eigen,results%buffer_real, &
3670 : : tmp1%buffer_real,tmp2%buffer_real, &
3671 : : processor%coords(1),processor%coords(2), &
3672 : : processor%grid%dims(1),processor%grid%dims(2), &
3673 : : matrix1%desc,processor%comm,use_gpu_elpa=use_gpu_elpa__)
3674 : : end if
3675 : : call tmp1%free()
3676 : : call tmp2%free()
3677 : :
3678 : :
3679 : : !#elif defined HAVE_LINALG_SLATE
3680 : : !!FIXME Doesn't work. slate_zheev/syev is fine though so we fall back to scaLAPACK instead
3681 : : !!Local variables-------------------------------
3682 : : ! integer :: nev__, use_gpu_elpa_, info_slate
3683 : : !! *************************************************************************
3684 : : !
3685 : : ! nev__ = matrix1%size_global(2); if (present(nev)) nev__ = nev
3686 : : ! use_gpu_elpa__ = 0; if (present(use_gpu_elpa)) use_gpu_elpa__ = use_gpu_elpa
3687 : : !
3688 : : ! if (istwf_k /= 2) then
3689 : : ! call slate_zhegv_c( &
3690 : : ! matrix1%size_global(1), & ! n
3691 : : ! matrix1%size_blocs(1), & ! nb (mb==nb required)
3692 : : ! processor%grid%dims(1), & ! nprow
3693 : : ! processor%grid%dims(2), & ! npcol
3694 : : ! processor%comm, & ! Fortran MPI comm handle
3695 : : ! matrix1%size_local(1), & ! lda
3696 : : ! matrix1%buffer_cplx, & ! a_data (destroyed on exit)
3697 : : ! matrix2%size_local(1), & ! ldb
3698 : : ! matrix2%buffer_cplx, & ! b_data (destroyed on exit)
3699 : : ! results%size_local(1), & ! ldz
3700 : : ! results%buffer_cplx, & ! z_data (eigenvectors out)
3701 : : ! eigen, & ! w (eigenvalues out)
3702 : : ! nev__, & ! nev
3703 : : ! use_gpu_elpa_, & ! use_gpu (reuse existing flag)
3704 : : ! info_slate)
3705 : : ! ABI_CHECK(info_slate == 0, "slate_zhgev_c failed")
3706 : : ! else
3707 : : ! call slate_dsygv_c( &
3708 : : ! matrix1%size_global(1), & ! n
3709 : : ! matrix1%size_blocs(1), & ! nb (mb==nb required)
3710 : : ! processor%grid%dims(1), & ! nprow
3711 : : ! processor%grid%dims(2), & ! npcol
3712 : : ! processor%comm, & ! Fortran MPI comm handle
3713 : : ! matrix1%size_local(1), & ! lda
3714 : : ! matrix1%buffer_real, & ! a_data (destroyed on exit)
3715 : : ! matrix2%size_local(1), & ! ldb
3716 : : ! matrix2%buffer_real, & ! b_data (destroyed on exit)
3717 : : ! results%size_local(1), & ! ldz
3718 : : ! results%buffer_real, & ! z_data (eigenvectors out)
3719 : : ! eigen, & ! w (eigenvalues out)
3720 : : ! nev__, & ! nev
3721 : : ! use_gpu_elpa_, & ! use_gpu (reuse existing flag)
3722 : : ! info_slate)
3723 : : ! ABI_CHECK(info_slate == 0, "slate_dsygv_c failed")
3724 : : ! end if
3725 : :
3726 : :
3727 : : #elif defined(HAVE_LINALG_SCALAPACK)
3728 : : !Local variables-------------------------------
3729 : : integer :: LRWORK,LIWORK,LCWORK,INFO
3730 : : !character(len=500) :: msg
3731 : : integer , dimension(1) :: IWORK_tmp
3732 : : DOUBLE PRECISION, dimension(1) :: RWORK_tmp
3733 : : complex(dpc) , dimension(1) :: CWORK_tmp
3734 : :
3735 : : integer , allocatable :: IWORK(:)
3736 : : DOUBLE PRECISION, allocatable :: RWORK(:)
3737 : : complex(dpc) , allocatable :: CWORK(:)
3738 : : integer, allocatable :: ICLUSTR(:)
3739 : : integer, allocatable :: IFAIL(:)
3740 : : DOUBLE PRECISION, allocatable :: GAP(:)
3741 : : DOUBLE PRECISION :: ABSTOL,ORFAC
3742 : : integer , parameter :: IZERO=0
3743 : : integer :: M,NZ,ierr,TWORK_tmp(3),TWORK(3) ! IA,JA,IZ,JZ,
3744 : : character(len=1) :: range
3745 : : integer :: nev__, il, iu
3746 : : ! *************************************************************************
3747 : :
3748 : : ABI_UNUSED(use_gpu_elpa) ! No GPU implementation is using scaLAPACK
3749 : : nev__ = matrix1%size_global(2); range = "A"; il = 0; iu = 0
3750 : : if (present(nev)) then
3751 : : nev__ = nev; range = "I"; il = 1; iu = nev
3752 : : end if
3753 : :
3754 : : ! Initialisation
3755 : : INFO = 0
3756 : : ABSTOL = zero
3757 : : ORFAC = -1.D+0
3758 : :
3759 : : ! Allocate the arrays for the results of the calculation
3760 : : ABI_MALLOC(IFAIL ,(matrix1%size_global(2)))
3761 : : ABI_MALLOC(ICLUSTR,(2*processor%grid%dims(1)*processor%grid%dims(2)))
3762 : : ABI_MALLOC(GAP ,( processor%grid%dims(1)*processor%grid%dims(2)))
3763 : :
3764 : : CWORK_tmp = (0.0_DP,0.0_DP)
3765 : : RWORK_tmp = 0.0_DP
3766 : : ! Get the size of the work arrays
3767 : : if (istwf_k /= 2) then
3768 : : call PZHEGVX(1, 'V', range, 'U',&
3769 : : matrix1%size_global(2),&
3770 : : matrix1%buffer_cplx,1,1,matrix1%desc, &
3771 : : matrix2%buffer_cplx,1,1,matrix2%desc, &
3772 : : ZERO,ZERO,il,iu,ABSTOL,&
3773 : : m,nz,eigen,ORFAC, &
3774 : : results%buffer_cplx,1,1,results%desc, &
3775 : : CWORK_tmp,-1,RWORK_tmp,-1,IWORK_tmp,-1,&
3776 : : IFAIL,ICLUSTR,GAP,INFO)
3777 : : else
3778 : : call PDSYGVX(1,'V',range,'U',&
3779 : : matrix1%size_global(2),&
3780 : : matrix1%buffer_real,1,1,matrix1%desc, &
3781 : : matrix2%buffer_real,1,1,matrix2%desc, &
3782 : : ZERO,ZERO,il,iu,ABSTOL,&
3783 : : m,nz,eigen,ORFAC, &
3784 : : results%buffer_real,1,1,results%desc, &
3785 : : RWORK_tmp,-1,IWORK_tmp,-1,&
3786 : : IFAIL,ICLUSTR,GAP,INFO)
3787 : : endif
3788 : :
3789 : : if (INFO/=0) then
3790 : : ABI_ERROR(sjoin("Problem to compute workspace to use ScaLAPACK, INFO=",itoa(INFO)))
3791 : : endif
3792 : :
3793 : : TWORK_tmp(1) = IWORK_tmp(1)
3794 : : TWORK_tmp(2) = INT(RWORK_tmp(1)) + matrix1%size_global(2) *(matrix1%size_global(2)-1)
3795 : : TWORK_tmp(3) = INT(real(CWORK_tmp(1)))
3796 : :
3797 : : ! Get the maximum of sizes of the work arrays processor%comm
3798 : : call MPI_ALLREDUCE(TWORK_tmp,TWORK,3,MPI_integer,MPI_MAX,comm,ierr)
3799 : :
3800 : : LIWORK = TWORK(1)
3801 : : LRWORK = TWORK(2)
3802 : : LCWORK = TWORK(3)
3803 : :
3804 : : ! Allocate the work arrays
3805 : : if (LIWORK>0) then
3806 : : ABI_MALLOC(IWORK,(LIWORK))
3807 : : IWORK(:) = 0
3808 : : else
3809 : : ABI_MALLOC(IWORK,(1))
3810 : : end if
3811 : : if (LRWORK>0) then
3812 : : ABI_MALLOC(RWORK,(LRWORK))
3813 : : RWORK(:) = 0._dp
3814 : : else
3815 : : ABI_MALLOC(RWORK,(1))
3816 : : end if
3817 : : if (LCWORK>0) then
3818 : : ABI_MALLOC(CWORK,(LCWORK))
3819 : : CWORK(:) = (0._dp,0._dp)
3820 : : else
3821 : : ABI_MALLOC(CWORK,(1))
3822 : : end if
3823 : :
3824 : : ! Call the calculation routine
3825 : : if (istwf_k/=2) then
3826 : : ! write(std_out,*) 'I am using PZHEGVX'
3827 : : call PZHEGVX(1,'V',range,'U',&
3828 : : matrix1%size_global(2),&
3829 : : matrix1%buffer_cplx,1,1,matrix1%desc, &
3830 : : matrix2%buffer_cplx,1,1,matrix2%desc, &
3831 : : ZERO,ZERO,il,iu,ABSTOL,&
3832 : : m,nz,eigen,ORFAC, &
3833 : : results%buffer_cplx,1,1,results%desc, &
3834 : : CWORK,LCWORK,RWORK,LRWORK,IWORK,LIWORK,&
3835 : : IFAIL,ICLUSTR,GAP,INFO)
3836 : : else
3837 : : ! write(std_out,*) 'I am using PDSYGVX'
3838 : : call PDSYGVX(1,'V',range,'U',&
3839 : : matrix1%size_global(2),&
3840 : : matrix1%buffer_real,1,1,matrix1%desc, &
3841 : : matrix2%buffer_real,1,1,matrix2%desc, &
3842 : : ZERO,ZERO,il,iu,ABSTOL,&
3843 : : m,nz,eigen,ORFAC, &
3844 : : results%buffer_real,1,1,results%desc, &
3845 : : RWORK,LRWORK,IWORK,LIWORK,&
3846 : : IFAIL,ICLUSTR,GAP,INFO)
3847 : : endif
3848 : :
3849 : : if (INFO/=0) then
3850 : : ABI_ERROR(sjoin("Problem to compute eigen problem with ScaLAPACK, INFO=", itoa(INFO)))
3851 : : endif
3852 : :
3853 : : ABI_FREE(IFAIl)
3854 : : ABI_FREE(ICLUSTR)
3855 : : ABI_FREE(GAP)
3856 : : ABI_SFREE(IWORK)
3857 : : ABI_SFREE(RWORK)
3858 : : ABI_SFREE(CWORK)
3859 : : #endif
3860 : 0 : return
3861 : :
3862 : : end subroutine compute_generalized_eigen_problem
3863 : : !!***
3864 : :
3865 : : !----------------------------------------------------------------------
3866 : :
3867 : : !!****f* m_slk/compute_eigen1
3868 : : !! NAME
3869 : : !! compute_eigen1
3870 : : !!
3871 : : !! FUNCTION
3872 : : !! Calculation of eigenvalues and eigenvectors of a Hermitian (real symmetric) matrix. complex and real cases.
3873 : : !! The global matrix, replicated on all the processes, is distributed, diagonalized with compute_eigen_problem
3874 : : !! and the eigenvectors are gathered on all the processes.
3875 : : !!
3876 : : !! INPUTS
3877 : : !! comm= MPI communicator of the processes in the grid
3878 : : !! processor= descriptor of a processor
3879 : : !! cplex=1 if matrix is real, 2 if complex. Must be consistent with istwf_k.
3880 : : !! nbli_global number of lines
3881 : : !! nbco_global number of columns
3882 : : !! istwf_k= 2 if we have a real matrix else complex.
3883 : : !! [use_gpu_elpa]= Flag to activate the use of GPU (ELPA only)
3884 : : !!
3885 : : !! SIDE EFFECTS
3886 : : !! matrix(cplex*nbli_global, nbco_global)=
3887 : : !! input: the global matrix (real/imag pairs if cplex=2). Only the upper triangle is used.
3888 : : !! With ELPA or SLATE, the lower triangle is overwritten with the conjugate of the upper triangle.
3889 : : !! output: the eigenvectors (columns), on all the processes.
3890 : : !! vector(nbli_global)= eigenvalues in ascending order.
3891 : : !!
3892 : : !! SOURCE
3893 : :
3894 : 0 : subroutine compute_eigen1(comm,processor,cplex,nbli_global,nbco_global,matrix,vector,istwf_k,&
3895 : : use_gpu_elpa) ! Optional argument
3896 : :
3897 : : !Arguments ------------------------------------
3898 : : !scalars
3899 : : integer,intent(in) :: comm
3900 : : integer,intent(in) :: cplex,nbli_global,nbco_global
3901 : : integer,intent(in) :: istwf_k
3902 : : class(slk_processor_t),intent(in) :: processor
3903 : : integer,intent(in),optional :: use_gpu_elpa
3904 : : !arrays
3905 : : real(dp),intent(inout) :: matrix(cplex*nbli_global,nbco_global)
3906 : : real(dp),intent(inout) :: vector(:)
3907 : :
3908 : : !Local variables-------------------------------
3909 : : #if defined(HAVE_LINALG_ELPA) || defined(HAVE_LINALG_SLATE)
3910 : : integer :: i,j
3911 : : #endif
3912 : : integer :: ierr,use_gpu_elpa_
3913 [ # # # # : 0 : type(slkmat_dp_t) :: sca_matrix1
# # ]
3914 [ # # # # : 0 : type(slkmat_dp_t) :: sca_matrix2
# # ]
3915 : 0 : real(dp),allocatable :: r_tmp_evec(:,:)
3916 : 0 : complex(dpc),allocatable :: z_tmp_evec(:,:)
3917 : : ! *************************************************************************
3918 : :
3919 : 0 : use_gpu_elpa_=0
3920 : : #if defined(HAVE_LINALG_ELPA) || defined(HAVE_LINALG_SLATE)
3921 : : if (present(use_gpu_elpa)) use_gpu_elpa_=use_gpu_elpa
3922 : : #endif
3923 : :
3924 : : ! ================================
3925 : : ! INITIALISATION SCALAPACK MATRIX
3926 : : ! ================================
3927 : 0 : call sca_matrix1%init(nbli_global,nbco_global,processor,istwf_k)
3928 : 0 : call sca_matrix2%init(nbli_global,nbco_global,processor,istwf_k)
3929 : :
3930 : : ! ==============================
3931 : : ! FILLING SCALAPACK MATRIX
3932 : : ! ==============================
3933 [ # # ]: 0 : if ( istwf_k /= 2 ) then
3934 [ # # ]: 0 : ABI_CHECK_IEQ(cplex, 2, "cplex != 2")
3935 [ # # # # : 0 : ABI_MALLOC(z_tmp_evec,(nbli_global,nbco_global))
# # # # #
# # # # #
# # ]
3936 [ # # # # ]: 0 : z_tmp_evec=cmplx(0._DP,0._DP)
3937 : : #if defined(HAVE_LINALG_ELPA) || defined(HAVE_LINALG_SLATE)
3938 : : ! The full matrix must be set (not only one half like in scalapack).
3939 : : do j=1,nbco_global
3940 : : do i=j+1,nbli_global
3941 : : matrix(2*(i-1)+1,j) = matrix(2*(j-1)+1,i)
3942 : : matrix(2*(i-1)+2,j) = -matrix(2*(j-1)+2,i)
3943 : : end do
3944 : : end do
3945 : : #endif
3946 : 0 : call sca_matrix1%from_complex_glob(matrix,istwf_k)
3947 : : else
3948 [ # # ]: 0 : ABI_CHECK_IEQ(cplex, 1, "cplex != 2")
3949 [ # # # # : 0 : ABI_MALLOC(r_tmp_evec,(nbli_global,nbco_global))
# # # # #
# # # # #
# # ]
3950 [ # # # # ]: 0 : r_tmp_evec(:,:)=0._DP
3951 : : #if defined(HAVE_LINALG_ELPA) || defined(HAVE_LINALG_SLATE)
3952 : : ! The full matrix must be set (not only one half like in scalapack).
3953 : : do j=1,nbco_global
3954 : : do i=j+1,nbli_global
3955 : : matrix(i,j) = matrix(j,i)
3956 : : end do
3957 : : end do
3958 : : #endif
3959 : 0 : call sca_matrix1%from_real_glob(matrix, istwf_k)
3960 : : endif
3961 : :
3962 : : ! ================================
3963 : : ! COMPUTE EIGEN VALUES AND VECTORS : A * X = lambda * X
3964 : : ! ================================
3965 : : call compute_eigen_problem(processor,sca_matrix1, sca_matrix2,vector, comm,istwf_k, &
3966 : 0 : use_gpu_elpa=use_gpu_elpa_)
3967 : :
3968 : : ! ==============================
3969 : : ! CONCATENATE EIGEN VECTORS
3970 : : ! ==============================
3971 : : #ifdef HAVE_MPI
3972 [ # # ]: 0 : if (istwf_k /= 2) then
3973 : 0 : call sca_matrix2%to_complex_glob(z_tmp_evec, istwf_k)
3974 : 0 : call MPI_ALLREDUCE(z_tmp_evec, matrix, nbli_global*nbco_global, MPI_DOUBLE_complex, MPI_SUM,comm,ierr)
3975 : : else
3976 : 0 : call sca_matrix2%to_real_glob(r_tmp_evec, istwf_k)
3977 : 0 : call MPI_ALLREDUCE(r_tmp_evec, matrix, nbli_global*nbco_global, MPI_DOUBLE_PRECISION, MPI_SUM,comm,ierr)
3978 : : endif
3979 : : #endif
3980 : :
3981 : : ! ====================================
3982 : : ! DESTRUCTION SCALAPACK AND TMP MATRICES
3983 : : ! ====================================
3984 : 0 : call sca_matrix1%free()
3985 : 0 : call sca_matrix2%free()
3986 : :
3987 [ # # ]: 0 : ABI_SFREE(z_tmp_evec)
3988 [ # # ]: 0 : ABI_SFREE(r_tmp_evec)
3989 : :
3990 : : #ifndef HAVE_LINALG_ELPA
3991 : : ABI_UNUSED(use_gpu_elpa)
3992 : : #endif
3993 : :
3994 [ # # # # : 0 : end subroutine compute_eigen1
# # # # #
# # # ]
3995 : : !!***
3996 : :
3997 : : !----------------------------------------------------------------------
3998 : :
3999 : : !!****f* m_slk/compute_eigen2
4000 : : !! NAME
4001 : : !! compute_eigen2
4002 : : !!
4003 : : !! FUNCTION
4004 : : !! Calculation of eigenvalues and eigenvectors: A * X = lambda * B * X
4005 : : !! complex and real cases.
4006 : : !! The global matrices, replicated on all the processes, are distributed, the problem is solved with
4007 : : !! compute_generalized_eigen_problem and the eigenvectors are gathered on all the processes.
4008 : : !!
4009 : : !! INPUTS
4010 : : !! comm= MPI communicator of the processes in the grid
4011 : : !! processor= descriptor of a processor
4012 : : !! cplex=1 if matrix is real, 2 if complex. Must be consistent with istwf_k.
4013 : : !! nbli_global number of lines
4014 : : !! nbco_global number of columns
4015 : : !! istwf_k= 2 if we have a real matrix else complex.
4016 : : !! [use_gpu_elpa]= Flag to activate the use of GPU (ELPA only)
4017 : : !!
4018 : : !! SIDE EFFECTS
4019 : : !! matrix1(cplex*nbli_global, nbco_global)=
4020 : : !! input: the global matrix A (real/imag pairs if cplex=2). Only the upper triangle is used.
4021 : : !! output: the eigenvectors (columns), on all the processes.
4022 : : !! matrix2(cplex*nbli_global, nbco_global)= the global matrix B (Hermitian positive definite).
4023 : : !! Only the upper triangle is used.
4024 : : !! With ELPA or SLATE, the lower triangles of matrix1 and matrix2 are overwritten with the conjugate of the upper ones.
4025 : : !! vector(nbli_global)= eigenvalues in ascending order.
4026 : : !!
4027 : : !! SOURCE
4028 : :
4029 : 0 : subroutine compute_eigen2(comm,processor,cplex,nbli_global,nbco_global,matrix1,matrix2,vector,istwf_k, &
4030 : : use_gpu_elpa) ! Optional argument
4031 : :
4032 : : !Arguments ------------------------------------
4033 : : !scalars
4034 : : integer,intent(in) :: cplex,nbli_global,nbco_global
4035 : : integer,intent(in) :: comm
4036 : : integer,intent(in) :: istwf_k
4037 : : class(slk_processor_t),intent(in) :: processor
4038 : : integer,optional,intent(in) :: use_gpu_elpa
4039 : : !arrays
4040 : : real(dp),intent(inout) :: matrix1(cplex*nbli_global,nbco_global)
4041 : : real(dp),intent(inout) :: matrix2(cplex*nbli_global,nbco_global)
4042 : : real(dp),intent(inout) :: vector(:)
4043 : :
4044 : : !Local variables-------------------------------
4045 : : #if defined(HAVE_LINALG_ELPA) || defined(HAVE_LINALG_SLATE)
4046 : : integer :: i,j
4047 : : #endif
4048 : : integer :: ierr,use_gpu_elpa_
4049 [ # # # # : 0 : type(slkmat_dp_t) :: sca_matrix1, sca_matrix2, sca_matrix3
# # # # #
# # # # #
# # # # ]
4050 : 0 : real(dp),allocatable :: r_tmp_evec(:,:)
4051 : 0 : complex(dpc),allocatable :: z_tmp_evec(:,:)
4052 : : ! *************************************************************************
4053 : :
4054 : 0 : use_gpu_elpa_=0
4055 : : #if defined HAVE_LINALG_ELPA
4056 : : if (present(use_gpu_elpa)) use_gpu_elpa_=use_gpu_elpa
4057 : : #endif
4058 : :
4059 : : ! ================================
4060 : : ! INITIALISATION SCALAPACK MATRIX
4061 : : ! ================================
4062 : 0 : call sca_matrix1%init(nbli_global,nbco_global,processor,istwf_k)
4063 : 0 : call sca_matrix2%init(nbli_global,nbco_global,processor,istwf_k)
4064 : 0 : call sca_matrix3%init(nbli_global,nbco_global,processor,istwf_k)
4065 : :
4066 : : ! ==============================
4067 : : ! FILLING SCALAPACK MATRIX
4068 : : ! ==============================
4069 [ # # ]: 0 : if ( istwf_k /= 2 ) then
4070 [ # # ]: 0 : ABI_CHECK_IEQ(cplex, 2, "cplex != 2")
4071 [ # # # # : 0 : ABI_MALLOC(z_tmp_evec,(nbli_global,nbco_global))
# # # # #
# # # # #
# # ]
4072 [ # # # # ]: 0 : z_tmp_evec=cmplx(0._DP,0._DP)
4073 : : #if defined(HAVE_LINALG_ELPA) || defined(HAVE_LINALG_SLATE)
4074 : : ! The full matrix must be set (not only one half like in scalapack).
4075 : : do j=1,nbco_global
4076 : : do i=j+1,nbli_global
4077 : : matrix1(2*(i-1)+1,j) = matrix1(2*(j-1)+1,i)
4078 : : matrix1(2*(i-1)+2,j) = -matrix1(2*(j-1)+2,i)
4079 : : matrix2(2*(i-1)+1,j) = matrix2(2*(j-1)+1,i)
4080 : : matrix2(2*(i-1)+2,j) = -matrix2(2*(j-1)+2,i)
4081 : : end do
4082 : : end do
4083 : : #endif
4084 : 0 : call sca_matrix1%from_complex_glob(matrix1, istwf_k)
4085 : 0 : call sca_matrix2%from_complex_glob(matrix2, istwf_k)
4086 : : else
4087 [ # # ]: 0 : ABI_CHECK_IEQ(cplex, 1, "cplex != 1")
4088 [ # # # # : 0 : ABI_MALLOC(r_tmp_evec,(nbli_global,nbco_global))
# # # # #
# # # # #
# # ]
4089 [ # # # # ]: 0 : r_tmp_evec(:,:)=0._DP
4090 : : #if defined(HAVE_LINALG_ELPA) || defined(HAVE_LINALG_SLATE)
4091 : : ! The full matrix must be set (not only one half like in scalapack).
4092 : : do j=1,nbco_global
4093 : : do i=j+1,nbli_global
4094 : : matrix1(i,j) = matrix1(j,i)
4095 : : matrix2(i,j) = matrix2(j,i)
4096 : : end do
4097 : : end do
4098 : : #endif
4099 : 0 : call sca_matrix1%from_real_glob(matrix1,istwf_k)
4100 : 0 : call sca_matrix2%from_real_glob(matrix2,istwf_k)
4101 : : endif
4102 : :
4103 : : ! ================================
4104 : : ! COMPUTE EIGEN VALUES AND VECTORS : A * X = lambda * B * X
4105 : : ! ================================
4106 : : call compute_generalized_eigen_problem(processor,sca_matrix1,sca_matrix2,&
4107 : 0 : sca_matrix3,vector,comm,istwf_k,use_gpu_elpa=use_gpu_elpa_)
4108 : :
4109 : : ! ==============================
4110 : : ! CONCATENATE EIGEN VECTORS
4111 : : ! ==============================
4112 : : #ifdef HAVE_MPI
4113 [ # # ]: 0 : if ( istwf_k /= 2 ) then
4114 : 0 : call sca_matrix3%to_complex_glob(z_tmp_evec,istwf_k)
4115 : 0 : call MPI_ALLREDUCE(z_tmp_evec, matrix1, nbli_global*nbco_global, MPI_DOUBLE_complex, MPI_SUM,comm,ierr)
4116 [ # # ]: 0 : ABI_FREE(z_tmp_evec)
4117 : : else
4118 : 0 : call sca_matrix3%to_real_glob(r_tmp_evec, istwf_k)
4119 : 0 : call MPI_ALLREDUCE(r_tmp_evec, matrix1, nbli_global*nbco_global, MPI_DOUBLE_PRECISION, MPI_SUM,comm,ierr)
4120 [ # # ]: 0 : ABI_FREE(r_tmp_evec)
4121 : : endif
4122 : : #endif
4123 : :
4124 : : ! ====================================
4125 : : ! DESTRUCTION SCALAPACK AND TMP MATRICES
4126 : : ! ====================================
4127 : 0 : call sca_matrix1%free()
4128 : 0 : call sca_matrix2%free()
4129 : 0 : call sca_matrix3%free()
4130 : :
4131 : : #ifndef HAVE_LINALG_ELPA
4132 : : ABI_UNUSED(use_gpu_elpa)
4133 : : #endif
4134 : :
4135 [ # # # # : 0 : end subroutine compute_eigen2
# # # # #
# # # # #
# # ]
4136 : : !!***
4137 : :
4138 : : !----------------------------------------------------------------------
4139 : :
4140 : : !!****f* m_slk/slkmat_dp_heev
4141 : : !! NAME
4142 : : !! slkmat_dp_heev
4143 : : !!
4144 : : !! FUNCTION
4145 : : !! slkmat_dp_heev computes selected eigenvalues and, optionally, eigenvectors of an Hermitian matrix A.
4146 : : !! A * X = lambda * X
4147 : : !!
4148 : : !! INPUTS
4149 : : !! JOBZ (global input) CHARACTER*1
4150 : : !! Specifies whether or not to compute the eigenvectors:
4151 : : !! = "N": Compute eigenvalues only.
4152 : : !! = "V": Compute eigenvalues and eigenvectors.
4153 : : !! UPLO (global input) CHARACTER*1
4154 : : !! Specifies whether the upper or lower triangular part of the symmetric matrix A is stored:
4155 : : !! = "U": Upper triangular
4156 : : !! = "L": Lower triangular
4157 : : !!
4158 : : !! mat=The object storing the local buffer in DOUBLE PRECISION, the array descriptor, the PBLAS context.
4159 : : !! Only complex matrices (buffer_cplx) are supported.
4160 : : !! vec=The distributed eigenvectors. Not referenced if JOBZ="N"
4161 : : !! [mat_size]= Order N of the (sub)matrix. Default: mat%size_global(2)
4162 : : !! [ija(2)]= Global row and column indices of the first entry of sub(A) in mat. Default: [1, 1]
4163 : : !! [ijz(2)]= Global row and column indices of the first entry of sub(Z) in vec. Default: [1, 1]
4164 : : !!
4165 : : !! OUTPUT
4166 : : !! W (global output) array, dimension (N) where N is the rank of the global matrix.
4167 : : !! On normal exit, the first M entries contain the selected eigenvalues in ascending order.
4168 : : !!
4169 : : !! SIDE EFFECTS
4170 : : !! If JOBZ="V", the local buffer vec%buffer_cplx will contain part of the distributed eigenvectors.
4171 : : !! On exit, the lower triangle (if UPLO='L') or the upper triangle (if UPLO='U') of A, including the diagonal, is destroyed.
4172 : : !!
4173 : : !! SOURCE
4174 : :
4175 : 0 : subroutine slkmat_dp_heev(mat, jobz, uplo, vec, w, &
4176 : : mat_size, ija, ijz) ! Optional
4177 : :
4178 : : !Arguments ------------------------------------
4179 : : !scalars
4180 : : class(slkmat_dp_t),intent(inout) :: mat
4181 : : character(len=*),intent(in) :: jobz, uplo
4182 : : class(slkmat_dp_t),intent(inout) :: vec
4183 : : !arrays
4184 : : real(dp),intent(out) :: w(:)
4185 : : integer,optional,intent(in) :: mat_size, ija(2), ijz(2)
4186 : :
4187 : : #ifdef HAVE_LINALG_SCALAPACK
4188 : : !Local variables ------------------------------
4189 : : !scalars
4190 : : integer :: lwork, lrwork, info, nn
4191 : : !character(len=500) :: msg
4192 : : !arrays
4193 : : integer :: ija__(2), ijz__(2)
4194 : : real(dp),allocatable :: rwork_dp(:)
4195 : : complex(dp),allocatable :: work_dp(:)
4196 : : !************************************************************************
4197 : :
4198 : : ABI_CHECK(allocated(mat%buffer_cplx), "buffer_cplx not allocated")
4199 : :
4200 : : nn = mat%size_global(2); if (present(mat_size)) nn = mat_size
4201 : : ija__ = [1, 1]; if (present(ija)) ija__ = ija
4202 : : ijz__ = [1, 1]; if (present(ijz)) ijz__ = ijz
4203 : :
4204 : : ! Get optimal size of workspace.
4205 : : lwork = - 1; lrwork = -1
4206 : : ABI_MALLOC(work_dp, (1))
4207 : : ABI_MALLOC(rwork_dp, (1))
4208 : :
4209 : : !call pzheev(jobz, uplo, n, a, ia, ja, desca, w, z, iz, jz, descz, work, lwork, rwork, lrwork, info)
4210 : :
4211 : : call PZHEEV(jobz, uplo, nn, mat%buffer_cplx, ija__(1), ija__(2), mat%desc, &
4212 : : w, vec%buffer_cplx, ijz__(1), ijz__(2), vec%desc, work_dp, lwork, rwork_dp, lrwork, info)
4213 : : ABI_CHECK(info == 0, sjoin("Error in the calculation of the workspace size, info:", itoa(info)))
4214 : :
4215 : : lwork = NINT(real(work_dp(1))); lrwork= NINT(rwork_dp(1)) !*2
4216 : : ABI_FREE(work_dp)
4217 : : ABI_FREE(rwork_dp)
4218 : :
4219 : : ! MG: Nov 23 2011. On my mac with the official scalapack package, rwork(1) is not large enough and causes a SIGFAULT.
4220 : : if (firstchar(jobz, ['V'])) then
4221 : : if (lrwork < 2*nn + 2*nn-2) lrwork = 2*nn + 2*nn-2
4222 : : else if (firstchar(jobz, ['N'])) then
4223 : : if (lrwork < 2*nn) lrwork = 2*nn
4224 : : end if
4225 : : !write(std_out,*)lwork,lrwork
4226 : :
4227 : : ! Solve the problem.
4228 : : ABI_MALLOC(work_dp, (lwork))
4229 : : ABI_MALLOC(rwork_dp, (lrwork))
4230 : :
4231 : : call PZHEEV(jobz, uplo, nn, mat%buffer_cplx, ija__(1), ija__(2), mat%desc, &
4232 : : w, vec%buffer_cplx, ijz__(1), ijz__(2), vec%desc, work_dp, lwork, rwork_dp, lrwork, info)
4233 : : ABI_CHECK(info == 0, sjoin("PZHEEV returned info:", itoa(info)))
4234 : : ABI_FREE(work_dp)
4235 : : ABI_FREE(rwork_dp)
4236 : : #endif
4237 : :
4238 : 0 : end subroutine slkmat_dp_heev
4239 : : !!***
4240 : :
4241 : : !----------------------------------------------------------------------
4242 : :
4243 : : !!****f* m_slk/slkmat_sp_heev
4244 : : !! NAME
4245 : : !! slkmat_sp_heev
4246 : : !!
4247 : : !! FUNCTION
4248 : : !! slkmat_sp_heev computes selected eigenvalues and, optionally, eigenvectors of an Hermitian matrix A.
4249 : : !! A * X = lambda * X
4250 : : !!
4251 : : !! INPUTS
4252 : : !! JOBZ (global input) CHARACTER*1
4253 : : !! Specifies whether or not to compute the eigenvectors:
4254 : : !! = "N": Compute eigenvalues only.
4255 : : !! = "V": Compute eigenvalues and eigenvectors.
4256 : : !! UPLO (global input) CHARACTER*1
4257 : : !! Specifies whether the upper or lower triangular part of the symmetric matrix A is stored:
4258 : : !! = "U": Upper triangular
4259 : : !! = "L": Lower triangular
4260 : : !!
4261 : : !! mat=The object storing the local buffer in SINGLE PRECISION, the array descriptor, the PBLAS context.
4262 : : !! Only complex matrices (buffer_cplx) are supported.
4263 : : !! vec=The distributed eigenvectors. Not referenced if JOBZ="N"
4264 : : !! [mat_size]= Order N of the (sub)matrix. Default: mat%size_global(2)
4265 : : !! [ija(2)]= Global row and column indices of the first entry of sub(A) in mat. Default: [1, 1]
4266 : : !! [ijz(2)]= Global row and column indices of the first entry of sub(Z) in vec. Default: [1, 1]
4267 : : !!
4268 : : !! OUTPUT
4269 : : !! W (global output) array, dimension (N) where N is the rank of the global matrix.
4270 : : !! On normal exit, the first M entries contain the selected eigenvalues in ascending order.
4271 : : !!
4272 : : !! SIDE EFFECTS
4273 : : !! If JOBZ="V", the local buffer vec%buffer_cplx will contain part of the distributed eigenvectors.
4274 : : !! On exit, the lower triangle (if UPLO='L') or the upper triangle (if UPLO='U') of A, including the diagonal, is destroyed.
4275 : : !!
4276 : : !! SOURCE
4277 : :
4278 : 0 : subroutine slkmat_sp_heev(mat, jobz, uplo, vec, w, &
4279 : : mat_size, ija, ijz) ! Optional
4280 : :
4281 : : !Arguments ------------------------------------
4282 : : !scalars
4283 : : class(slkmat_sp_t),intent(inout) :: mat
4284 : : character(len=*),intent(in) :: jobz, uplo
4285 : : class(slkmat_sp_t),intent(inout) :: vec
4286 : : !arrays
4287 : : real(sp),intent(out) :: w(:)
4288 : : integer,optional,intent(in) :: mat_size, ija(2), ijz(2)
4289 : :
4290 : : #ifdef HAVE_LINALG_SCALAPACK
4291 : : !Local variables ------------------------------
4292 : : !scalars
4293 : : integer :: lwork, lrwork, info, nn
4294 : : !arrays
4295 : : integer :: ija__(2), ijz__(2)
4296 : : real(sp),allocatable :: rwork_sp(:)
4297 : : complex(sp),allocatable :: work_sp(:)
4298 : : !************************************************************************
4299 : :
4300 : : ABI_CHECK(allocated(mat%buffer_cplx), "buffer_cplx not allocated")
4301 : :
4302 : : nn = mat%size_global(2); if (present(mat_size)) nn = mat_size
4303 : : ija__ = [1, 1]; if (present(ija)) ija__ = ija
4304 : : ijz__ = [1, 1]; if (present(ijz)) ijz__ = ijz
4305 : :
4306 : : ! Get optimal size of workspace.
4307 : : lwork = - 1; lrwork = -1
4308 : : ABI_MALLOC(work_sp, (1))
4309 : : ABI_MALLOC(rwork_sp, (1))
4310 : :
4311 : : !call pzheev(jobz, uplo, n, a, ia, ja, desca, w, z, iz, jz, descz, work, lwork, rwork, lrwork, info)
4312 : :
4313 : : call PCHEEV(jobz, uplo, nn, mat%buffer_cplx, ija__(1), ija__(2), mat%desc, &
4314 : : w, vec%buffer_cplx, ijz__(1), ijz__(2), vec%desc, work_sp, lwork, rwork_sp, lrwork, info)
4315 : : ABI_CHECK(info == 0, sjoin("Error in the calculation of the workspace size, info:", itoa(info)))
4316 : :
4317 : : lwork = NINT(real(work_sp(1))); lrwork= NINT(rwork_sp(1)) !*2
4318 : : ABI_FREE(work_sp)
4319 : : ABI_FREE(rwork_sp)
4320 : :
4321 : : ! MG: Nov 23 2011. On my mac with the official scalapack package, rwork(1) is not large enough and causes a SIGFAULT.
4322 : : if (firstchar(jobz, ['V'])) then
4323 : : if (lrwork < 2*nn + 2*nn-2) lrwork = 2*nn + 2*nn-2
4324 : : else if (firstchar(jobz, ['N'])) then
4325 : : if (lrwork < 2*nn) lrwork = 2*nn
4326 : : end if
4327 : : !write(std_out,*)lwork,lrwork
4328 : :
4329 : : ! Solve the problem.
4330 : : ABI_MALLOC(work_sp, (lwork))
4331 : : ABI_MALLOC(rwork_sp, (lrwork))
4332 : :
4333 : : call PCHEEV(jobz, uplo, nn, mat%buffer_cplx, ija__(1), ija__(2), mat%desc, &
4334 : : w, vec%buffer_cplx, ijz__(1), ijz__(2), vec%desc, work_sp, lwork, rwork_sp, lrwork, info)
4335 : : ABI_CHECK(info == 0, sjoin("PCHEEV returned info:", itoa(info)))
4336 : : ABI_FREE(work_sp)
4337 : : ABI_FREE(rwork_sp)
4338 : : #endif
4339 : :
4340 : 0 : end subroutine slkmat_sp_heev
4341 : : !!***
4342 : :
4343 : : !----------------------------------------------------------------------
4344 : :
4345 : : !!****f* m_slk/slkmat_dp_pzheevx
4346 : : !! NAME
4347 : : !! slkmat_dp_pzheevx
4348 : : !!
4349 : : !! FUNCTION
4350 : : !! slkmat_dp_pzheevx computes selected eigenvalues and, optionally, eigenvectors of a complex Hermitian matrix A.
4351 : : !! A * X = lambda * X
4352 : : !!
4353 : : !! INPUTS
4354 : : !! mat=ScaLAPACK matrix (matrix A). Only complex matrices (buffer_cplx) are supported.
4355 : : !!
4356 : : !! vec=The distributed eigenvectors X. Not referenced if JOBZ="N"
4357 : : !!
4358 : : !! JOBZ (global input) CHARACTER*1
4359 : : !! Specifies whether or not to compute the eigenvectors:
4360 : : !! = "N": Compute eigenvalues only.
4361 : : !! = "V": Compute eigenvalues and eigenvectors.
4362 : : !!
4363 : : !! RANGE (global input) CHARACTER*1
4364 : : !! = "A": all eigenvalues will be found.
4365 : : !! = "V": all eigenvalues in the interval [VL,VU] will be found.
4366 : : !! = "I": the IL-th through IU-th eigenvalues will be found.
4367 : : !!
4368 : : !! UPLO (global input) CHARACTER*1
4369 : : !! Specifies whether the upper or lower triangular part of the Hermitian matrix A is stored:
4370 : : !! = "U": Upper triangular
4371 : : !! = "L": Lower triangular
4372 : : !!
4373 : : !! VL (global input) DOUBLE PRECISION
4374 : : !! If RANGE="V",the lower bound of the interval to be searched for eigenvalues. Not referenced if RANGE =
4375 : : !! "A" or "I"
4376 : : !!
4377 : : !! VU (global input) DOUBLE PRECISION
4378 : : !! If RANGE="V", the upper bound of the interval to be searched for eigenvalues. Not referenced if RANGE =
4379 : : !! "A" or "I".
4380 : : !!
4381 : : !! IL (global input) integer
4382 : : !! If RANGE="I", the index (from smallest to largest) of the smallest eigenvalue to be returned. IL >= 1.
4383 : : !! Not referenced if RANGE = "A" or "V".
4384 : : !!
4385 : : !! IU (global input) integer
4386 : : !! If RANGE="I", the index (from smallest to largest) of the largest eigenvalue to be returned. min(IL,N) <=
4387 : : !! IU <= N. Not referenced if RANGE = "A" or "V"
4388 : : !!
4389 : : !! ABSTOL (global input) DOUBLE PRECISION
4390 : : !! If JOBZ="V", setting ABSTOL to PDLAMCH( CONTEXT, "U") yields the most orthogonal eigenvectors.
4391 : : !! The absolute error tolerance for the eigenvalues. An approximate eigenvalue is accepted as converged when
4392 : : !! it is determined to lie in an interval [a,b] of width less than or equal to
4393 : : !!
4394 : : !! ABSTOL + EPS * max( |a|,|b| ) ,
4395 : : !!
4396 : : !! where EPS is the machine precision. If ABSTOL is less than or equal to zero, then EPS*norm(T) will be used
4397 : : !! in its place, where norm(T) is the 1-norm of the tridiagonal matrix obtained by reducing A to tridiagonal form.
4398 : : !! Eigenvalues will be computed most accurately when ABSTOL is set to twice the underflow threshold
4399 : : !! 2*PDLAMCH("S") not zero. If this routine returns with ((MOD(INFO,2).NE.0) .OR. (MOD(INFO/8,2).NE.0)),
4400 : : !! indicating that some eigenvalues or eigenvectors did not converge, try setting ABSTOL to 2*PDLAMCH("S").
4401 : : !!
4402 : : !! OUTPUT
4403 : : !! mene_found= (global output) Total number of eigenvalues found. 0 <= mene_found <= N.
4404 : : !! eigen(N)= (global output) Eigenvalues of A where N is the dimension of M
4405 : : !! On normal exit, the first mene_found entries contain the selected eigenvalues in ascending order.
4406 : : !!
4407 : : !! SIDE EFFECTS
4408 : : !! If JOBZ="V", the local buffer vec%buffer_cplx will contain part of the distributed eigenvectors.
4409 : : !! mat%buffer_cplx is destroyed when the routine returns
4410 : : !!
4411 : : !! SOURCE
4412 : :
4413 : 0 : subroutine slkmat_dp_pzheevx(mat, jobz, range, uplo, vl, vu, il, iu, abstol, vec, mene_found, eigen)
4414 : :
4415 : : !Arguments ------------------------------------
4416 : : class(slkmat_dp_t),intent(inout) :: mat
4417 : : integer,intent(in) :: il, iu
4418 : : integer,intent(out) :: mene_found
4419 : : real(dp),intent(in) :: abstol,vl,vu
4420 : : character(len=*),intent(in) :: jobz,range,uplo
4421 : : class(slkmat_dp_t),intent(inout) :: vec
4422 : : !arrays
4423 : : real(dp),intent(out) :: eigen(*)
4424 : :
4425 : : #ifdef HAVE_LINALG_SCALAPACK
4426 : : !Local variables-------------------------------
4427 : : !scalars
4428 : : integer :: lwork,lrwork,liwork,info,nvec_calc !,ierr
4429 : : real(dp) :: orfac
4430 : : character(len=500) :: msg
4431 : : !arrays
4432 : : !integer :: ibuff(3),max_ibuff(3)
4433 : : integer,allocatable :: iwork(:),iclustr(:),ifail(:)
4434 : : real(dp),allocatable :: rwork(:),gap(:)
4435 : : complex(dpc),allocatable :: work(:)
4436 : : !************************************************************************
4437 : :
4438 : : ABI_CHECK(allocated(mat%buffer_cplx), "buffer_cplx is not allocated!")
4439 : :
4440 : : ! abstol = PDLAMCH(vec%processor%grid%comm,'U')
4441 : :
4442 : : orfac = -one ! Only for eigenvectors: use default value 10d-3.
4443 : : ! Vectors within orfac*norm(A) will be reorthogonalized.
4444 : :
4445 : : ! Allocate the arrays for the results of the calculation
4446 : : ABI_MALLOC(gap, (mat%processor%grid%dims(1) * mat%processor%grid%dims(2)))
4447 : :
4448 : : if (firstchar(jobz, ["V","v"])) then
4449 : : ABI_MALLOC(ifail, (mat%size_global(2)))
4450 : : ABI_MALLOC(iclustr, (2*mat%processor%grid%dims(1) * mat%processor%grid%dims(2)))
4451 : : end if
4452 : :
4453 : : ! Get the optimal size of the work arrays.
4454 : : lwork=-1; lrwork=-1; liwork=-1
4455 : : ABI_MALLOC(work, (1))
4456 : : ABI_MALLOC(iwork, (1))
4457 : : ABI_MALLOC(rwork, (3))
4458 : : ! This is clearly seen in the source in which rwork(1:3) is accessed
4459 : : ! in the calculation of the workspace size.
4460 : :
4461 : : ! prototype
4462 : : !call pzheevx(jobz, range, uplo, n, a, ia, ja, desca, vl, vu, il, iu, abstol, m, nz, w,
4463 : : ! orfac, z, iz, jz, descz, work, lwork, rwork, lrwork, iwork, liwork, ifail, iclustr, gap, info)
4464 : :
4465 : : call PZHEEVX(jobz,range,uplo, mat%size_global(2),mat%buffer_cplx,1,1,mat%desc,&
4466 : : vl,vu,il,iu,abstol,mene_found,nvec_calc,eigen,orfac,&
4467 : : vec%buffer_cplx,1,1,vec%desc,&
4468 : : work,lwork,rwork,lrwork,iwork,liwork,ifail,iclustr,gap,info)
4469 : :
4470 : : ABI_CHECK(info == 0, sjoin("Problem to compute workspace, info:", itoa(info)))
4471 : :
4472 : : lwork = NINT(real(work(1)),kind=dp)
4473 : : lrwork = NINT(rwork(1))
4474 : : liwork = iwork(1)
4475 : :
4476 : : ABI_FREE(work)
4477 : : ABI_FREE(rwork)
4478 : : ABI_FREE(iwork)
4479 : : !
4480 : : ! FROM THE SCALAPACK MAN PAGE:
4481 : : ! The computed eigenvectors may not be orthogonal if the minimal workspace is supplied and ORFAC is too
4482 : : ! small. If you want to guarantee orthogonality (at the cost of potentially poor performance) you should
4483 : : ! add the following to LRWORK: (CLUSTERSIZE-1)*N where CLUSTERSIZE is the number of eigenvalues in the
4484 : : ! largest cluster, where a cluster is defined as a set of close eigenvalues: { W(K),...,W(K+CLUSTERSIZE-1) |
4485 : : ! W(J+1) <= W(J) + ORFAC*2*norm(A) }.
4486 : :
4487 : : if (firstchar(jobz, ["V","v"])) then
4488 : : lrwork = INT( lrwork + mat%size_global(2) *(mat%size_global(2)-1) )
4489 : : end if
4490 : :
4491 : : ! ibuff(1) = lwork
4492 : : ! ibuff(2) = lrwork !INT(lrwork + mat%size_global(2) *(mat%size_global(2)-1)
4493 : : ! ibuff(3) = liwork
4494 : :
4495 : : ! Get the maximum of sizes of the work arrays processor%comm
4496 : : ! call MPI_ALLREDUCE(ibuff,max_ibuff,3,MPI_integer,MPI_MAX,comm,ierr)
4497 : :
4498 : : ! lwork = max_ibuff(1)
4499 : : ! lrwork = max_ibuff(2)
4500 : : ! liwork = max_ibuff(3)
4501 : :
4502 : : ABI_MALLOC(work , (lwork ))
4503 : : ABI_MALLOC(rwork, (lrwork))
4504 : : ABI_MALLOC(iwork, (liwork))
4505 : :
4506 : : ! prototype
4507 : : !call pzheevx(jobz, range, uplo, n, a, ia, ja, desca, vl, vu, il, iu, abstol, m, nz, w,
4508 : : ! orfac, z, iz, jz, descz, work, lwork, rwork, lrwork, iwork, liwork, ifail, iclustr, gap, info)
4509 : :
4510 : : ! Call the scaLAPACK routine.
4511 : : ! write(std_out,*) 'I am using PZHEEVX'
4512 : : call PZHEEVX(jobz,range,uplo, mat%size_global(2),mat%buffer_cplx,1,1,mat%desc,&
4513 : : vl,vu,il,iu,abstol,mene_found,nvec_calc, eigen,orfac,&
4514 : : vec%buffer_cplx,1,1,vec%desc,&
4515 : : work,lwork,rwork,lrwork,iwork,liwork,ifail,iclustr,gap,info)
4516 : :
4517 : : ! Handle possible error.
4518 : : if (info < 0) then
4519 : : write(msg,'(a,i0,a)')" The ", -info, "-th argument of P?HEEVX had an illegal value."
4520 : : if (info == -25) msg = " LRWORK is too small to compute all the eigenvectors requested, no computation is performed"
4521 : : ABI_ERROR(msg)
4522 : : end if
4523 : :
4524 : : if (info > 0) then
4525 : : write(msg,'(a,i0)') " P?HEEVX returned info: ",info
4526 : : call wrtout(std_out, msg)
4527 : : if (MOD(info, 2) /= 0)then
4528 : : write(msg,'(3a)')&
4529 : : " One or more eigenvectors failed to converge. ",ch10,&
4530 : : " Their indices are stored in IFAIL. Ensure ABSTOL=2.0*PDLAMCH('U')"
4531 : : call wrtout(std_out, msg)
4532 : : end if
4533 : : if (MOD(info / 2, 2) /= 0) then
4534 : : write(msg,'(5a)')&
4535 : : " Eigenvectors corresponding to one or more clusters of eigenvalues ",ch10,&
4536 : : " could not be reorthogonalized because of insufficient workspace. ",ch10,&
4537 : : " The indices of the clusters are stored in the array ICLUSTR."
4538 : : call wrtout(std_out, msg)
4539 : : end if
4540 : : if (MOD(info / 4, 2) /= 0) then
4541 : : write(msg,'(3a)')" Space limit prevented PZHEEVX from computing all of the eigenvectors between VL and VU. ",ch10,&
4542 : : " The number of eigenvectors computed is returned in NZ."
4543 : : call wrtout(std_out, msg)
4544 : : end if
4545 : : if (MOD(info / 8, 2) /= 0) then
4546 : : call wrtout(std_out, "PZSTEBZ failed to compute eigenvalues. Ensure ABSTOL=2.0*PDLAMCH('U')")
4547 : : end if
4548 : : ABI_ERROR("Cannot continue")
4549 : : end if
4550 : :
4551 : : ! Check the number of eigenvalues found wrt to the number of vectors calculated.
4552 : : if (firstchar(jobz, ['V','v']) .and. mene_found /= nvec_calc) then
4553 : : write(msg,'(5a)') &
4554 : : " The user supplied insufficient space and PZHEEVX is not able to detect this before beginning computation. ",ch10,&
4555 : : " To get all the eigenvectors requested, the user must supply both sufficient space to hold the ",ch10,&
4556 : : " eigenvectors in Z (M .LE. DESCZ(N_)) and sufficient workspace to compute them. "
4557 : : ABI_ERROR(msg)
4558 : : end if
4559 : :
4560 : : ABI_FREE(work)
4561 : : ABI_FREE(rwork)
4562 : : ABI_FREE(iwork)
4563 : : ABI_FREE(gap)
4564 : : ABI_SFREE(ifail)
4565 : : ABI_SFREE(iclustr)
4566 : : #endif
4567 : :
4568 : 0 : end subroutine slkmat_dp_pzheevx
4569 : : !!***
4570 : :
4571 : : !----------------------------------------------------------------------
4572 : :
4573 : : !!****f* m_slk/slkmat_dp_pzhegvx
4574 : : !! NAME
4575 : : !! slkmat_dp_pzhegvx
4576 : : !!
4577 : : !! FUNCTION
4578 : : !! slkmat_dp_pzhegvx provides an object-oriented interface to the ScaLAPACK routine PZHEGVX that
4579 : : !! computes selected eigenvalues and, optionally, eigenvectors of a complex generalized
4580 : : !! Hermitian-definite eigenproblem, of the form
4581 : : !! sub( A )*x=(lambda)*sub( B )*x, sub( A )*sub( B )x=(lambda)*x, or sub( B )*sub( A )*x=(lambda)*x.
4582 : : !! Here sub( A ) denoting A( IA:IA+N-1, JA:JA+N-1 ) is assumed to be
4583 : : !! Hermitian, and sub( B ) denoting B( IB:IB+N-1, JB:JB+N-1 ) is assumed
4584 : : !! to be Hermitian positive definite.
4585 : : !!
4586 : : !! INPUTS
4587 : : !! Slk_matA<slkmat_dp_t>=ScaLAPACK matrix (matrix A)
4588 : : !! Slk_matB<slkmat_dp_t>=ScaLAPACK matrix (matrix B)
4589 : : !! Slk_vec<slkmat_dp_t>=The distributed eigenvectors X. Not referenced if JOBZ="N"
4590 : : !!
4591 : : !! IBtype (global input) integer
4592 : : !! Specifies the problem type to be solved:
4593 : : !! = 1: sub( A )*x = (lambda)*sub( B )*x
4594 : : !! = 2: sub( A )*sub( B )*x = (lambda)*x
4595 : : !! = 3: sub( B )*sub( A )*x = (lambda)*x
4596 : : !!
4597 : : !! JOBZ (global input) CHARACTER*1
4598 : : !! Specifies whether or not to compute the eigenvectors:
4599 : : !! = "N": Compute eigenvalues only.
4600 : : !! = "V": Compute eigenvalues and eigenvectors.
4601 : : !!
4602 : : !! RANGE (global input) CHARACTER*1
4603 : : !! = "A": all eigenvalues will be found.
4604 : : !! = "V": all eigenvalues in the interval [VL,VU] will be found.
4605 : : !! = "I": the IL-th through IU-th eigenvalues will be found.
4606 : : !!
4607 : : !! UPLO (global input) CHARACTER*1
4608 : : !! Specifies whether the upper or lower triangular part of the Hermitian matrix sub(A) and sub(B) is stored:
4609 : : !! = "U": Upper triangular
4610 : : !! = "L": Lower triangular
4611 : : !!
4612 : : !! VL (global input) DOUBLE PRECISION
4613 : : !! If RANGE="V",the lower bound of the interval to be searched for eigenvalues. Not referenced if RANGE =
4614 : : !! "A" or "I"
4615 : : !!
4616 : : !! VU (global input) DOUBLE PRECISION
4617 : : !! If RANGE="V", the upper bound of the interval to be searched for eigenvalues. Not referenced if RANGE =
4618 : : !! "A" or "I".
4619 : : !!
4620 : : !! IL (global input) integer
4621 : : !! If RANGE="I", the index (from smallest to largest) of the smallest eigenvalue to be returned. IL >= 1.
4622 : : !! Not referenced if RANGE = "A" or "V".
4623 : : !!
4624 : : !! IU (global input) integer
4625 : : !! If RANGE="I", the index (from smallest to largest) of the largest eigenvalue to be returned. min(IL,N) <=
4626 : : !! IU <= N. Not referenced if RANGE = "A" or "V"
4627 : : !!
4628 : : !! ABSTOL (global input) DOUBLE PRECISION
4629 : : !! If JOBZ="V", setting ABSTOL to PDLAMCH( CONTEXT, "U") yields the most orthogonal eigenvectors.
4630 : : !! The absolute error tolerance for the eigenvalues. An approximate eigenvalue is accepted as converged when
4631 : : !! it is determined to lie in an interval [a,b] of width less than or equal to
4632 : : !!
4633 : : !! ABSTOL + EPS * max( |a|,|b| ) ,
4634 : : !!
4635 : : !! where EPS is the machine precision. If ABSTOL is less than or equal to zero, then EPS*norm(T) will be used
4636 : : !! in its place, where norm(T) is the 1-norm of the tridiagonal matrix obtained by reducing A to tridiagonal form.
4637 : : !! Eigenvalues will be computed most accurately when ABSTOL is set to twice the underflow threshold
4638 : : !! 2*PDLAMCH("S") not zero. If this routine returns with ((MOD(INFO,2).NE.0) .OR. (MOD(INFO/8,2).NE.0)),
4639 : : !! indicating that some eigenvalues or eigenvectors did not converge, try setting ABSTOL to 2*PDLAMCH("S").
4640 : : !!
4641 : : !! OUTPUT
4642 : : !! mene_found= (global output) Total number of eigenvalues found. 0 <= mene_found <= N.
4643 : : !!
4644 : : !! eigen(N)= (global output) Eigenvalues of A where N is the dimension of M
4645 : : !! On normal exit, the first mene_found entries contain the selected eigenvalues in ascending order.
4646 : : !!
4647 : : !! SIDE EFFECTS
4648 : : !! Slk_vec<slkmat_dp_t>:
4649 : : !! %buffer_cplx local output (global dimension (N,N)
4650 : : !! If JOBZ = 'V', then on normal exit the first M columns of Z
4651 : : !! contain the orthonormal eigenvectors of the matrix
4652 : : !! corresponding to the selected eigenvalues.
4653 : : !! If JOBZ = 'N', then Z is not referenced.
4654 : : !!
4655 : : !! Slk_matA<slkmat_dp_t>:
4656 : : !! %buffer_cplx
4657 : : !! (local input/local output) complex(DP) pointer into the
4658 : : !! local memory to an array of dimension (LLD_A, LOCc(JA+N-1)).
4659 : : !! On entry, this array contains the local pieces of the
4660 : : !! N-by-N Hermitian distributed matrix sub( A ). If UPLO = 'U',
4661 : : !! the leading N-by-N upper triangular part of sub( A ) contains
4662 : : !! the upper triangular part of the matrix. If UPLO = 'L', the
4663 : : !! leading N-by-N lower triangular part of sub( A ) contains
4664 : : !! the lower triangular part of the matrix.
4665 : : !!
4666 : : !! On exit, if JOBZ = 'V', then if INFO = 0, sub( A ) contains
4667 : : !! the distributed matrix Z of eigenvectors. The eigenvectors
4668 : : !! are normalized as follows:
4669 : : !! if IBtype = 1 or 2, Z**H*sub( B )*Z = I;
4670 : : !! if IBtype = 3, Z**H*inv( sub( B ) )*Z = I.
4671 : : !! If JOBZ = 'N', then on exit the upper triangle (if UPLO='U')
4672 : : !! or the lower triangle (if UPLO='L') of sub( A ), including
4673 : : !! the diagonal, is destroyed.
4674 : : !!
4675 : : !! Slk_matB=
4676 : : !! %buffer_cplx
4677 : : !! (local input/local output) complex*(DP) pointer into the
4678 : : !! local memory to an array of dimension (LLD_B, LOCc(JB+N-1)).
4679 : : !! On entry, this array contains the local pieces of the
4680 : : !! N-by-N Hermitian distributed matrix sub( B ). If UPLO = 'U',
4681 : : !! the leading N-by-N upper triangular part of sub( B ) contains
4682 : : !! the upper triangular part of the matrix. If UPLO = 'L', the
4683 : : !! leading N-by-N lower triangular part of sub( B ) contains
4684 : : !! the lower triangular part of the matrix.
4685 : : !!
4686 : : !! On exit, if INFO <= N, the part of sub( B ) containing the
4687 : : !! matrix is overwritten by the triangular factor U or L from
4688 : : !! the Cholesky factorization sub( B ) = U**H*U or
4689 : : !! sub( B ) = L*L**H.
4690 : : !!
4691 : : !! SOURCE
4692 : :
4693 : 0 : subroutine slkmat_dp_pzhegvx(Slk_matA, ibtype, jobz, range, uplo, Slk_matB, vl, vu, il, iu, abstol, Slk_vec, mene_found, eigen)
4694 : :
4695 : : !Arguments ------------------------------------
4696 : : class(slkmat_dp_t),intent(inout) :: Slk_matA
4697 : : integer,intent(in) :: il,iu,ibtype
4698 : : integer,intent(out) :: mene_found
4699 : : real(dp),intent(in) :: abstol,vl,vu
4700 : : character(len=*),intent(in) :: jobz,range,uplo
4701 : : class(slkmat_dp_t),intent(inout) :: Slk_matB
4702 : : class(slkmat_dp_t),intent(inout) :: Slk_vec
4703 : : !arrays
4704 : : real(dp),intent(out) :: eigen(*)
4705 : :
4706 : : #ifdef HAVE_LINALG_SCALAPACK
4707 : : !Local variables-------------------------------
4708 : : !scalars
4709 : : integer :: lwork,lrwork,liwork,info,nvec_calc !,ierr
4710 : : real(dp) :: orfac
4711 : : logical :: ltest
4712 : : character(len=500) :: msg
4713 : : !arrays
4714 : : !integer :: ibuff(3),max_ibuff(3)
4715 : : integer :: desca(DLEN_),descb(DLEN_),descz(DLEN_)
4716 : : integer,allocatable :: iwork(:),iclustr(:),ifail(:)
4717 : : real(dp),allocatable :: rwork(:),gap(:)
4718 : : complex(dpc),allocatable :: work(:)
4719 : : !************************************************************************
4720 : :
4721 : : ABI_CHECK(allocated(Slk_matA%buffer_cplx), "buffer_cplx is not allocated!")
4722 : :
4723 : : ! abstol = PDLAMCH(Slk_vecprocessor%grid%comm,'U')
4724 : :
4725 : : orfac = -one ! Only for eigenvectors: use default value 10d-3.
4726 : : ! Vectors within orfac*norm(A) will be reorthogonalized.
4727 : :
4728 : : ! ======================
4729 : : ! Alignment requirements
4730 : : ! ======================
4731 : : ! The distributed submatrices A(IA:*, JA:*), C(IC:IC+M-1,JC:JC+N-1),
4732 : : ! and B( IB:IB+N-1, JB:JB+N-1 ) must verify some alignment properties,
4733 : :
4734 : : desca = Slk_matA%desc
4735 : : descb = Slk_matB%desc
4736 : : if (firstchar(jobz, ["V", "v"])) then
4737 : : descz = Slk_vec%desc
4738 : : else
4739 : : descz = Slk_matA%desc
4740 : : end if
4741 : :
4742 : : ltest = .TRUE.
4743 : : ltest = ltest .and. (DESCA(MB_) == DESCA(NB_))
4744 : : !IA = IB = IZ
4745 : : !JA = IB = JZ
4746 : : ltest = ltest .and.ALL(DESCA(M_ ) == [DESCB(M_ ), DESCZ(M_ )])
4747 : : ltest = ltest .and.ALL(DESCA(N_ ) == [DESCB(N_ ), DESCZ(N_ )])
4748 : : ltest = ltest .and.ALL(DESCA(MB_ ) == [DESCB(MB_ ), DESCZ(MB_ )])
4749 : : ltest = ltest .and.ALL(DESCA(NB_ ) == [DESCB(NB_ ), DESCZ(NB_ )])
4750 : : ltest = ltest .and.ALL(DESCA(RSRC_) == [DESCB(RSRC_), DESCZ(RSRC_)])
4751 : : ltest = ltest .and.ALL(DESCA(CSRC_) == [DESCB(CSRC_), DESCZ(CSRC_)])
4752 : : !MOD( IA-1, DESCA( MB_ ) ) = 0
4753 : : !MOD( JA-1, DESCA( NB_ ) ) = 0
4754 : : !MOD( IB-1, DESCB( MB_ ) ) = 0
4755 : : !MOD( JB-1, DESCB( NB_ ) ) = 0
4756 : :
4757 : : if (.not.ltest) then
4758 : : ABI_ERROR("Alignment requirements not satisfied, check the caller")
4759 : : end if
4760 : :
4761 : : !Allocate the arrays for the results of the calculation
4762 : : ABI_MALLOC(gap, (Slk_matA%processor%grid%dims(1) * Slk_matA%processor%grid%dims(2)))
4763 : :
4764 : : if (firstchar(jobz, ["V","v"])) then
4765 : : ABI_MALLOC(ifail,(Slk_matA%size_global(2)))
4766 : : ABI_MALLOC(iclustr,( 2*Slk_matA%processor%grid%dims(1) * Slk_matA%processor%grid%dims(2)))
4767 : : else
4768 : : ABI_MALLOC(ifail,(1))
4769 : : end if
4770 : :
4771 : : ! Get the optimal size of the work arrays.
4772 : : lwork=-1; lrwork=-1; liwork=-1
4773 : : ABI_MALLOC(work,(1))
4774 : : ABI_MALLOC(iwork,(1))
4775 : : ABI_MALLOC(rwork,(3))
4776 : : ! This is clearly seen in the source in which rwork(1:3) is accessed
4777 : : ! in the calculation of the workspace size.
4778 : :
4779 : : call pzhegvx(ibtype,jobz,range,uplo, Slk_matA%size_global(2),Slk_matA%buffer_cplx,1,1,Slk_matA%desc,&
4780 : : Slk_matB%buffer_cplx,1,1,Slk_matB%desc,&
4781 : : vl,vu,il,iu,abstol,mene_found,nvec_calc,eigen,orfac,&
4782 : : Slk_vec%buffer_cplx,1,1,Slk_vec%desc,&
4783 : : work,lwork,rwork,lrwork,iwork,liwork,ifail,iclustr,gap,info)
4784 : :
4785 : : ABI_CHECK(info == 0, sjoin("Problem to compute workspace, info:", itoa(info)))
4786 : :
4787 : : lwork = NINT(real(work(1)),kind=dp)
4788 : : lrwork = NINT(rwork(1))
4789 : : liwork = iwork(1)
4790 : :
4791 : : ABI_FREE(work)
4792 : : ABI_FREE(rwork)
4793 : : ABI_FREE(iwork)
4794 : :
4795 : : !FROM THE SCALAPACK MAN PAGE:
4796 : : !The computed eigenvectors may not be orthogonal if the minimal workspace is supplied and ORFAC is too
4797 : : !small. If you want to guarantee orthogonality (at the cost of potentially poor performance) you should
4798 : : !add the following to LRWORK: (CLUSTERSIZE-1)*N where CLUSTERSIZE is the number of eigenvalues in the
4799 : : !largest cluster, where a cluster is defined as a set of close eigenvalues: { W(K),...,W(K+CLUSTERSIZE-1) |
4800 : : !W(J+1) <= W(J) + ORFAC*2*norm(A) }.
4801 : :
4802 : : if (firstchar(jobz, ["V","v"])) then
4803 : : lrwork = INT( lrwork + Slk_matA%size_global(2) *(Slk_matA%size_global(2)-1) )
4804 : : end if
4805 : :
4806 : : !ibuff(1) = lwork
4807 : : !ibuff(2) = lrwork !INT(lrwork + Slk_matA%size_global(2) *(Slk_matA%size_global(2)-1)
4808 : : !ibuff(3) = liwork
4809 : :
4810 : : !Get the maximum of sizes of the work arrays processor%comm
4811 : : !call MPI_ALLREDUCE(ibuff,max_ibuff,3,MPI_integer,MPI_MAX,comm,ierr)
4812 : :
4813 : : !lwork = max_ibuff(1)
4814 : : !lrwork = max_ibuff(2)
4815 : : !liwork = max_ibuff(3)
4816 : :
4817 : : ABI_MALLOC(work , (lwork ))
4818 : : ABI_MALLOC(rwork, (lrwork))
4819 : : ABI_MALLOC(iwork, (liwork))
4820 : :
4821 : : ! Call the scaLAPACK routine.
4822 : : ! write(std_out,*) 'I am using PZHEGVX'
4823 : : call pzhegvx(ibtype,jobz,range,uplo, Slk_matA%size_global(2),Slk_matA%buffer_cplx,1,1,Slk_matA%desc,&
4824 : : Slk_matB%buffer_cplx,1,1,Slk_matB%desc,&
4825 : : vl,vu,il,iu,abstol,mene_found,nvec_calc, eigen,orfac,&
4826 : : Slk_vec%buffer_cplx,1,1,Slk_vec%desc,&
4827 : : work,lwork,rwork,lrwork,iwork,liwork,ifail,iclustr,gap,info)
4828 : :
4829 : : ! Handle the possible error.
4830 : : if (info < 0) then
4831 : : write(msg,'(a,i0,a)')" The ",-info,"-th argument of PZHEGVX had an illegal value."
4832 : : if (info==-25) msg = " LRWORK is too small to compute all the eigenvectors requested, no computation is performed"
4833 : : ABI_ERROR(msg)
4834 : : end if
4835 : :
4836 : : if (info > 0) then
4837 : : write(msg,'(a,i0)') " PZHEGVX returned info: ",info
4838 : : call wrtout(std_out, msg)
4839 : : if (MOD(info,2)/=0)then
4840 : : write(msg,'(3a)')&
4841 : : " One or more eigenvectors failed to converge. ",ch10,&
4842 : : " Their indices are stored in IFAIL. Ensure ABSTOL=2.0*PDLAMCH('U')"
4843 : : call wrtout(std_out, msg)
4844 : : end if
4845 : : if (MOD(info / 2, 2) /= 0) then
4846 : : write(msg,'(5a)')&
4847 : : " Eigenvectors corresponding to one or more clusters of eigenvalues ",ch10,&
4848 : : " could not be reorthogonalized because of insufficient workspace. ",ch10,&
4849 : : " The indices of the clusters are stored in the array ICLUSTR."
4850 : : call wrtout(std_out, msg)
4851 : : end if
4852 : : if (MOD(info / 4, 2) /= 0) then
4853 : : write(msg,'(3a)')&
4854 : : " Space limit prevented PZHEGVX from computing all of the eigenvectors between VL and VU. ",ch10,&
4855 : : " The number of eigenvectors computed is returned in NZ."
4856 : : call wrtout(std_out, msg)
4857 : : end if
4858 : : if (MOD(info / 8, 2) /= 0) then
4859 : : msg = " PZSTEBZ failed to compute eigenvalues. Ensure ABSTOL=2.0*PDLAMCH('U')"
4860 : : call wrtout(std_out, msg)
4861 : : end if
4862 : : if (MOD(info / 16, 2) /= 0) then
4863 : : write(msg,'(3a)')&
4864 : : " B was not positive definite.",ch10,&
4865 : : " IFAIL(1) indicates the order of the smallest minor which is not positive definite."
4866 : : call wrtout(std_out, msg)
4867 : : end if
4868 : : ABI_ERROR("Cannot continue")
4869 : : end if
4870 : :
4871 : : ! Check the number of eigenvalues found wrt to the number of vectors calculated.
4872 : : if ( firstchar(jobz, ['V','v']) .and. mene_found/=nvec_calc) then
4873 : : write(msg,'(5a)')&
4874 : : " The user supplied insufficient space and PZHEGVX is not able to detect this before beginning computation. ",ch10,&
4875 : : " To get all the eigenvectors requested, the user must supply both sufficient space to hold the ",ch10,&
4876 : : " eigenvectors in Z (M .LE. DESCZ(N_)) and sufficient workspace to compute them. "
4877 : : ABI_ERROR(msg)
4878 : : end if
4879 : :
4880 : : ABI_FREE(work)
4881 : : ABI_FREE(rwork)
4882 : : ABI_FREE(iwork)
4883 : : ABI_FREE(gap)
4884 : : ABI_FREE(ifail)
4885 : : ABI_SFREE(iclustr)
4886 : : #endif
4887 : :
4888 : 0 : end subroutine slkmat_dp_pzhegvx
4889 : : !!***
4890 : :
4891 : : !----------------------------------------------------------------------
4892 : :
4893 : : !!****f* m_slk/basemat_invert
4894 : : !! NAME
4895 : : !! basemat_invert
4896 : : !!
4897 : : !! FUNCTION
4898 : : !! Compute the inverse of a complex matrix.
4899 : : !! Uses LU factorization (P?GETRF + P?GETRI). Requires square blocks (MB == NB).
4900 : : !! Real matrices are not supported.
4901 : : !!
4902 : : !! SIDE EFFECTS
4903 : : !! mat
4904 : : !! In input, the matrix to invert.
4905 : : !! In output the matrix inverted and distributed among the nodes.
4906 : : !!
4907 : : !! SOURCE
4908 : :
4909 : 0 : subroutine basemat_invert(mat)
4910 : :
4911 : : !Arguments ------------------------------------
4912 : : class(basemat_t),intent(inout) :: mat
4913 : :
4914 : : #ifdef HAVE_LINALG_SCALAPACK
4915 : : !Local variables ------------------------------
4916 : : !scalars
4917 : : integer :: lwork,info,ipiv_size,liwork
4918 : : !array
4919 : : integer,allocatable :: ipiv(:), iwork(:)
4920 : : complex(dp),allocatable :: work_dp(:)
4921 : : complex(sp),allocatable :: work_sp(:)
4922 : : !************************************************************************
4923 : :
4924 : : if (mat%desc(MB_) /= mat%desc(NB_)) then
4925 : : ABI_ERROR("PZGETRF requires square block decomposition i.e MB_A = NB_A.")
4926 : : end if
4927 : :
4928 : : ipiv_size = my_locr(mat) + mat%desc(MB_)
4929 : : ABI_MALLOC(ipiv, (ipiv_size))
4930 : :
4931 : : select type (mat)
4932 : : class is (slkmat_dp_t)
4933 : : if (allocated(mat%buffer_cplx)) then
4934 : : ! P * L * U Factorization.
4935 : : call PZGETRF(mat%size_global(1), mat%size_global(2), mat%buffer_cplx, 1, 1, mat%desc,ipiv, info)
4936 : : ABI_CHECK(info == 0, sjoin(" PZGETRF returned info:", itoa(info)))
4937 : :
4938 : : ! Get optimal size of workspace for PZGETRI.
4939 : : lwork = -1; liwork = -1
4940 : : ABI_MALLOC(work_dp,(1))
4941 : : ABI_MALLOC(iwork,(1))
4942 : :
4943 : : call PZGETRI(mat%size_global(1), mat%buffer_cplx, 1, 1, mat%desc, ipiv, work_dp, lwork, iwork, liwork, info)
4944 : : ABI_CHECK(info == 0, "PZGETRI: Error while computing workspace size")
4945 : :
4946 : : lwork = nint(real(work_dp(1))); liwork=iwork(1)
4947 : : ABI_FREE(work_dp)
4948 : : ABI_FREE(iwork)
4949 : :
4950 : : ! Solve the problem.
4951 : : ABI_MALLOC(work_dp, (lwork))
4952 : : ABI_MALLOC(iwork, (liwork))
4953 : :
4954 : : call PZGETRI(mat%size_global(1), mat%buffer_cplx, 1, 1, mat%desc, ipiv, work_dp, lwork, iwork, liwork, info)
4955 : : ABI_CHECK(info == 0, sjoin("PZGETRI returned info:", itoa(info)))
4956 : : ABI_FREE(work_dp)
4957 : :
4958 : : else if (allocated(mat%buffer_real)) then
4959 : : ABI_ERROR("Inversion for real matrices not coded!")
4960 : : end if
4961 : :
4962 : : class is (slkmat_sp_t)
4963 : : if (allocated(mat%buffer_cplx)) then
4964 : : ! P * L * U Factorization.
4965 : : call PCGETRF(mat%size_global(1), mat%size_global(2), mat%buffer_cplx, 1, 1, mat%desc,ipiv, info)
4966 : : ABI_CHECK(info == 0, sjoin(" PCGETRF returned info:", itoa(info)))
4967 : :
4968 : : ! Get optimal size of workspace for PZGETRI.
4969 : : lwork = -1; liwork = -1
4970 : : ABI_MALLOC(work_sp,(1))
4971 : : ABI_MALLOC(iwork,(1))
4972 : :
4973 : : call PCGETRI(mat%size_global(1), mat%buffer_cplx, 1, 1, mat%desc, ipiv, work_sp, lwork, iwork, liwork, info)
4974 : : ABI_CHECK(info == 0, "PZGETRI: Error while computing workspace size")
4975 : :
4976 : : lwork = nint(real(work_sp(1))); liwork=iwork(1)
4977 : : ABI_FREE(work_sp)
4978 : : ABI_FREE(iwork)
4979 : :
4980 : : ! Solve the problem.
4981 : : ABI_MALLOC(work_sp, (lwork))
4982 : : ABI_MALLOC(iwork, (liwork))
4983 : :
4984 : : call PCGETRI(mat%size_global(1), mat%buffer_cplx, 1, 1, mat%desc, ipiv, work_sp, lwork, iwork, liwork, info)
4985 : : ABI_CHECK(info == 0, sjoin("PZGETRI returned info:", itoa(info)))
4986 : : ABI_FREE(work_sp)
4987 : :
4988 : : else if (allocated(mat%buffer_real)) then
4989 : : ABI_ERROR("Inversion for real matrices not coded!")
4990 : : end if
4991 : :
4992 : : class default
4993 : : ABI_ERROR("Wrong class")
4994 : : end select
4995 : :
4996 : : ABI_FREE(iwork)
4997 : : ABI_FREE(ipiv)
4998 : : #endif
4999 : :
5000 : 0 : end subroutine basemat_invert
5001 : : !!***
5002 : :
5003 : : !----------------------------------------------------------------------
5004 : :
5005 : : !!****f* m_slk/slkmat_dp_hpd_invert
5006 : : !! NAME
5007 : : !! slkmat_dp_hpd_invert
5008 : : !!
5009 : : !! FUNCTION
5010 : : !! Compute the inverse of an Hermitian positive definite matrix (Cholesky factorization).
5011 : : !! Only complex matrices (buffer_cplx) are supported.
5012 : : !!
5013 : : !! INPUTS
5014 : : !! uplo: global input
5015 : : !! = 'U': Upper triangle of sub( A ) is stored;
5016 : : !! = 'L': Lower triangle of sub( A ) is stored.
5017 : : !! [full]: If full PBLAS matrix is needed. Default: True
5018 : : !!
5019 : : !! SIDE EFFECTS
5020 : : !! mat= The object storing the local buffer, the array descriptor, the context, etc.
5021 : : !! On entry, this array contains the local pieces of the N-by-N Hermitian distributed matrix sub( A ) to be factored.
5022 : : !! If UPLO = 'U', the leading N-by-N upper triangular part of sub( A ) contains the upper triangular part of the matrix,
5023 : : !! and its strictly lower triangular part is not referenced.
5024 : : !! If UPLO = 'L', the leading N-by-N lower triangular part of sub( A ) contains the lower triangular part of the distribu-
5025 : : !! ted matrix, and its strictly upper triangular part is not referenced.
5026 : : !! On exit, the local pieces of the upper or lower triangle of the (Hermitian) inverse of sub( A )
5027 : : !!
5028 : : !! SOURCE
5029 : :
5030 : 0 : subroutine slkmat_dp_hpd_invert(mat, uplo, full)
5031 : :
5032 : : !Arguments ------------------------------------
5033 : : class(slkmat_dp_t),intent(inout) :: mat
5034 : : character(len=*),intent(in) :: uplo
5035 : : logical,optional,intent(in) :: full
5036 : :
5037 : : #ifdef HAVE_LINALG_SCALAPACK
5038 : : !Local variables ------------------------------
5039 : : !scalars
5040 : : integer :: info, mm, il1, il2, iglob1, iglob2
5041 : : type(slkmat_dp_t) :: work_mat
5042 : : logical :: full__
5043 : : !************************************************************************
5044 : :
5045 : : ABI_CHECK(allocated(mat%buffer_cplx), "buffer_cplx not allocated")
5046 : :
5047 : : ! ZPOTRF computes the Cholesky factorization of a complex Hermitian positive definite.
5048 : : ! A = U**H * U, if UPLO = 'U', or
5049 : : ! A = L * L**H, if UPLO = 'L',
5050 : : mm = mat%size_global(1)
5051 : : call PZPOTRF(uplo, mm, mat%buffer_cplx, 1, 1, mat%desc, info)
5052 : : ABI_CHECK(info == 0, sjoin("PZPOTRF returned info:", itoa(info)))
5053 : :
5054 : : ! PZPOTRI computes the inverse of a complex Hermitian positive definite
5055 : : ! distributed matrix sub( A ) = A(IA:IA+N-1,JA:JA+N-1) using the
5056 : : ! Cholesky factorization sub( A ) = U**H*U or L*L**H computed by PZPOTRF.
5057 : : call PZPOTRI(uplo, mm, mat%buffer_cplx, 1, 1, mat%desc, info)
5058 : : ABI_CHECK(info == 0, sjoin("PZPOTRI returned info:", itoa(info)))
5059 : :
5060 : : full__ = .True.; if (present(full)) full__ = full
5061 : : if (full__) then
5062 : : ! Only the uplo part contains the inverse so we need to fill the other triangular part.
5063 : : ! 1) Fill the missing triangle with zeros and copy results to work_mat
5064 : : ! 2) Call pzgeadd to compute: sub(C) := beta*sub(C) + alpha*op(sub(A))
5065 : : ! 3) Divide diagonal elements by two.
5066 : :
5067 : : do il2=1,mat%size_local(2)
5068 : : iglob2 = mat%loc2gcol(il2)
5069 : : do il1=1,mat%size_local(1)
5070 : : iglob1 = mat%loc2grow(il1)
5071 : : if (uplo == "L" .and. iglob2 > iglob1) mat%buffer_cplx(il1, il2) = zero
5072 : : if (uplo == "U" .and. iglob2 < iglob1) mat%buffer_cplx(il1, il2) = zero
5073 : : end do
5074 : : end do
5075 : :
5076 : : call mat%copy(work_mat, empty=.False.)
5077 : :
5078 : : ! call pzgeadd(trans, m, n, alpha, a, ia, ja, desca, beta, c, ic, jc, descc)
5079 : : ! sub(C) := beta*sub(C) + alpha*op(sub(A))
5080 : : call pzgeadd("C", mm, mm, cone, work_mat%buffer_cplx, 1, 1, work_mat%desc, &
5081 : : cone, mat%buffer_cplx, 1, 1, mat%desc)
5082 : : call work_mat%free()
5083 : :
5084 : : do il2=1,mat%size_local(2)
5085 : : iglob2 = mat%loc2gcol(il2)
5086 : : do il1=1,mat%size_local(1)
5087 : : iglob1 = mat%loc2grow(il1)
5088 : : if (iglob2 == iglob1) mat%buffer_cplx(il1, il2) = half * mat%buffer_cplx(il1, il2)
5089 : : end do
5090 : : end do
5091 : : end if ! full__
5092 : : #endif
5093 : :
5094 : 0 : end subroutine slkmat_dp_hpd_invert
5095 : : !!***
5096 : :
5097 : : !----------------------------------------------------------------------
5098 : :
5099 : : !!****f* m_slk/slkmat_sp_hpd_invert
5100 : : !! NAME
5101 : : !! slkmat_sp_hpd_invert
5102 : : !!
5103 : : !! FUNCTION
5104 : : !! Compute the inverse of an Hermitian positive definite matrix (Cholesky factorization).
5105 : : !! Only complex matrices (buffer_cplx) are supported.
5106 : : !!
5107 : : !! INPUTS
5108 : : !! uplo: global input
5109 : : !! = 'U': Upper triangle of sub( A ) is stored;
5110 : : !! = 'L': Lower triangle of sub( A ) is stored.
5111 : : !! [full]: If full PBLAS matrix is needed. Default: True
5112 : : !!
5113 : : !! SIDE EFFECTS
5114 : : !! mat= The object storing the local buffer, the array descriptor, the context, etc.
5115 : : !! On entry, this array contains the local pieces of the N-by-N Hermitian distributed matrix sub( A ) to be factored.
5116 : : !! If UPLO = 'U', the leading N-by-N upper triangular part of sub( A ) contains the upper triangular part of the matrix,
5117 : : !! and its strictly lower triangular part is not referenced.
5118 : : !! If UPLO = 'L', the leading N-by-N lower triangular part of sub( A ) contains the lower triangular part of the distribu-
5119 : : !! ted matrix, and its strictly upper triangular part is not referenced.
5120 : : !! On exit, the local pieces of the upper or lower triangle of the (Hermitian) inverse of sub( A )
5121 : : !!
5122 : : !! SOURCE
5123 : :
5124 : 0 : subroutine slkmat_sp_hpd_invert(mat, uplo, full)
5125 : :
5126 : : !Arguments ------------------------------------
5127 : : class(slkmat_sp_t),intent(inout) :: mat
5128 : : character(len=*),intent(in) :: uplo
5129 : : logical,optional,intent(in) :: full
5130 : :
5131 : : #ifdef HAVE_LINALG_SCALAPACK
5132 : : !Local variables ------------------------------
5133 : : !scalars
5134 : : integer :: info, mm, il1, il2, iglob1, iglob2
5135 : : type(slkmat_sp_t) :: work_mat
5136 : : logical :: full__
5137 : : !************************************************************************
5138 : :
5139 : : ABI_CHECK(allocated(mat%buffer_cplx), "buffer_cplx not allocated")
5140 : :
5141 : : ! ZPOTRF computes the Cholesky factorization of a complex Hermitian positive definite.
5142 : : ! A = U**H * U, if UPLO = 'U', or
5143 : : ! A = L * L**H, if UPLO = 'L',
5144 : : mm = mat%size_global(1)
5145 : : call PCPOTRF(uplo, mm, mat%buffer_cplx, 1, 1, mat%desc, info)
5146 : : ABI_CHECK(info == 0, sjoin("PCPOTRF returned info:", itoa(info)))
5147 : :
5148 : : ! PZPOTRI computes the inverse of a complex Hermitian positive definite
5149 : : ! distributed matrix sub( A ) = A(IA:IA+N-1,JA:JA+N-1) using the
5150 : : ! Cholesky factorization sub( A ) = U**H*U or L*L**H computed by PZPOTRF.
5151 : : call PCPOTRI(uplo, mm, mat%buffer_cplx, 1, 1, mat%desc, info)
5152 : : ABI_CHECK(info == 0, sjoin("PCPOTRI returned info:", itoa(info)))
5153 : :
5154 : : full__ = .True.; if (present(full)) full__ = full
5155 : : if (full__) then
5156 : : ! Only the uplo part contains the inverse so we need to fill the other triangular part.
5157 : : ! 1) Fill the missing triangle with zeros and copy results to work_mat
5158 : : ! 2) Call pzgeadd to compute: sub(C) := beta*sub(C) + alpha*op(sub(A))
5159 : : ! 3) Divide diagonal elements by two.
5160 : :
5161 : : do il2=1,mat%size_local(2)
5162 : : iglob2 = mat%loc2gcol(il2)
5163 : : do il1=1,mat%size_local(1)
5164 : : iglob1 = mat%loc2grow(il1)
5165 : : if (uplo == "L" .and. iglob2 > iglob1) mat%buffer_cplx(il1, il2) = zero_sp
5166 : : if (uplo == "U" .and. iglob2 < iglob1) mat%buffer_cplx(il1, il2) = zero_sp
5167 : : end do
5168 : : end do
5169 : :
5170 : : call mat%copy(work_mat, empty=.False.)
5171 : :
5172 : : ! call pzgeadd(trans, m, n, alpha, a, ia, ja, desca, beta, c, ic, jc, descc)
5173 : : ! sub(C) := beta*sub(C) + alpha*op(sub(A))
5174 : : call pcgeadd("C", mm, mm, cone_sp, work_mat%buffer_cplx, 1, 1, work_mat%desc, &
5175 : : cone_sp, mat%buffer_cplx, 1, 1, mat%desc)
5176 : : call work_mat%free()
5177 : :
5178 : : do il2=1,mat%size_local(2)
5179 : : iglob2 = mat%loc2gcol(il2)
5180 : : do il1=1,mat%size_local(1)
5181 : : iglob1 = mat%loc2grow(il1)
5182 : : if (iglob2 == iglob1) mat%buffer_cplx(il1, il2) = 0.5_sp * mat%buffer_cplx(il1, il2)
5183 : : end do
5184 : : end do
5185 : : end if ! full__
5186 : : #endif
5187 : :
5188 : 0 : end subroutine slkmat_sp_hpd_invert
5189 : : !!***
5190 : :
5191 : : !----------------------------------------------------------------------
5192 : :
5193 : : !!****f* m_slk/slkmat_dp_ptrans
5194 : : !! NAME
5195 : : !! slkmat_dp_ptrans
5196 : : !!
5197 : : !! FUNCTION
5198 : : !! Transposes a matrix
5199 : : !!
5200 : : !! sub( C ) := beta*sub( C ) + alpha*op( sub( A ) )
5201 : : !!
5202 : : !! where
5203 : : !!
5204 : : !! sub( C ) denotes C(IC:IC+M-1,JC:JC+N-1),
5205 : : !! sub( A ) denotes A(IA:IA+N-1,JA:JA+M-1), and, op( X ) = X'.
5206 : : !!
5207 : : !! Thus, op( sub( A ) ) denotes A(IA:IA+N-1,JA:JA+M-1)'.
5208 : : !! Beta is a scalar, sub( C ) is an m by n submatrix, and sub( A ) is an n by m submatrix.
5209 : : !!
5210 : : !! out_mat is initialized by the routine with the processor of in_mat.
5211 : : !!
5212 : : !! INPUTS
5213 : : !! trans= "N" for the transpose (p?tranu), "C" for the conjugate transpose (p?tranc).
5214 : : !! Ignored for real matrices.
5215 : : !! [out_gshape(2)]: Global shape of out_mat. Default: transpose of the global shape of in_mat.
5216 : : !! [size_blocs(2)]: Block sizes of out_mat. Default: one-dimensional column distribution
5217 : : !! [in_mat%size_global(2), ceiling(in_mat%size_global(1) / npcol)].
5218 : : !! [alpha], [beta]: Scalars in the formula above. Default: alpha = 1, beta = 0.
5219 : : !! Only the real part is used for real matrices.
5220 : : !! [ija(2)]: (global) The row and column indices in the distributed matrix in_mat indicating
5221 : : !! the first row and the first column of the submatrix sub(A), respectively.
5222 : : !! [ijc(2)]: (global) The row and column indices in the distributed matrix out_mat
5223 : : !! indicating the first row and the first column of the submatrix sub(C), respectively.
5224 : : !! [free]: True to deallocate in_mat. Default: False
5225 : : !!
5226 : : !! SOURCE
5227 : :
5228 : 0 : subroutine slkmat_dp_ptrans(in_mat, trans, out_mat, &
5229 : : out_gshape, ija, ijc, size_blocs, alpha, beta, free) ! optional
5230 : :
5231 : : !Arguments ------------------------------------
5232 : : class(slkmat_dp_t),intent(inout) :: in_mat
5233 : : character(len=1),intent(in) :: trans
5234 : : class(slkmat_dp_t),intent(inout) :: out_mat
5235 : : integer,optional,intent(in) :: out_gshape(2), size_blocs(2), ija(2), ijc(2)
5236 : : complex(dp),optional,intent(in) :: alpha, beta
5237 : : logical,optional,intent(in) :: free
5238 : :
5239 : : !Local variables-------------------------------
5240 : : integer :: sb, mm, nn, size_blocs__(2)
5241 : : real(dp) :: ralpha__, rbeta__
5242 : : integer :: ija__(2), ijc__(2)
5243 : : complex(dp) :: calpha__, cbeta__
5244 : : ! *************************************************************************
5245 : :
5246 : 0 : ija__ = [1, 1]; if (present(ija)) ija__ = ija
5247 : 0 : ijc__ = [1, 1]; if (present(ijc)) ijc__ = ijc
5248 : :
5249 : : ! transposed output (sub)matrix has shape (nn, mm)
5250 [ # # ]: 0 : if (present(out_gshape)) then
5251 : 0 : nn = out_gshape(1)
5252 : 0 : mm = out_gshape(2)
5253 : : else
5254 : 0 : nn = in_mat%size_global(2)
5255 : 0 : mm = in_mat%size_global(1)
5256 : : end if
5257 : :
5258 [ # # ]: 0 : if (present(size_blocs)) then
5259 : 0 : size_blocs__ = size_blocs
5260 : : else
5261 : : ! FIXME: This can cause problems if I start to use round-robin distribution in GWR!!!!!
5262 : 0 : size_blocs__(1) = in_mat%size_global(2)
5263 : 0 : sb = in_mat%size_global(1) / in_mat%processor%grid%dims(2)
5264 [ # # ]: 0 : if (mod(in_mat%size_global(1), in_mat%processor%grid%dims(2)) /= 0) sb = sb + 1
5265 : 0 : size_blocs__(2) = sb
5266 : : !size_blocs__(2) = in_mat%size_blocs(1); size_blocs__(1) = in_mat%size_blocs(2)
5267 : : end if
5268 : :
5269 : 0 : call out_mat%init(nn, mm, in_mat%processor, in_mat%istwf_k, size_blocs=size_blocs__)
5270 : :
5271 : : ! prototype: call pdtran(m, n, alpha, a, ia, ja, desca, beta, c, ic, jc, descc)
5272 : :
5273 [ # # ]: 0 : if (allocated(in_mat%buffer_cplx)) then
5274 : : #ifdef HAVE_LINALG_SCALAPACK
5275 : : select case (trans)
5276 : : case ("N")
5277 : : ! sub(C) := beta*sub(C) + alpha*sub(A)',
5278 : : calpha__ = cone; if (present(alpha)) calpha__ = alpha
5279 : : cbeta__ = czero; if (present(beta)) cbeta__ = beta
5280 : : call pztranu(nn, mm, calpha__, in_mat%buffer_cplx, ija__(1), ija__(2), &
5281 : : in_mat%desc, cbeta__, out_mat%buffer_cplx, ijc__(1), ijc__(2), out_mat%desc)
5282 : :
5283 : : case ("C")
5284 : : ! sub(C) := beta * sub(C) + alpha * conjg(sub(A)')
5285 : : calpha__ = cone; if (present(alpha)) calpha__ = alpha
5286 : : cbeta__ = czero; if (present(beta)) cbeta__ = beta
5287 : : call pztranc(nn, mm, calpha__, in_mat%buffer_cplx, ija__(1), ija__(2), &
5288 : : in_mat%desc, cbeta__, out_mat%buffer_cplx, ijc__(1), ijc__(2), out_mat%desc)
5289 : :
5290 : : case default
5291 : : ABI_ERROR(sjoin("Invalid value for trans:", trans))
5292 : : end select
5293 : :
5294 : : else if (allocated(in_mat%buffer_real)) then
5295 : : ralpha__ = one; if (present(alpha)) ralpha__ = real(alpha)
5296 : : rbeta__ = zero; if (present(beta)) rbeta__ = real(beta)
5297 : : call pdtran(nn, mm, ralpha__, in_mat%buffer_real, ija__(1), ija__(2), &
5298 : : in_mat%desc, rbeta__, out_mat%buffer_real, ijc__(1), ijc__(2), out_mat%desc)
5299 : : #endif
5300 : : else
5301 : 0 : ABI_ERROR("Neither buffer_cplx nor buffer_real are allocated!")
5302 : : end if
5303 : :
5304 [ # # ]: 0 : if (present(free)) then
5305 [ # # ]: 0 : if (free) call in_mat%free()
5306 : : end if
5307 : :
5308 : 0 : end subroutine slkmat_dp_ptrans
5309 : : !!***
5310 : :
5311 : : !!****f* m_slk/slkmat_sp_ptrans
5312 : : !! NAME
5313 : : !! slkmat_sp_ptrans
5314 : : !!
5315 : : !! FUNCTION
5316 : : !! Transposes a matrix
5317 : : !!
5318 : : !! sub( C ) := beta*sub( C ) + alpha*op( sub( A ) )
5319 : : !!
5320 : : !! where
5321 : : !!
5322 : : !! sub( C ) denotes C(IC:IC+M-1,JC:JC+N-1),
5323 : : !! sub( A ) denotes A(IA:IA+N-1,JA:JA+M-1), and, op( X ) = X'.
5324 : : !!
5325 : : !! Thus, op( sub( A ) ) denotes A(IA:IA+N-1,JA:JA+M-1)'.
5326 : : !! Beta is a scalar, sub( C ) is an m by n submatrix, and sub( A ) is an n by m submatrix.
5327 : : !!
5328 : : !! out_mat is initialized by the routine with the processor of in_mat.
5329 : : !!
5330 : : !! INPUTS
5331 : : !! trans= "N" for the transpose (p?tranu), "C" for the conjugate transpose (p?tranc).
5332 : : !! Ignored for real matrices.
5333 : : !! [out_gshape(2)]: Global shape of out_mat. Default: transpose of the global shape of in_mat.
5334 : : !! [size_blocs(2)]: Block sizes of out_mat. Default: one-dimensional column distribution
5335 : : !! [in_mat%size_global(2), ceiling(in_mat%size_global(1) / npcol)].
5336 : : !! [alpha], [beta]: Scalars in the formula above. Default: alpha = 1, beta = 0.
5337 : : !! Only the real part is used for real matrices.
5338 : : !! [ija(2)]: (global) The row and column indices in the distributed matrix in_mat indicating
5339 : : !! the first row and the first column of the submatrix sub(A), respectively.
5340 : : !! [ijc(2)]: (global) The row and column indices in the distributed matrix out_mat
5341 : : !! indicating the first row and the first column of the submatrix sub(C), respectively.
5342 : : !! [free]: True to deallocate in_mat. Default: False
5343 : : !!
5344 : : !! SOURCE
5345 : :
5346 : 0 : subroutine slkmat_sp_ptrans(in_mat, trans, out_mat, &
5347 : : out_gshape, ija, ijc, size_blocs, alpha, beta, free) ! optional
5348 : :
5349 : : !Arguments ------------------------------------
5350 : : class(slkmat_sp_t),intent(inout) :: in_mat
5351 : : character(len=1),intent(in) :: trans
5352 : : class(slkmat_sp_t),intent(inout) :: out_mat
5353 : : integer,optional,intent(in) :: out_gshape(2), size_blocs(2), ija(2), ijc(2)
5354 : : complex(sp),optional,intent(in) :: alpha, beta
5355 : : logical,optional,intent(in) :: free
5356 : :
5357 : : !Local variables-------------------------------
5358 : : integer :: sb, mm, nn, size_blocs__(2)
5359 : : real(sp) :: ralpha__, rbeta__
5360 : : integer :: ija__(2), ijc__(2)
5361 : : complex(sp) :: calpha__, cbeta__
5362 : : ! *************************************************************************
5363 : :
5364 : 0 : ija__ = [1, 1]; if (present(ija)) ija__ = ija
5365 : 0 : ijc__ = [1, 1]; if (present(ijc)) ijc__ = ijc
5366 : :
5367 : : ! transposed output (sub)matrix has shape (nn, mm)
5368 [ # # ]: 0 : if (present(out_gshape)) then
5369 : 0 : nn = out_gshape(1)
5370 : 0 : mm = out_gshape(2)
5371 : : else
5372 : 0 : nn = in_mat%size_global(2)
5373 : 0 : mm = in_mat%size_global(1)
5374 : : end if
5375 : :
5376 [ # # ]: 0 : if (present(size_blocs)) then
5377 : 0 : size_blocs__ = size_blocs
5378 : : else
5379 : : ! FIXME: This can cause problems if I start to use round-robin distribution in GWR!!!!!
5380 : 0 : size_blocs__(1) = in_mat%size_global(2)
5381 : 0 : sb = in_mat%size_global(1) / in_mat%processor%grid%dims(2)
5382 [ # # ]: 0 : if (mod(in_mat%size_global(1), in_mat%processor%grid%dims(2)) /= 0) sb = sb + 1
5383 : 0 : size_blocs__(2) = sb
5384 : : !size_blocs__(2) = in_mat%size_blocs(1); size_blocs__(1) = in_mat%size_blocs(2)
5385 : : end if
5386 : :
5387 : 0 : call out_mat%init(nn, mm, in_mat%processor, in_mat%istwf_k, size_blocs=size_blocs__)
5388 : :
5389 : : ! prototype: call pdtran(m, n, alpha, a, ia, ja, desca, beta, c, ic, jc, descc)
5390 : :
5391 [ # # ]: 0 : if (allocated(in_mat%buffer_cplx)) then
5392 : : #ifdef HAVE_LINALG_SCALAPACK
5393 : : select case (trans)
5394 : : case ("N")
5395 : : ! sub(C) := beta*sub(C) + alpha*sub(A)',
5396 : : calpha__ = cone_sp; if (present(alpha)) calpha__ = alpha
5397 : : cbeta__ = czero_sp; if (present(beta)) cbeta__ = beta
5398 : : call pctranu(nn, mm, calpha__, in_mat%buffer_cplx, ija__(1), ija__(2), &
5399 : : in_mat%desc, cbeta__, out_mat%buffer_cplx, ijc__(1), ijc__(2), out_mat%desc)
5400 : :
5401 : : case ("C")
5402 : : ! sub(C) := beta * sub(C) + alpha * conjg(sub(A)')
5403 : : calpha__ = cone_sp; if (present(alpha)) calpha__ = alpha
5404 : : cbeta__ = czero_sp; if (present(beta)) cbeta__ = beta
5405 : : call pctranc(nn, mm, calpha__, in_mat%buffer_cplx, ija__(1), ija__(2), &
5406 : : in_mat%desc, cbeta__, out_mat%buffer_cplx, ijc__(1), ijc__(2), out_mat%desc)
5407 : :
5408 : : case default
5409 : : ABI_ERROR(sjoin("Invalid value for trans:", trans))
5410 : : end select
5411 : :
5412 : : else if (allocated(in_mat%buffer_real)) then
5413 : : ralpha__ = one_sp; if (present(alpha)) ralpha__ = real(alpha)
5414 : : rbeta__ = zero_sp; if (present(beta)) rbeta__ = real(beta)
5415 : : call pstran(nn, mm, ralpha__, in_mat%buffer_real, ija__(1), ija__(2), &
5416 : : in_mat%desc, rbeta__, out_mat%buffer_real, ijc__(1), ijc__(2), out_mat%desc)
5417 : : #endif
5418 : : else
5419 : 0 : ABI_ERROR("Neither buffer_cplx nor buffer_real are allocated!")
5420 : : end if
5421 : :
5422 [ # # ]: 0 : if (present(free)) then
5423 [ # # ]: 0 : if (free) call in_mat%free()
5424 : : end if
5425 : :
5426 : 0 : end subroutine slkmat_sp_ptrans
5427 : : !!***
5428 : :
5429 : : !----------------------------------------------------------------------
5430 : :
5431 : : !!****f* m_slk/basemat_change_size_blocs
5432 : : !! NAME
5433 : : !! basemat_change_size_blocs
5434 : : !!
5435 : : !! FUNCTION
5436 : : !! Change the block sizes, return new matrix in out_mat
5437 : : !! Uses p?gemr2d with the BLACS context of the output processor: its grid must include all the
5438 : : !! processes of the grid of in_mat. All these processes must call the routine.
5439 : : !!
5440 : : !! INPUTS
5441 : : !! [size_blocs(2)]: Block sizes of out_mat. Default: see basemat_init.
5442 : : !! [processor]: Processor used to initialize out_mat. Default: in_mat%processor.
5443 : : !! [free]: True if `in_mat` should be deallocated. Default: False
5444 : : !!
5445 : : !! OUTPUT
5446 : : !! out_mat: New matrix with the same global shape and the same values as in_mat.
5447 : : !!
5448 : : !! SOURCE
5449 : :
5450 [ # # ]: 0 : subroutine basemat_change_size_blocs(in_mat, out_mat, &
5451 : : size_blocs, processor, free) ! Optional
5452 : :
5453 : : !Arguments ------------------------------------
5454 : : class(basemat_t),target,intent(inout) :: in_mat
5455 : : class(basemat_t),intent(out) :: out_mat
5456 : : integer,optional,intent(in) :: size_blocs(2)
5457 : : class(slk_processor_t), target, optional,intent(in) :: processor
5458 : : logical,optional,intent(in) :: free
5459 : :
5460 : : !Local variables-------------------------------
5461 : : type(slk_processor_t), pointer :: processor__
5462 : : ! *************************************************************************
5463 : :
5464 [ # # # # ]: 0 : processor__ => in_mat%processor; if (present(processor)) processor__ => processor
5465 : :
5466 [ # # ]: 0 : if (present(size_blocs)) then
5467 : 0 : call out_mat%init(in_mat%size_global(1), in_mat%size_global(2), processor__, in_mat%istwf_k, size_blocs=size_blocs)
5468 : : else
5469 : 0 : call out_mat%init(in_mat%size_global(1), in_mat%size_global(2), processor__, in_mat%istwf_k)
5470 : : end if
5471 : :
5472 : : !write(std_out, *)"out_mat%size_global", out_mat%size_global
5473 : : !call in_mat%print(header="input matrix in by basemat_change_size_blocs")
5474 : : !call out_mat%print(header="output matrix generated by basemat_change_size_blocs")
5475 : :
5476 [ # # ]: 0 : ABI_CHECK(same_type_as(in_mat, out_mat), "in_mat and out_mat should have same type!")
5477 : :
5478 : : ! p?gemr2d: Copies a submatrix from one general rectangular matrix to another.
5479 : : ! prototype
5480 : : !call pzgemr2d(m, n, a, ia, ja, desca, b, ib, jb, descb, comm)
5481 : :
5482 : : #ifdef HAVE_LINALG_SCALAPACK
5483 : : select type (in_mat)
5484 : : class is (slkmat_dp_t)
5485 : : select type (out_mat)
5486 : : class is (slkmat_dp_t)
5487 : : if (allocated(in_mat%buffer_cplx)) then
5488 : : ABI_CHECK_IEQ(kind(in_mat%buffer_cplx), kind(out_mat%buffer_cplx), "Different kind")
5489 : : ABI_CHECK(allocated(out_mat%buffer_cplx), "out_mat%buffer_cplx should be allocated")
5490 : : call pzgemr2d(in_mat%size_global(1), in_mat%size_global(2), &
5491 : : in_mat%buffer_cplx, 1, 1, in_mat%desc, &
5492 : : out_mat%buffer_cplx, 1, 1, out_mat%desc, &
5493 : : processor__%grid%comm)
5494 : :
5495 : : else if (allocated(in_mat%buffer_real)) then
5496 : : ABI_CHECK_IEQ(kind(in_mat%buffer_real), kind(out_mat%buffer_real), "Different kind")
5497 : : ABI_CHECK(allocated(out_mat%buffer_real), "out_mat%buffer_real should be allocated")
5498 : : call pdgemr2d(in_mat%size_global(1), in_mat%size_global(2), &
5499 : : in_mat%buffer_real, 1, 1, in_mat%desc, &
5500 : : out_mat%buffer_real, 1, 1, out_mat%desc, &
5501 : : processor__%grid%comm)
5502 : : else
5503 : : ABI_ERROR("Neither buffer_cplx nor buffer_real are allocated!")
5504 : : end if
5505 : : end select
5506 : :
5507 : : class is (slkmat_sp_t)
5508 : : select type (out_mat)
5509 : : class is (slkmat_sp_t)
5510 : : if (allocated(in_mat%buffer_cplx)) then
5511 : : ABI_CHECK_IEQ(kind(in_mat%buffer_cplx), kind(out_mat%buffer_cplx), "Different kind")
5512 : :
5513 : : ABI_CHECK(allocated(out_mat%buffer_cplx), "out_mat%buffer should be allocated")
5514 : : call pcgemr2d(in_mat%size_global(1), in_mat%size_global(2), &
5515 : : in_mat%buffer_cplx, 1, 1, in_mat%desc, &
5516 : : out_mat%buffer_cplx, 1, 1, out_mat%desc, &
5517 : : processor__%grid%comm)
5518 : :
5519 : : else if (allocated(in_mat%buffer_real)) then
5520 : : ABI_CHECK_IEQ(kind(in_mat%buffer_real), kind(out_mat%buffer_real), "Different kind")
5521 : : ABI_CHECK(allocated(out_mat%buffer_real), "out_mat%buffer_real should be allocated")
5522 : : call psgemr2d(in_mat%size_global(1), in_mat%size_global(2), &
5523 : : in_mat%buffer_real, 1, 1, in_mat%desc, &
5524 : : out_mat%buffer_real, 1, 1, out_mat%desc, &
5525 : : processor__%grid%comm)
5526 : : else
5527 : : ABI_ERROR("Neither buffer_cplx nor buffer_real are allocated!")
5528 : : end if
5529 : : end select
5530 : :
5531 : : class default
5532 : : ABI_ERROR("Wrong class")
5533 : : end select
5534 : : #endif
5535 : :
5536 [ # # ]: 0 : if (present(free)) then
5537 [ # # ]: 0 : if (free) call in_mat%free()
5538 : : end if
5539 : :
5540 : 0 : end subroutine basemat_change_size_blocs
5541 : : !!***
5542 : :
5543 : : !----------------------------------------------------------------------
5544 : :
5545 : : !!****f* m_slk/slkmat_dp_cut
5546 : : !! NAME
5547 : : !! slkmat_dp_cut
5548 : : !!
5549 : : !! FUNCTION
5550 : : !! Extract submatrix of shape (glob_nrows, glob_ncols) starting at `ija` from `in_mat`
5551 : : !! and create new matrix with `size_blocs` and `processor`
5552 : : !! Uses p?gemr2d with the BLACS context of the output processor: its grid must include all the
5553 : : !! processes of the grid of in_mat. All these processes must call the routine.
5554 : : !!
5555 : : !! INPUTS
5556 : : !! glob_nrows, glob_ncols: Global shape of the submatrix.
5557 : : !! [size_blocs(2)]: Block sizes of out_mat. Default: see basemat_init.
5558 : : !! [processor]: Processor used to initialize out_mat. Default: in_mat%processor.
5559 : : !! [ija(2)]: Global row and column indices of the first entry of the submatrix in in_mat. Default: [1, 1]
5560 : : !! [ijb(2)]: Global row and column indices where the submatrix is copied in out_mat. Default: [1, 1]
5561 : : !! [free]: True if `in_mat` should be deallocated. Default: False
5562 : : !!
5563 : : !! OUTPUT
5564 : : !! out_mat: New matrix of global shape (glob_nrows, glob_ncols).
5565 : : !!
5566 : : !! SOURCE
5567 : :
5568 [ # # ]: 0 : subroutine slkmat_dp_cut(in_mat, glob_nrows, glob_ncols, out_mat, &
5569 : : size_blocs, processor, ija, ijb, free) ! Optional
5570 : :
5571 : : !Arguments ------------------------------------
5572 : : class(slkmat_dp_t),target,intent(inout) :: in_mat
5573 : : integer,intent(in) :: glob_nrows, glob_ncols
5574 : : class(slkmat_dp_t),intent(out) :: out_mat
5575 : : integer,optional,intent(in) :: size_blocs(2)
5576 : : class(slk_processor_t), target, optional,intent(in) :: processor
5577 : : integer,optional,intent(in) :: ija(2), ijb(2)
5578 : : logical,optional,intent(in) :: free
5579 : :
5580 : : !Local variables-------------------------------
5581 : : type(slk_processor_t), pointer :: processor__
5582 : : integer :: ija__(2), ijb__(2)
5583 : : ! *************************************************************************
5584 : :
5585 : 0 : ija__ = [1, 1]; if (present(ija)) ija__ = ija
5586 : 0 : ijb__ = [1, 1]; if (present(ijb)) ijb__ = ijb
5587 : :
5588 [ # # # # ]: 0 : processor__ => in_mat%processor; if (present(processor)) processor__ => processor
5589 : :
5590 [ # # ]: 0 : if (present(size_blocs)) then
5591 : 0 : call out_mat%init(glob_nrows, glob_ncols, processor__, in_mat%istwf_k, size_blocs=size_blocs)
5592 : : else
5593 : 0 : call out_mat%init(glob_nrows, glob_ncols, processor__, in_mat%istwf_k)
5594 : : end if
5595 : : !call out_mat%print(header="output matrix generated by slkmat_dp_cut")
5596 : :
5597 : : ! p?gemr2d: Copies a submatrix from one general rectangular matrix to another.
5598 : : ! prototype
5599 : : !call pzgemr2d(m, n, a, ia, ja, desca, b, ib, jb, descb, comm)
5600 : :
5601 [ # # ]: 0 : if (allocated(in_mat%buffer_cplx)) then
5602 : : #ifdef HAVE_LINALG_SCALAPACK
5603 : : call pzgemr2d(glob_nrows, glob_ncols, &
5604 : : in_mat%buffer_cplx, ija__(1), ija__(2), in_mat%desc, &
5605 : : out_mat%buffer_cplx, ijb__(1), ijb__(2), out_mat%desc, &
5606 : : processor__%grid%comm)
5607 : :
5608 : : else if (allocated(in_mat%buffer_real)) then
5609 : : call pdgemr2d(glob_nrows, glob_ncols, &
5610 : : in_mat%buffer_real, ija__(1), ija__(2), in_mat%desc, &
5611 : : out_mat%buffer_real, ijb__(1), ijb__(2), out_mat%desc, &
5612 : : processor__%grid%comm)
5613 : : #endif
5614 : : else
5615 : 0 : ABI_ERROR("Neither buffer_cplx nor buffer_real are allocated!")
5616 : : end if
5617 : :
5618 [ # # ]: 0 : if (present(free)) then
5619 [ # # ]: 0 : if (free) call in_mat%free()
5620 : : end if
5621 : :
5622 : 0 : end subroutine slkmat_dp_cut
5623 : : !!***
5624 : :
5625 : : !----------------------------------------------------------------------
5626 : :
5627 : : !!****f* m_slk/slkmat_sp_cut
5628 : : !! NAME
5629 : : !! slkmat_sp_cut
5630 : : !!
5631 : : !! FUNCTION
5632 : : !! Extract submatrix of shape (glob_nrows, glob_ncols) starting at `ija` from `in_mat`
5633 : : !! and create new matrix with `size_blocs` and `processor`
5634 : : !! Uses p?gemr2d with the BLACS context of the output processor: its grid must include all the
5635 : : !! processes of the grid of in_mat. All these processes must call the routine.
5636 : : !!
5637 : : !! INPUTS
5638 : : !! glob_nrows, glob_ncols: Global shape of the submatrix.
5639 : : !! [size_blocs(2)]: Block sizes of out_mat. Default: see basemat_init.
5640 : : !! [processor]: Processor used to initialize out_mat. Default: in_mat%processor.
5641 : : !! [ija(2)]: Global row and column indices of the first entry of the submatrix in in_mat. Default: [1, 1]
5642 : : !! [ijb(2)]: Global row and column indices where the submatrix is copied in out_mat. Default: [1, 1]
5643 : : !! [free]: True if `in_mat` should be deallocated. Default: False
5644 : : !!
5645 : : !! OUTPUT
5646 : : !! out_mat: New matrix of global shape (glob_nrows, glob_ncols).
5647 : : !!
5648 : : !! SOURCE
5649 : :
5650 [ # # ]: 0 : subroutine slkmat_sp_cut(in_mat, glob_nrows, glob_ncols, out_mat, &
5651 : : size_blocs, processor, ija, ijb, free) ! Optional
5652 : :
5653 : : !Arguments ------------------------------------
5654 : : class(slkmat_sp_t),target,intent(inout) :: in_mat
5655 : : integer,intent(in) :: glob_nrows, glob_ncols
5656 : : class(slkmat_sp_t),intent(out) :: out_mat
5657 : : integer,optional,intent(in) :: size_blocs(2)
5658 : : class(slk_processor_t), target, optional,intent(in) :: processor
5659 : : integer,optional,intent(in) :: ija(2), ijb(2)
5660 : : logical,optional,intent(in) :: free
5661 : :
5662 : : !Local variables-------------------------------
5663 : : type(slk_processor_t), pointer :: processor__
5664 : : integer :: ija__(2), ijb__(2)
5665 : : ! *************************************************************************
5666 : :
5667 : 0 : ija__ = [1, 1]; if (present(ija)) ija__ = ija
5668 : 0 : ijb__ = [1, 1]; if (present(ijb)) ijb__ = ijb
5669 : :
5670 [ # # # # ]: 0 : processor__ => in_mat%processor; if (present(processor)) processor__ => processor
5671 : :
5672 [ # # ]: 0 : if (present(size_blocs)) then
5673 : 0 : call out_mat%init(glob_nrows, glob_ncols, processor__, in_mat%istwf_k, size_blocs=size_blocs)
5674 : : else
5675 : 0 : call out_mat%init(glob_nrows, glob_ncols, processor__, in_mat%istwf_k)
5676 : : end if
5677 : : !call out_mat%print(header="output matrix generated by slkmat_dp_cut")
5678 : :
5679 : : ! p?gemr2d: Copies a submatrix from one general rectangular matrix to another.
5680 : : ! prototype
5681 : : !call pzgemr2d(m, n, a, ia, ja, desca, b, ib, jb, descb, comm)
5682 : :
5683 [ # # ]: 0 : if (allocated(in_mat%buffer_cplx)) then
5684 : : #ifdef HAVE_LINALG_SCALAPACK
5685 : : call pcgemr2d(glob_nrows, glob_ncols, &
5686 : : in_mat%buffer_cplx, ija__(1), ija__(2), in_mat%desc, &
5687 : : out_mat%buffer_cplx, ijb__(1), ijb__(2), out_mat%desc, &
5688 : : processor__%grid%comm)
5689 : :
5690 : : else if (allocated(in_mat%buffer_real)) then
5691 : : call psgemr2d(glob_nrows, glob_ncols, &
5692 : : in_mat%buffer_real, ija__(1), ija__(2), in_mat%desc, &
5693 : : out_mat%buffer_real, ijb__(1), ijb__(2), out_mat%desc, &
5694 : : processor__%grid%comm)
5695 : : #endif
5696 : : else
5697 : 0 : ABI_ERROR("Neither buffer_cplx nor buffer_real are allocated!")
5698 : : end if
5699 : :
5700 [ # # ]: 0 : if (present(free)) then
5701 [ # # ]: 0 : if (free) call in_mat%free()
5702 : : end if
5703 : :
5704 : 0 : end subroutine slkmat_sp_cut
5705 : : !!***
5706 : :
5707 : : !----------------------------------------------------------------------
5708 : :
5709 : : !!****f* m_slk/slkmat_dp_take_from
5710 : : !! NAME
5711 : : !! slkmat_dp_take_from
5712 : : !!
5713 : : !! FUNCTION
5714 : : !! Take values from source
5715 : : !! NB: This routine should be called by all procs owning mat and source.
5716 : : !! Uses p?gemr2d with the BLACS context of source: the grid of source must include all the processes
5717 : : !! of the grid of out_mat. out_mat and source must have the same global shape and the same istwf_k
5718 : : !! but they can have different grids and block sizes.
5719 : : !! On the processes that do not own out_mat, out_mat must never have been initialized
5720 : : !! (size_global = [-1, -1]): then desc(CTXT_) is set to -1. A freed matrix has size_global = [0, 0].
5721 : : !!
5722 : : !! INPUTS
5723 : : !! [ija(2)]: Global row and column indices of the first entry to copy in source. Default: [1, 1]
5724 : : !! [ijb(2)]: Global row and column indices of the first entry to write in out_mat. Default: [1, 1]
5725 : : !! [free]: True if source should be deallocated. Default: False
5726 : : !!
5727 : : !! SIDE EFFECTS
5728 : : !! out_mat: Filled with the values of source.
5729 : : !!
5730 : : !! SOURCE
5731 : :
5732 : 0 : subroutine slkmat_dp_take_from(out_mat, source, &
5733 : : ija, ijb, free) ! optional
5734 : :
5735 : : !Arguments ------------------------------------
5736 : : class(slkmat_dp_t),intent(inout) :: out_mat
5737 : : class(slkmat_dp_t),intent(inout) :: source
5738 : : integer,optional,intent(in) :: ija(2), ijb(2)
5739 : : logical,optional,intent(in) :: free
5740 : :
5741 : : !Local variables-------------------------------
5742 : : integer :: mm, nn
5743 : : character(len=500) :: msg
5744 : : integer :: ija__(2), ijb__(2)
5745 : : ! *************************************************************************
5746 : :
5747 : : ! prototype
5748 : : !call pzgemr2d(m, n, a, ia, ja, desca, b, ib, jb, descb, comm)
5749 : :
5750 : : ! Take care when context A is disjoint from context B. The general rules for which parameters need to be set are:
5751 : : !
5752 : : ! - All calling processes must have the correct m and n.
5753 : : ! - Processes in context A must correctly define all parameters describing A.
5754 : : ! - Processes in context B must correctly define all parameters describing B.
5755 : : ! - Processes which are not members of context A must pass ctxt_a = -1 and need not set other parameters describing A.
5756 : : ! - Processes which are not members of contextB must pass ctxt_b = -1 and need not set other parameters describing B.
5757 : :
5758 : 0 : mm = source%size_global(1)
5759 : 0 : nn = source%size_global(2)
5760 : :
5761 : 0 : ija__ = [1, 1]; if (present(ija)) ija__ = ija
5762 : 0 : ijb__ = [1, 1]; if (present(ijb)) ijb__ = ijb
5763 : :
5764 [ # # # # : 0 : if (all(out_mat%size_global == -1)) then
# # ]
5765 : 0 : out_mat%desc(CTXT_) = -1
5766 : : else
5767 [ # # ]: 0 : ABI_CHECK_IEQ(out_mat%istwf_k, source%istwf_k, "istwfk_mat /= istwfk_source")
5768 [ # # # # : 0 : if (any(out_mat%size_global /= source%size_global)) then
# # ]
5769 : : msg = sjoin("Matrices should have same global shape but out_mat:", ltoa(out_mat%size_global), &
5770 [ # # ]: 0 : "source:", ltoa(source%size_global))
5771 : 0 : ABI_ERROR(msg)
5772 : : end if
5773 : : end if
5774 : :
5775 [ # # ]: 0 : if (allocated(source%buffer_cplx)) then
5776 : : #ifdef HAVE_LINALG_SCALAPACK
5777 : : call pzgemr2d(mm, nn, &
5778 : : source%buffer_cplx, ija__(1), ija__(2), source%desc, &
5779 : : out_mat%buffer_cplx, ijb__(1), ijb__(2), out_mat%desc, &
5780 : : source%processor%grid%comm)
5781 : :
5782 : : else if (allocated(source%buffer_real)) then
5783 : : call pdgemr2d(mm, nn, &
5784 : : source%buffer_real, ija__(1), ija__(2), source%desc, &
5785 : : out_mat%buffer_real,ijb__(1), ijb__(2), out_mat%desc, &
5786 : : source%processor%grid%comm)
5787 : : #endif
5788 : : else
5789 : 0 : ABI_ERROR("Neither buffer_cplx nor buffer_real are allocated!")
5790 : : end if
5791 : :
5792 [ # # ]: 0 : if (present(free)) then
5793 [ # # ]: 0 : if (free) call source%free()
5794 : : end if
5795 : :
5796 : 0 : end subroutine slkmat_dp_take_from
5797 : : !!***
5798 : :
5799 : : !----------------------------------------------------------------------
5800 : :
5801 : : !!****f* m_slk/slkmat_sp_take_from
5802 : : !! NAME
5803 : : !! slkmat_sp_take_from
5804 : : !!
5805 : : !! FUNCTION
5806 : : !! Take values from source
5807 : : !! NB: This routine should be called by all procs owning mat and source.
5808 : : !! Uses p?gemr2d with the BLACS context of source: the grid of source must include all the processes
5809 : : !! of the grid of out_mat. out_mat and source must have the same global shape and the same istwf_k
5810 : : !! but they can have different grids and block sizes.
5811 : : !! On the processes that do not own out_mat, out_mat must never have been initialized
5812 : : !! (size_global = [-1, -1]): then desc(CTXT_) is set to -1. A freed matrix has size_global = [0, 0].
5813 : : !!
5814 : : !! INPUTS
5815 : : !! [ija(2)]: Global row and column indices of the first entry to copy in source. Default: [1, 1]
5816 : : !! [ijb(2)]: Global row and column indices of the first entry to write in out_mat. Default: [1, 1]
5817 : : !! [free]: True if source should be deallocated. Default: False
5818 : : !!
5819 : : !! SIDE EFFECTS
5820 : : !! out_mat: Filled with the values of source.
5821 : : !!
5822 : : !! SOURCE
5823 : :
5824 : 0 : subroutine slkmat_sp_take_from(out_mat, source, &
5825 : : ija, ijb, free) ! optional
5826 : :
5827 : : !Arguments ------------------------------------
5828 : : class(slkmat_sp_t),intent(inout) :: out_mat
5829 : : class(slkmat_sp_t),intent(inout) :: source
5830 : : integer,optional,intent(in) :: ija(2), ijb(2)
5831 : : logical,optional,intent(in) :: free
5832 : :
5833 : : !Local variables-------------------------------
5834 : : integer :: mm, nn
5835 : : character(len=500) :: msg
5836 : : integer :: ija__(2), ijb__(2)
5837 : : ! *************************************************************************
5838 : :
5839 : : ! prototype
5840 : : !call pzgemr2d(m, n, a, ia, ja, desca, b, ib, jb, descb, comm)
5841 : :
5842 : : ! Take care when context A is disjoint from context B. The general rules for which parameters need to be set are:
5843 : : !
5844 : : ! - All calling processes must have the correct m and n.
5845 : : ! - Processes in context A must correctly define all parameters describing A.
5846 : : ! - Processes in context B must correctly define all parameters describing B.
5847 : : ! - Processes which are not members of context A must pass ctxt_a = -1 and need not set other parameters describing A.
5848 : : ! - Processes which are not members of contextB must pass ctxt_b = -1 and need not set other parameters describing B.
5849 : :
5850 : 0 : mm = source%size_global(1)
5851 : 0 : nn = source%size_global(2)
5852 : :
5853 : 0 : ija__ = [1, 1]; if (present(ija)) ija__ = ija
5854 : 0 : ijb__ = [1, 1]; if (present(ijb)) ijb__ = ijb
5855 : :
5856 [ # # # # : 0 : if (all(out_mat%size_global == -1)) then
# # ]
5857 : 0 : out_mat%desc(CTXT_) = -1
5858 : : else
5859 [ # # ]: 0 : ABI_CHECK_IEQ(out_mat%istwf_k, source%istwf_k, "istwfk_mat /= istwfk_source")
5860 [ # # # # : 0 : if (any(out_mat%size_global /= source%size_global)) then
# # ]
5861 : : msg = sjoin("Matrices should have same global shape but out_mat:", ltoa(out_mat%size_global), &
5862 [ # # ]: 0 : "source:", ltoa(source%size_global))
5863 : 0 : ABI_ERROR(msg)
5864 : : end if
5865 : : end if
5866 : :
5867 [ # # ]: 0 : if (allocated(source%buffer_cplx)) then
5868 : : #ifdef HAVE_LINALG_SCALAPACK
5869 : : call pcgemr2d(mm, nn, &
5870 : : source%buffer_cplx, ija__(1), ija__(2), source%desc, &
5871 : : out_mat%buffer_cplx, ijb__(1), ijb__(2), out_mat%desc, &
5872 : : source%processor%grid%comm)
5873 : :
5874 : : else if (allocated(source%buffer_real)) then
5875 : : call psgemr2d(mm, nn, &
5876 : : source%buffer_real, ija__(1), ija__(2), source%desc, &
5877 : : out_mat%buffer_real,ijb__(1), ijb__(2), out_mat%desc, &
5878 : : source%processor%grid%comm)
5879 : : #endif
5880 : : else
5881 : 0 : ABI_ERROR("Neither buffer_cplx nor buffer_real are allocated!")
5882 : : end if
5883 : :
5884 [ # # ]: 0 : if (present(free)) then
5885 [ # # ]: 0 : if (free) call source%free()
5886 : : end if
5887 : :
5888 : 0 : end subroutine slkmat_sp_take_from
5889 : : !!***
5890 : :
5891 : : !----------------------------------------------------------------------
5892 : :
5893 : : !!****f* m_slk/slkmat_dp_collect_cplx
5894 : : !! NAME
5895 : : !! slkmat_dp_collect_cplx
5896 : : !!
5897 : : !! FUNCTION
5898 : : !! Return on all processors the complex submatrix of shape (mm, nn) starting at position ija.
5899 : : !! NB: `out_carr` is allocated by the routine.
5900 : : !! If optional argument request is use, the routine uses non-blocking BCAST and client code is
5901 : : !! supposed to wait before accessing out_carr.
5902 : : !! Collective call: all the processes of the grid of in_mat must call the routine.
5903 : : !! The submatrix is first gathered on the master with p?gemr2d, then broadcast.
5904 : : !!
5905 : : !! INPUTS
5906 : : !! mm, nn: Global shape of the submatrix.
5907 : : !! ija(2): Global row and column indices of the first entry of the submatrix.
5908 : : !!
5909 : : !! OUTPUT
5910 : : !! out_carr(mm, nn): The submatrix, on all the processes.
5911 : : !! [request]: MPI request of the non-blocking broadcast.
5912 : : !!
5913 : : !! SOURCE
5914 : :
5915 : 0 : subroutine slkmat_dp_collect_cplx(in_mat, mm, nn, ija, out_carr, request)
5916 : :
5917 : : !Arguments ------------------------------------
5918 : : class(slkmat_dp_t),intent(in) :: in_mat
5919 : : integer,intent(in) :: mm, nn, ija(2)
5920 : : complex(dp) ABI_ASYNC, allocatable,intent(out) :: out_carr(:,:)
5921 : : integer ABI_ASYNC, optional,intent(out) :: request
5922 : :
5923 : : !Local variables-------------------------------
5924 : : integer,parameter :: master = 0
5925 : : integer :: ierr
5926 : : type(slk_processor_t) :: self_processor
5927 [ # # # # : 0 : type(slkmat_dp_t) :: out_mat
# # ]
5928 : : ! *************************************************************************
5929 : :
5930 : 0 : ABI_CHECK(allocated(in_mat%buffer_cplx), "buffer_cplx is not allocated")
5931 : :
5932 [ # # ]: 0 : if (in_mat%processor%grid%nprocs == 1) then
5933 : : ! Copy buffer and return
5934 [ # # # # : 0 : ABI_MALLOC(out_carr, (mm, nn))
# # # # #
# # # # #
# # # # ]
5935 [ # # # # ]: 0 : out_carr(:,:) = in_mat%buffer_cplx(ija(1):ija(1)+mm-1, ija(2):ija(2)+nn-1); return
5936 : : end if
5937 : :
5938 : : ! Two-step algorithm:
5939 : : ! 1) Use pzgemr2d to collect submatrix on master.
5940 : : ! 2) Master broadcasts submatrix.
5941 : :
5942 [ # # ]: 0 : if (in_mat%processor%my_rank == master) then
5943 : 0 : call self_processor%init(xmpi_comm_self)
5944 [ # # ]: 0 : call out_mat%init(mm, nn, self_processor, in_mat%istwf_k, size_blocs=[mm, nn])
5945 : : else
5946 : 0 : out_mat%desc(CTXT_) = -1
5947 : : end if
5948 : :
5949 : : #ifdef HAVE_LINALG_SCALAPACK
5950 : : call pzgemr2d(mm, nn, &
5951 : : in_mat%buffer_cplx, ija(1), ija(2), in_mat%desc, &
5952 : : out_mat%buffer_cplx, 1, 1, out_mat%desc, &
5953 : : in_mat%processor%grid%comm)
5954 : : #endif
5955 : :
5956 [ # # ]: 0 : if (in_mat%processor%my_rank == master) then
5957 [ # # ]: 0 : ABI_MOVE_ALLOC(out_mat%buffer_cplx, out_carr)
5958 : 0 : call out_mat%free()
5959 : 0 : call self_processor%free()
5960 : : else
5961 [ # # # # : 0 : ABI_MALLOC(out_carr, (mm, nn))
# # # # #
# # # # #
# # # # ]
5962 : : end if
5963 : :
5964 [ # # ]: 0 : if (present(request)) then
5965 : 0 : call xmpi_ibcast(out_carr, master, in_mat%processor%comm, request, ierr)
5966 : : else
5967 : 0 : call xmpi_bcast(out_carr, master, in_mat%processor%comm, ierr)
5968 : : end if
5969 : :
5970 [ # # # # : 0 : end subroutine slkmat_dp_collect_cplx
# # # # #
# # # #
# ]
5971 : : !!***
5972 : :
5973 : : !----------------------------------------------------------------------
5974 : :
5975 : : !!****f* m_slk/slkmat_sp_collect_cplx
5976 : : !! NAME
5977 : : !! slkmat_sp_collect_cplx
5978 : : !!
5979 : : !! FUNCTION
5980 : : !! Return on all processors the complex submatrix of shape (mm, nn) starting at position ija.
5981 : : !! NB: `out_carr` is allocated by the routine.
5982 : : !! If optional argument request is use, the routine uses non-blocking BCAST and client code is
5983 : : !! supposed to wait before accessing out_carr.
5984 : : !! Collective call: all the processes of the grid of in_mat must call the routine.
5985 : : !! The submatrix is first gathered on the master with p?gemr2d, then broadcast.
5986 : : !!
5987 : : !! INPUTS
5988 : : !! mm, nn: Global shape of the submatrix.
5989 : : !! ija(2): Global row and column indices of the first entry of the submatrix.
5990 : : !!
5991 : : !! OUTPUT
5992 : : !! out_carr(mm, nn): The submatrix, on all the processes.
5993 : : !! [request]: MPI request of the non-blocking broadcast.
5994 : : !!
5995 : : !! SOURCE
5996 : :
5997 : 0 : subroutine slkmat_sp_collect_cplx(in_mat, mm, nn, ija, out_carr, request)
5998 : :
5999 : : !Arguments ------------------------------------
6000 : : class(slkmat_sp_t),intent(in) :: in_mat
6001 : : integer,intent(in) :: mm, nn, ija(2)
6002 : : complex(sp) ABI_ASYNC, allocatable,intent(out) :: out_carr(:,:)
6003 : : integer ABI_ASYNC, optional,intent(out) :: request
6004 : :
6005 : : !Local variables-------------------------------
6006 : : integer,parameter :: master = 0
6007 : : integer :: ierr
6008 : : type(slk_processor_t) :: self_processor
6009 [ # # # # : 0 : type(slkmat_sp_t) :: out_mat
# # ]
6010 : : ! *************************************************************************
6011 : :
6012 : 0 : ABI_CHECK(allocated(in_mat%buffer_cplx), "buffer_cplx is not allocated")
6013 : :
6014 [ # # ]: 0 : if (in_mat%processor%grid%nprocs == 1) then
6015 : : ! Copy buffer and return
6016 [ # # # # : 0 : ABI_MALLOC(out_carr, (mm, nn))
# # # # #
# # # # #
# # # # ]
6017 [ # # # # ]: 0 : out_carr(:,:) = in_mat%buffer_cplx(ija(1):ija(1)+mm-1, ija(2):ija(2)+nn-1); return
6018 : : end if
6019 : :
6020 : : ! Two-step algorithm:
6021 : : ! 1) Use pzgemr2d to collect submatrix on master.
6022 : : ! 2) Master broadcasts submatrix.
6023 : :
6024 [ # # ]: 0 : if (in_mat%processor%my_rank == master) then
6025 : 0 : call self_processor%init(xmpi_comm_self)
6026 [ # # ]: 0 : call out_mat%init(mm, nn, self_processor, in_mat%istwf_k, size_blocs=[mm, nn])
6027 : : else
6028 : 0 : out_mat%desc(CTXT_) = -1
6029 : : end if
6030 : :
6031 : : #ifdef HAVE_LINALG_SCALAPACK
6032 : : call pcgemr2d(mm, nn, &
6033 : : in_mat%buffer_cplx, ija(1), ija(2), in_mat%desc, &
6034 : : out_mat%buffer_cplx, 1, 1, out_mat%desc, &
6035 : : in_mat%processor%grid%comm)
6036 : : #endif
6037 : :
6038 [ # # ]: 0 : if (in_mat%processor%my_rank == master) then
6039 [ # # ]: 0 : ABI_MOVE_ALLOC(out_mat%buffer_cplx, out_carr)
6040 : 0 : call out_mat%free()
6041 : 0 : call self_processor%free()
6042 : : else
6043 [ # # # # : 0 : ABI_MALLOC(out_carr, (mm, nn))
# # # # #
# # # # #
# # # # ]
6044 : : end if
6045 : :
6046 [ # # ]: 0 : if (present(request)) then
6047 : 0 : call xmpi_ibcast(out_carr, master, in_mat%processor%comm, request, ierr)
6048 : : else
6049 : 0 : call xmpi_bcast(out_carr, master, in_mat%processor%comm, ierr)
6050 : : end if
6051 : :
6052 [ # # # # : 0 : end subroutine slkmat_sp_collect_cplx
# # # # #
# # # #
# ]
6053 : : !!***
6054 : :
6055 : : !----------------------------------------------------------------------
6056 : :
6057 : : !!****f* m_slk/basemat_get_trace
6058 : : !! NAME
6059 : : !! basemat_get_trace
6060 : : !!
6061 : : !! FUNCTION
6062 : : !! Compute the trace of an N-by-N distributed matrix.
6063 : : !! The result is summed over mat%processor%comm (collective call) and returned as complex(dp)
6064 : : !! also for real and single precision matrices.
6065 : : !!
6066 : : !! SOURCE
6067 : :
6068 : 0 : complex(dp) function basemat_get_trace(mat) result(ctrace)
6069 : :
6070 : : !Arguments ------------------------------------
6071 : : class(basemat_t), intent(in) :: mat
6072 : :
6073 : : !Local variables-------------------------------
6074 : : #ifdef HAVE_LINALG_SCALAPACK
6075 : : integer :: iloc, jloc, iglob, jglob, ierr
6076 : : real(dp) :: rtrace
6077 : : real(sp) :: rtrace_sp
6078 : : complex(sp) :: ctrace_sp
6079 : : #endif
6080 : : ! *************************************************************************
6081 : :
6082 [ # # ]: 0 : ABI_CHECK_IEQ(mat%size_global(1), mat%size_global(2), "get_trace assumes square matrix!")
6083 : :
6084 : : ! prototype for complex version.
6085 : : ! COMPLEX*16 FUNCTION PZLATRA( N, A, IA, JA, DESCA )
6086 : : !
6087 : : ! NOTE: We don't use PZLATRA and friends as these calls can lead to SIGSEGV when
6088 : : ! we compile with gcc and link with intel scalapack (very likely there's an ABI mismatch
6089 : : ! even when the compatibility layer is used --> we implement our own version.
6090 : :
6091 : : #ifdef HAVE_LINALG_SCALAPACK
6092 : : select type (mat)
6093 : : class is (slkmat_dp_t)
6094 : : if (allocated(mat%buffer_cplx)) then
6095 : : !ctrace = PZLATRA(mat%size_global(1), mat%buffer_cplx, 1, 1, mat%desc)
6096 : : ctrace = zero
6097 : : do jloc=1,mat%size_local(2)
6098 : : do iloc=1,mat%size_local(1)
6099 : : call mat%loc2glob(iloc, jloc, iglob, jglob)
6100 : : if (iglob == jglob) ctrace = ctrace + mat%buffer_cplx(iloc, jloc)
6101 : : end do
6102 : : end do
6103 : :
6104 : : else if (allocated(mat%buffer_real)) then
6105 : : !rtrace = PDLATRA(mat%size_global(1), mat%buffer_real, 1, 1, mat%desc)
6106 : : rtrace = zero
6107 : : do jloc=1,mat%size_local(2)
6108 : : do iloc=1,mat%size_local(1)
6109 : : call mat%loc2glob(iloc, jloc, iglob, jglob)
6110 : : if (iglob == jglob) rtrace = rtrace + mat%buffer_real(iloc, jloc)
6111 : : end do
6112 : : end do
6113 : : ctrace = rtrace
6114 : :
6115 : : else
6116 : : ABI_ERROR("Neither buffer_cplx nor buffer_real are allocated!")
6117 : : end if
6118 : :
6119 : : class is (slkmat_sp_t)
6120 : : if (allocated(mat%buffer_cplx)) then
6121 : : !ctrace_sp = PCLATRA(mat%size_global(1), mat%buffer_cplx, 1, 1, mat%desc)
6122 : : ctrace_sp = zero
6123 : : do jloc=1,mat%size_local(2)
6124 : : do iloc=1,mat%size_local(1)
6125 : : call mat%loc2glob(iloc, jloc, iglob, jglob)
6126 : : if (iglob == jglob) ctrace_sp = ctrace_sp + mat%buffer_cplx(iloc, jloc)
6127 : : end do
6128 : : end do
6129 : : ctrace = ctrace_sp
6130 : :
6131 : : else if (allocated(mat%buffer_real)) then
6132 : : !rtrace_sp = PSLATRA(mat%size_global(1), mat%buffer_real, 1, 1, mat%desc)
6133 : : rtrace_sp = zero
6134 : : do jloc=1,mat%size_local(2)
6135 : : do iloc=1,mat%size_local(1)
6136 : : call mat%loc2glob(iloc, jloc, iglob, jglob)
6137 : : if (iglob == jglob) rtrace_sp = rtrace_sp + mat%buffer_real(iloc, jloc)
6138 : : end do
6139 : : end do
6140 : : ctrace = rtrace_sp
6141 : :
6142 : : else
6143 : : ABI_ERROR("Neither buffer_cplx nor buffer_real are allocated!")
6144 : : end if
6145 : :
6146 : : class default
6147 : : ABI_ERROR("Wrong class")
6148 : : end select
6149 : :
6150 : : call xmpi_sum(ctrace, mat%processor%comm, ierr)
6151 : : #endif
6152 : :
6153 : 0 : end function basemat_get_trace
6154 : : !!***
6155 : :
6156 : : !----------------------------------------------------------------------
6157 : :
6158 : : !!****f* m_slk/basemat_set_imag_diago_to_zero
6159 : : !! NAME
6160 : : !! basemat_set_imag_diago_to_zero
6161 : : !!
6162 : : !! FUNCTION
6163 : : !! Set the imaginary part of the diagonal to zero.
6164 : : !! Return in local_max the max of the imaginary part in the local buffer.
6165 : : !! No MPI communication is performed inside the routine. Client code can easily reduce
6166 : : !! local_max within the PBLAS communicator if needed.
6167 : : !!
6168 : : !! OUTPUT
6169 : : !! local_max: Max of the imaginary part (signed, not the absolute value) of the local diagonal entries
6170 : : !! before zeroing. -huge(one) if this process does not own diagonal entries.
6171 : : !!
6172 : : !! SOURCE
6173 : :
6174 : 0 : subroutine basemat_set_imag_diago_to_zero(mat, local_max)
6175 : :
6176 : : !Arguments ------------------------------------
6177 : : class(basemat_t), intent(inout) :: mat
6178 : : real(dp),intent(out) :: local_max
6179 : :
6180 : : !Local variables-------------------------------
6181 : : integer :: il1, iglob1, il2, iglob2
6182 : : ! *************************************************************************
6183 : :
6184 : 0 : local_max = -huge(one)
6185 : :
6186 : : select type (mat)
6187 : : class is (slkmat_dp_t)
6188 [ # # ]: 0 : if (allocated(mat%buffer_real)) return
6189 [ # # ]: 0 : do il2=1,mat%size_local(2)
6190 : 0 : iglob2 = mat%loc2gcol(il2)
6191 [ # # ]: 0 : do il1=1,mat%size_local(1)
6192 : 0 : iglob1 = mat%loc2grow(il1)
6193 [ # # ]: 0 : if (iglob1 == iglob2) then
6194 : 0 : local_max = max(local_max, aimag(mat%buffer_cplx(il1, il2)))
6195 : 0 : mat%buffer_cplx(il1, il2) = real(mat%buffer_cplx(il1, il2))
6196 : : end if
6197 : : end do
6198 : : end do
6199 : :
6200 : : class is (slkmat_sp_t)
6201 [ # # ]: 0 : if (allocated(mat%buffer_real)) return
6202 [ # # ]: 0 : do il2=1,mat%size_local(2)
6203 : 0 : iglob2 = mat%loc2gcol(il2)
6204 [ # # ]: 0 : do il1=1,mat%size_local(1)
6205 : 0 : iglob1 = mat%loc2grow(il1)
6206 [ # # ]: 0 : if (iglob1 == iglob2) then
6207 : 0 : local_max = max(local_max, aimag(mat%buffer_cplx(il1, il2)))
6208 : 0 : mat%buffer_cplx(il1, il2) = real(mat%buffer_cplx(il1, il2))
6209 : : end if
6210 : : end do
6211 : : end do
6212 : :
6213 : : class default
6214 : 0 : ABI_ERROR("Wrong class")
6215 : : end select
6216 : :
6217 : : end subroutine basemat_set_imag_diago_to_zero
6218 : : !!***
6219 : :
6220 : : !----------------------------------------------------------------------
6221 : :
6222 : : !!****f* m_slk/slk_write
6223 : : !! NAME
6224 : : !! slk_write
6225 : : !!
6226 : : !! FUNCTION
6227 : : !! Routine to write a square scaLAPACK-distributed matrix to an external file using MPI-IO.
6228 : : !!
6229 : : !! INPUTS
6230 : : !! Slk_mat<slkmat_dp_t>=Structured datatype defining the scaLAPACK distribution with the local buffer
6231 : : !! containing the distributed matrix.
6232 : : !! uplo=String specifying whether only the upper or lower triangular part of the global matrix is used:
6233 : : !! = "U": Upper triangular
6234 : : !! = "L": Lower triangular
6235 : : !! = "A": Full matrix (used for general complex matrices)
6236 : : !! is_fortran_file=.FALSE. is C stream is used. .TRUE. for writing Fortran binary files.
6237 : : !! [fname]= Mutually exclusive with mpi_fh. The name of the external file on which the matrix will be written.
6238 : : !! The file is open and closed inside the routine with MPI flags specified by flags.
6239 : : !! [mpi_fh]=File handler associated to the file (already open in the caller). Not compatible with fname.
6240 : : !! [flags]=MPI-IO flags used to open the file in MPI_FILE_OPEN.
6241 : : !! Default is MPI_MODE_CREATE + MPI_MODE_WRONLY + MPI_MODE_EXCL.
6242 : : !! [glob_subarray(2,2)] = Used to select the subarray of the global matrix. Used only when uplo="All"
6243 : : !! NOTE that each node should call the routine with the same value.
6244 : : !! glob_subarray(:,1)=starting global coordinates of the subarray in each dimension
6245 : : !! (array of nonnegative integers >=1, <=array_of_sizes)
6246 : : !! glob_subarray(:,2)=Number of elements in each dimension of the subarray (array of positive integers)
6247 : : !!
6248 : : !! OUTPUT
6249 : : !! Only writing. The global scaLAPACK matrix is written to file fname.
6250 : : !! If fname is present then the file is open and closed inside the routine. Any exception is fatal.
6251 : : !!
6252 : : !! SIDE EFFECTS
6253 : : !! [offset]=
6254 : : !! input: Offset used to access the content of the file. Default is zero.
6255 : : !! output: New offset incremented with the byte size of the matrix that has been read (Fortran
6256 : : !! markers are included if is_fortran_file=.TRUE.)
6257 : : !! TODO
6258 : : !! - Generalize the implementation adding the writing the real buffer.
6259 : : !! - This routine should be removed and replaced by hdf5 + mpi-io
6260 : : !!
6261 : : !! SOURCE
6262 : :
6263 : 0 : subroutine slk_write(Slk_mat, uplo, is_fortran_file, fname,mpi_fh, offset, flags, glob_subarray)
6264 : :
6265 : : !Arguments ------------------------------------
6266 : : !scalars
6267 : : integer,optional,intent(in) :: flags
6268 : : integer,optional,intent(inout) :: mpi_fh
6269 : : integer(XMPI_OFFSET_KIND),optional,intent(inout) :: offset
6270 : : logical,intent(in) :: is_fortran_file
6271 : : character(len=*),optional,intent(in) :: fname
6272 : : character(len=*),intent(in) :: uplo
6273 : : class(slkmat_dp_t),intent(in) :: Slk_mat
6274 : : !array
6275 : : integer,optional,intent(in) :: glob_subarray(2,2)
6276 : :
6277 : : !Local variables ------------------------------
6278 : : !scalars
6279 : : #if defined HAVE_LINALG_SCALAPACK && defined HAVE_MPI_IO
6280 : : integer :: jloc,iloc,nrows_glob,ncols_glob,elw,nrows_w,ncols_w ! iglob,jglob,
6281 : : integer :: slk_type,offset_err,etype,nfrec,bsize_elm,mpi_type_elm
6282 : : integer(XMPI_OFFSET_KIND) :: my_offset
6283 : : logical :: do_open
6284 : : integer :: comm,my_flags,my_fh,buffer_size
6285 : : integer :: ierr,nelw,col_glob ! ij_loc,
6286 : : !arrays
6287 : : integer(XMPI_OFFSET_KIND),allocatable :: bsize_frecord(:)
6288 : : integer,pointer :: elw2slk(:,:)
6289 : : complex(dpc),allocatable :: buffer1_cplx(:)
6290 : : character(len=500) :: msg
6291 : : !************************************************************************
6292 : :
6293 : : ABI_CHECK(allocated(Slk_mat%buffer_cplx), "buffer_cplx not allocated")
6294 : :
6295 : : if (firstchar(uplo, ["U","L"]) .and. Slk_mat%size_global(1) /= Slk_mat%size_global(2) ) then
6296 : : ABI_ERROR("rectangular matrices are not compatible with the specified uplo")
6297 : : end if
6298 : :
6299 : : if (PRESENT(glob_subarray).and. .not. firstchar(uplo, ["A"])) then
6300 : : ABI_ERROR("glob_subarray should not be used when uplo/=All")
6301 : : end if
6302 : :
6303 : : do_open = PRESENT(fname)
6304 : : if (do_open) then
6305 : : ABI_CHECK(.not.PRESENT(fname),"fname should not be present")
6306 : : else
6307 : : ABI_CHECK(PRESENT(mpi_fh),"mpi_fh should be present")
6308 : : end if
6309 : :
6310 : : my_offset=0; if (PRESENT(offset)) my_offset=offset
6311 : :
6312 : : comm = Slk_mat%processor%comm
6313 : :
6314 : : nrows_glob=Slk_mat%size_global(1)
6315 : : ncols_glob=Slk_mat%size_global(1)
6316 : : buffer_size= PRODUCT(Slk_mat%size_local(1:2))
6317 : :
6318 : : call slk_mat%bsize_and_type(bsize_elm, mpi_type_elm)
6319 : :
6320 : : if (do_open) then !Open the file.
6321 : : my_flags=MPI_MODE_CREATE + MPI_MODE_WRONLY + MPI_MODE_APPEND
6322 : : if (PRESENT(flags)) my_flags = flags
6323 : :
6324 : : call MPI_FILE_OPEN(comm, fname, my_flags, MPI_INFO_NULL, my_fh, ierr)
6325 : : ABI_CHECK_MPI(ierr, "MPI_FILE_OPEN "//TRIM(fname))
6326 : : else
6327 : : my_fh = mpi_fh
6328 : : end if
6329 : :
6330 : : if (PRESENT(glob_subarray)) then
6331 : : call slk_single_fview_write(Slk_mat,uplo,nelw,elw2slk,etype,slk_type,offset_err,&
6332 : : is_fortran_file=is_fortran_file,glob_subarray=glob_subarray)
6333 : : else
6334 : : call slk_single_fview_write(Slk_mat,uplo,nelw,elw2slk,etype,slk_type,offset_err,&
6335 : : is_fortran_file=is_fortran_file)
6336 : : end if
6337 : :
6338 : : if (offset_err /= 0) then
6339 : : write(msg,"(3a)")&
6340 : : " Global position index cannot be stored in standard Fortran integer ",ch10,&
6341 : : " scaLAPACK matrix cannot be read with a single MPI-IO call."
6342 : : ABI_ERROR(msg)
6343 : : end if
6344 : :
6345 : : call MPI_FILE_SET_VIEW(my_fh, my_offset, etype, slk_type, 'native', MPI_INFO_NULL, ierr)
6346 : : ABI_CHECK_MPI(ierr,"SET_VIEW")
6347 : :
6348 : : call MPI_TYPE_FREE(slk_type,ierr)
6349 : : ABI_CHECK_MPI(ierr,"MPI_type_FREE")
6350 : :
6351 : : if (nelw == buffer_size) then
6352 : : ! Dump Slk_mat% immediately.
6353 : : call MPI_FILE_WRITE_ALL(my_fh, Slk_mat%buffer_cplx, buffer_size, MPI_DOUBLE_complex, MPI_STATUS_IGNORE, ierr)
6354 : : ABI_CHECK_MPI(ierr,"WRITE_ALL")
6355 : : else
6356 : : ! Have to extract the data to be written.
6357 : : ABI_MALLOC(buffer1_cplx,(nelw))
6358 : : do elw=1,nelw
6359 : : iloc = elw2slk(1,elw)
6360 : : jloc = elw2slk(2,elw)
6361 : : buffer1_cplx(elw) = Slk_mat%buffer_cplx(iloc,jloc)
6362 : : end do
6363 : : call MPI_FILE_WRITE_ALL(my_fh, buffer1_cplx, nelw, MPI_DOUBLE_complex, MPI_STATUS_IGNORE, ierr)
6364 : : ABI_CHECK_MPI(ierr,"WRITE_ALL")
6365 : : ABI_FREE(buffer1_cplx)
6366 : : end if
6367 : :
6368 : : ABI_FREE(elw2slk)
6369 : : !
6370 : : ! Number of columns and rows that have been written.
6371 : : ! Used to write the Fortran markers and to increment the offset.
6372 : : nrows_w = nrows_glob
6373 : : ncols_w = ncols_glob
6374 : : if (PRESENT(glob_subarray)) then
6375 : : nrows_w = glob_subarray(1,2) - glob_subarray(1,1) + 1
6376 : : ncols_w = glob_subarray(2,2) - glob_subarray(2,1) + 1
6377 : : if (.not.firstchar(uplo, ["A"])) then
6378 : : ABI_ERROR("glob_subarray should not be used when uplo/=All")
6379 : : end if
6380 : : end if
6381 : :
6382 : : !TODO check whether slk_single_fview_write can report an offset to reduce the extent.
6383 : : if (is_fortran_file) then ! Collective writing of the Fortran markers.
6384 : : nfrec = ncols_w
6385 : : ABI_MALLOC(bsize_frecord,(nfrec))
6386 : : if (firstchar(uplo, ["A"])) then
6387 : : bsize_frecord = nrows_w * bsize_elm
6388 : : else if (firstchar(uplo, ["U"])) then
6389 : : bsize_frecord = (/(col_glob * bsize_elm, col_glob=1,nfrec)/)
6390 : : else if (firstchar(uplo, ["L"])) then
6391 : : bsize_frecord = (/(col_glob * bsize_elm, col_glob=nfrec,1,-1)/)
6392 : : else
6393 : : ABI_ERROR("Wrong uplo")
6394 : : end if
6395 : : call xmpio_write_frmarkers(mpi_fh,my_offset,xmpio_collective,nfrec,bsize_frecord,ierr)
6396 : : ABI_CHECK(ierr==0,"Error while writing Fortran markers")
6397 : : ABI_FREE(bsize_frecord)
6398 : : end if
6399 : :
6400 : : if (do_open) then
6401 : : ! Close the file.
6402 : : call MPI_FILE_CLOSE(my_fh, ierr)
6403 : : ABI_CHECK_MPI(ierr,"FILE_CLOSE")
6404 : : end if
6405 : :
6406 : : ! Increment the offset
6407 : : if (PRESENT(offset)) then
6408 : : if (firstchar(uplo, ["A"])) then
6409 : : offset = offset + nrows_w*ncols_w*bsize_elm
6410 : : if (is_fortran_file) offset = offset + ncols_w*2*xmpio_bsize_frm
6411 : : else if (firstchar(uplo, ["U","L"])) then
6412 : : offset = offset + ( (Slk_mat%size_global(2) * (Slk_mat%size_global(2))+1)/2 ) * bsize_elm
6413 : : if (is_fortran_file) offset = offset + Slk_mat%size_global(2)*2*xmpio_bsize_frm
6414 : : else
6415 : : ABI_ERROR("Wrong uplo")
6416 : : end if
6417 : : end if
6418 : :
6419 : : call xmpi_barrier(comm)
6420 : : RETURN
6421 : :
6422 : : #else
6423 : 0 : ABI_ERROR("MPI-IO support not activated")
6424 : : #endif
6425 : :
6426 : 0 : end subroutine slk_write
6427 : : !!***
6428 : :
6429 : : !----------------------------------------------------------------------
6430 : :
6431 : : !!****f* m_slk/slk_read
6432 : : !! NAME
6433 : : !! slk_read
6434 : : !!
6435 : : !! FUNCTION
6436 : : !! Routine to read a square scaLAPACK distributed matrix from an external file using MPI-IO.
6437 : : !!
6438 : : !! INPUTS
6439 : : !! uplo=String specifying whether only the upper or lower triangular part of the global matrix is stored on disk:
6440 : : !! = "U": Upper triangular is stored
6441 : : !! = "L": Lower triangular is stored
6442 : : !! = "A": Full matrix (used for general complex matrices)
6443 : : !! symtype=Symmetry type of the matrix stored on disk (used only if uplo = "L" or "A").
6444 : : !! = "H" for Hermitian matrix
6445 : : !! = "S" for symmetric matrix.
6446 : : !! = "N" if matrix has no symmetry (not compatible with uplo="L" or uplo="U".
6447 : : !! is_fortran_file=.FALSE. is C stream is used. .TRUE. for writing Fortran binary files.
6448 : : !! [fname]= Mutually exclusive with mpi_fh. The name of the external file from which the matrix will be read.
6449 : : !! The file is open and closed inside the routine with MPI flags specified by flags.
6450 : : !! [mpi_fh]=File handler associated to the file (already open in the caller). Not compatible with fname.
6451 : : !! [flags]=MPI-IO flags used to open the file in MPI_FILE_OPEN. Default is MPI_MODE_RDONLY. Referenced only when fname is used.
6452 : : !!
6453 : : !! SIDE EFFECTS
6454 : : !! Slk_mat<slkmat_dp_t>=Structured datatype defining the scaLAPACK distribution with the local buffer
6455 : : !! supposed to be allocated.
6456 : : !! %buffer_cplx=Local buffer containing the distributed matrix stored on the external file.
6457 : : !! If fname is present then the file is opened and closed inside the routine. Any exception is fatal.
6458 : : !! [offset]=
6459 : : !! input: Offset used to access the content of the file. Default is zero.
6460 : : !! output: New offset incremented with the byte size of the matrix that has been read (Fortran
6461 : : !! markers are included if is_fortran_file=.TRUE.)
6462 : : !!
6463 : : !! TODO
6464 : : !! - Generalize the implementation adding the reading of the real buffer.
6465 : : !!
6466 : : !! - This routine is not portable as this kind of access pattern is not supported by all MPI implementations
6467 : : !! E.g. with MPICH we have
6468 : : !!
6469 : : !! --- !ERROR
6470 : : !! src_file: m_slk.F90
6471 : : !! src_line: 3780
6472 : : !! mpi_rank: 1
6473 : : !! message: |
6474 : : !! SET_VIEW
6475 : : !! Other I/O error , error stack:
6476 : : !! ADIO_Set_view(48): **iobadoverlap displacements of filetype must be in a monotonically nondecreasing order
6477 : : !! ...
6478 : : !!
6479 : : !! - This routine should be removed and replaced by hdf5 + mpi-io
6480 : : !!
6481 : : !! SOURCE
6482 : :
6483 : 0 : subroutine slk_read(Slk_mat,uplo,symtype,is_fortran_file,fname,mpi_fh,offset,flags)
6484 : :
6485 : : !Arguments ------------------------------------
6486 : : !scalars
6487 : : integer,optional,intent(in) :: flags,mpi_fh
6488 : : integer(XMPI_OFFSET_KIND),optional,intent(inout) :: offset
6489 : : character(len=*),optional,intent(in) :: fname
6490 : : character(len=*),intent(in) :: uplo,symtype
6491 : : logical,intent(in) :: is_fortran_file
6492 : : class(slkmat_dp_t),intent(inout) :: Slk_mat
6493 : :
6494 : : !Local variables ------------------------------
6495 : : #if defined HAVE_LINALG_SCALAPACK && defined HAVE_MPI_IO
6496 : : !scalars
6497 : : integer :: nrows_glob,offset_err,slk_type,etype
6498 : : integer(XMPI_OFFSET_KIND) :: my_offset
6499 : : logical :: do_open
6500 : : integer :: comm,my_flags,my_fh,buffer_size,ierr,col_glob
6501 : : integer :: nfrec,bsize_elm,mpi_type_elm
6502 : : !complex(dpc) :: ctest
6503 : : logical,parameter :: check_frm=.TRUE.
6504 : : integer(XMPI_OFFSET_KIND),allocatable :: bsize_frecord(:)
6505 : : !arrays
6506 : : character(len=500) :: msg
6507 : : !************************************************************************
6508 : :
6509 : : do_open = PRESENT(fname)
6510 : : if (do_open) then
6511 : : ABI_CHECK(.not.PRESENT(fname), "fname should not be present")
6512 : : else
6513 : : ABI_CHECK(PRESENT(mpi_fh), "mpi_fh should be present")
6514 : : end if
6515 : :
6516 : : my_offset=0; if (PRESENT(offset)) my_offset=offset
6517 : :
6518 : : ABI_CHECK(allocated(Slk_mat%buffer_cplx), "%buffer_cplx not allocated")
6519 : : if (firstchar(uplo, ["U","L"]) .and. Slk_mat%size_global(1) /= Slk_mat%size_global(2) ) then
6520 : : ABI_ERROR("rectangular matrices are not compatible with the specified uplo")
6521 : : end if
6522 : :
6523 : : nrows_glob = Slk_mat%size_global(1)
6524 : :
6525 : : buffer_size= PRODUCT(Slk_mat%size_local(1:2))
6526 : :
6527 : : call wrtout(std_out, "slk_read: Using MPI-IO")
6528 : :
6529 : : comm = Slk_mat%processor%comm
6530 : :
6531 : : if (do_open) then ! Open the file.
6532 : : my_flags=MPI_MODE_RDONLY; if (PRESENT(flags)) my_flags = flags
6533 : : call MPI_FILE_OPEN(comm, fname, my_flags, MPI_INFO_NULL, my_fh, ierr)
6534 : : ABI_CHECK_MPI(ierr,"FILE_OPEN "//TRIM(fname))
6535 : : else
6536 : : my_fh = mpi_fh
6537 : : end if
6538 : :
6539 : : call slk_single_fview_read(Slk_mat,uplo,etype,slk_type,offset_err,is_fortran_file=is_fortran_file)
6540 : :
6541 : : if (offset_err/=0) then
6542 : : write(msg,"(3a)")&
6543 : : "Global position index cannot be stored in standard Fortran integer ",ch10,&
6544 : : "scaLAPACK matrix cannot be read with a single MPI-IO call."
6545 : : ABI_ERROR(msg)
6546 : : end if
6547 : :
6548 : : call MPI_FILE_SET_VIEW(my_fh, my_offset, etype, slk_type, 'native', MPI_INFO_NULL, ierr)
6549 : : ABI_CHECK_MPI(ierr,"SET_VIEW")
6550 : :
6551 : : call MPI_FILE_READ_ALL(my_fh, Slk_mat%buffer_cplx, buffer_size, MPI_DOUBLE_complex, MPI_STATUS_IGNORE, ierr)
6552 : : ABI_CHECK_MPI(ierr,"READ_ALL")
6553 : :
6554 : : ! Symmetrize local buffer if uplo /= "All"
6555 : : call Slk_mat%symmetrize(uplo, symtype)
6556 : :
6557 : : !BEGINDEBUG
6558 : : !call MPI_FILE_READ_AT(mpi_fh,my_offset+xmpio_bsize_frm,ctest,1,MPI_DOUBLE_complex,MPI_STATUS_IGNORE,ierr)
6559 : : !write(std_out,*)"ctest",ctest
6560 : : !call MPI_FILE_READ_AT(mpi_fh,my_offset+2*xmpio_bsize_frm,ctest,1,MPI_DOUBLE_complex,MPI_STATUS_IGNORE,ierr)
6561 : : !write(std_out,*)"ctest",ctest
6562 : : !ENDDEBUG
6563 : :
6564 : : !call print_arr(Slk_mat%buffer_cplx,max_r=10,max_c=10,unit=std_out)
6565 : : !
6566 : : ! Close the file and release the MPI filetype.
6567 : : call MPI_type_FREE(slk_type,ierr)
6568 : : ABI_CHECK_MPI(ierr,"MPI_type_FREE")
6569 : :
6570 : : call slk_mat%bsize_and_type(bsize_elm, mpi_type_elm)
6571 : :
6572 : : !It seems that personal call makes the code stuck
6573 : : !if (is_fortran_file .and. check_frm .and. Slk_mat%Processor%my_rank==0) then ! Master checks the Fortran markers.
6574 : : if (is_fortran_file .and. check_frm) then ! Master checks the Fortran markers.
6575 : : call wrtout(std_out,"Checking Fortran record markers...", do_flush=.True.)
6576 : : nfrec = Slk_mat%size_global(2)
6577 : : ABI_MALLOC(bsize_frecord,(nfrec))
6578 : : if (firstchar(uplo, ["A"])) then
6579 : : bsize_frecord = Slk_mat%size_global(1) * bsize_elm
6580 : : else if (firstchar(uplo, ["U"])) then
6581 : : bsize_frecord = (/(col_glob * bsize_elm, col_glob=1,nfrec)/)
6582 : : else if (firstchar(uplo, ["L"])) then
6583 : : bsize_frecord = (/(col_glob * bsize_elm, col_glob=nfrec,1,-1)/)
6584 : : else
6585 : : ABI_ERROR("Wrong uplo")
6586 : : end if
6587 : : call xmpio_check_frmarkers(my_fh,my_offset,xmpio_collective,nfrec,bsize_frecord,ierr)
6588 : : ABI_CHECK(ierr==0,"Wrong Fortran record markers")
6589 : : ABI_FREE(bsize_frecord)
6590 : : end if
6591 : :
6592 : : if (do_open) then ! Close the file.
6593 : : call MPI_FILE_CLOSE(my_fh, ierr)
6594 : : ABI_CHECK_MPI(ierr,"FILE_CLOSE")
6595 : : end if
6596 : :
6597 : : !Increment the offset
6598 : : if (PRESENT(offset)) then
6599 : : if (firstchar(uplo, ["A"])) then
6600 : : offset = offset + PRODUCT(Slk_mat%size_global(1:2)) * bsize_elm
6601 : : if (is_fortran_file) offset = offset + Slk_mat%size_global(2)*2*xmpio_bsize_frm
6602 : : else if (firstchar(uplo, ["U","L"])) then
6603 : : offset = offset + ( (Slk_mat%size_global(2) * (Slk_mat%size_global(2))+1)/2 ) * bsize_elm
6604 : : if (is_fortran_file) offset = offset + Slk_mat%size_global(2)*2*xmpio_bsize_frm
6605 : : else
6606 : : ABI_ERROR("Wrong uplo")
6607 : : end if
6608 : : end if
6609 : :
6610 : : call xmpi_barrier(comm)
6611 : : RETURN
6612 : :
6613 : : #else
6614 : 0 : ABI_ERROR("MPI-IO support not enabled")
6615 : : #endif
6616 : :
6617 : 0 : end subroutine slk_read
6618 : : !!***
6619 : :
6620 : : !----------------------------------------------------------------------
6621 : :
6622 : : !!****f* m_slk/slk_single_fview_read_mask
6623 : : !! NAME
6624 : : !! slk_single_fview_read_mask
6625 : : !!
6626 : : !! FUNCTION
6627 : : !! Return an MPI datatype that can be used to read a scaLAPACK distributed matrix from
6628 : : !! a binary file using MPI-IO. The view is created using the user-defined mask function
6629 : : !! mask_of_glob. The storage of the data on file is described via the user-defined function offset_of_glob.
6630 : : !!
6631 : : !! INPUTS
6632 : : !! Slk_mat<slkmat_dp_t>=Structured datatype defining the scaLAPACK matrix.
6633 : : !! mask_of_glob(row_glob,col_glob,size_glob) is an integer function that accepts in input
6634 : : !! the global indices of the matrix size_glob(1:2) are the global dimensions.
6635 : : !! Return 0 if (row_glob,col_glob) should not be read.
6636 : : !! offset_of_glob(row_glob,col_glob,size_glob,nsblocks,sub_block,bsize_elm,bsize_frm)
6637 : : !! nsblocks=Number of sub-blocks (will be passed to offset_of_glob)
6638 : : !! sub_block(2,2,nsblocks)=Global coordinates of the extremal points delimiting the sub-blocs
6639 : : !! e.g. sub_block(:,1,1) gives the coordinates of the left upper corner of the first block.
6640 : : !! sub_block(:,2,1) gives the coordinates of the right lower corner of the first block.
6641 : : !! [is_fortran_file]=.FALSE. is C stream is used. Set to .TRUE. for writing Fortran binary files
6642 : : !! with record marker.
6643 : : !!
6644 : : !! OUTPUT
6645 : : !! my_nel=Number of elements that will be read by this node.
6646 : : !! etype=Elementary data type (handle) defining the elementary unit used to access the file.
6647 : : !! This is the elementary type that must be used to create the view (MPI_BYTE is used).
6648 : : !! slk_type=New MPI type that can be used to instantiate the MPI-IO view for the Fortran file.
6649 : : !! Note that the view assumes that the file pointer points to the FIRST Fortran record marker.
6650 : : !! offset_err=Error code. A returned non-zero value signals that the global matrix is too large
6651 : : !! for a single MPI-IO access. See notes in other slk_single_fview_* routines.
6652 : : !!
6653 : : !! SIDE EFFECTS
6654 : : !! myel2loc(:,:)
6655 : : !! input: pointer to NULL
6656 : : !! output: myel2loc(2,my_nel): myel2loc(:,el) gives (iloc,jloc) for el=1,my_nel.
6657 : : !!
6658 : : !! SOURCE
6659 : :
6660 : 0 : subroutine slk_single_fview_read_mask(Slk_mat,mask_of_glob,offset_of_glob,nsblocks,sub_block,&
6661 : : my_nel,myel2loc,etype,slk_type,offset_err,is_fortran_file)
6662 : :
6663 : : !Arguments ------------------------------------
6664 : : !scalars
6665 : : integer,intent(in) :: nsblocks
6666 : : integer,intent(out) :: my_nel,offset_err,slk_type,etype
6667 : : logical,optional,intent(in) :: is_fortran_file
6668 : : class(slkmat_dp_t),intent(in) :: Slk_mat
6669 : : !arrays
6670 : : integer,intent(in) :: sub_block(2,2,nsblocks)
6671 : : integer,pointer :: myel2loc(:,:)
6672 : :
6673 : : interface
6674 : : function mask_of_glob(row_glob,col_glob,size_glob)
6675 : : use defs_basis
6676 : : integer :: mask_of_glob
6677 : : integer,intent(in) :: row_glob,col_glob
6678 : : integer,intent(in) :: size_glob(2)
6679 : : end function mask_of_glob
6680 : : end interface
6681 : :
6682 : : interface
6683 : : function offset_of_glob(row_glob,col_glob,size_glob,nsblocks,sub_block,bsize_elm,bsize_frm)
6684 : : use defs_basis
6685 : : use m_xmpi
6686 : : integer(XMPI_OFFSET_KIND) :: offset_of_glob
6687 : : integer,intent(in) :: row_glob,col_glob,bsize_elm,bsize_frm,nsblocks
6688 : : integer,intent(in) :: size_glob(2),sub_block(2,2,nsblocks)
6689 : : end function offset_of_glob
6690 : : end interface
6691 : :
6692 : : !Local variables ------------------------------
6693 : : !scalars
6694 : : integer :: el,jloc,iloc,iglob,jglob,mpi_err,sweep
6695 : : integer :: bsize_frm,mpi_type_elm,bsize_elm
6696 : : integer(XMPI_OFFSET_KIND) :: tmp_off,max_displ
6697 : : !arrays
6698 : : character(len=500) :: msg
6699 : 0 : integer,allocatable :: block_length(:),block_type(:)
6700 : 0 : integer(XMPI_ADDRESS_KIND),allocatable :: block_displ(:)
6701 : : !************************************************************************
6702 : :
6703 : : #ifdef HAVE_MPI_IO
6704 : 0 : bsize_frm = xmpio_bsize_frm ! Byte size of the Fortran record marker.
6705 [ # # ]: 0 : if (PRESENT(is_fortran_file)) then
6706 [ # # ]: 0 : if (.not.is_fortran_file) bsize_frm = 0
6707 : : end if
6708 : :
6709 : : ! Byte size of the matrix element.
6710 : 0 : call slk_mat%bsize_and_type(bsize_elm, mpi_type_elm)
6711 : :
6712 : : ! Find the number of local matrix elements to be read, then create the table myel2loc.
6713 [ # # ]: 0 : do sweep=1,2
6714 [ # # ]: 0 : if (sweep==2) then
6715 [ # # # # : 0 : ABI_MALLOC(myel2loc,(2,my_nel))
# # # # #
# # # ]
6716 : : end if
6717 : 0 : my_nel=0
6718 : :
6719 [ # # ]: 0 : do jloc=1,Slk_mat%size_local(2)
6720 [ # # ]: 0 : do iloc=1,Slk_mat%size_local(1)
6721 : 0 : call slk_mat%loc2glob(iloc, jloc, iglob, jglob)
6722 [ # # ]: 0 : if ( mask_of_glob(iglob,jglob,Slk_mat%size_global)/= 0) then ! Will fill this entry.
6723 : 0 : my_nel = my_nel+1
6724 [ # # # # ]: 0 : if (sweep==2) myel2loc(:,my_nel) = (/iloc,jloc/)
6725 : : end if
6726 : : end do
6727 : : end do
6728 : : end do
6729 : :
6730 : 0 : etype = MPI_BYTE
6731 : :
6732 : : ! Define the mapping between scaLAPACK buffer and the storage on file.
6733 : : ! Note that the view assumes that the file pointer points to the first Fortran record marker.
6734 [ # # # # : 0 : ABI_MALLOC(block_length,(my_nel+2))
# # # # #
# # # #
# ]
6735 [ # # # # : 0 : ABI_MALLOC(block_displ,(my_nel+2))
# # # # #
# # # ]
6736 [ # # # # : 0 : ABI_MALLOC(block_type,(my_nel+2))
# # # # ]
6737 : 0 : block_length(1)=1
6738 : 0 : block_displ (1)=0
6739 : 0 : block_type (1)=MPI_LB
6740 : :
6741 : 0 : offset_err=0; max_displ=0
6742 [ # # ]: 0 : do el=1,my_nel
6743 : 0 : iloc = myel2loc(1,el)
6744 : 0 : jloc = myel2loc(2,el)
6745 : 0 : call slk_mat%loc2glob(iloc, jloc, iglob, jglob)
6746 : 0 : tmp_off = offset_of_glob(iglob,jglob,Slk_mat%size_global,nsblocks,sub_block,bsize_elm,bsize_frm)
6747 [ # # ]: 0 : if (xmpio_max_address(tmp_off)) offset_err=1 ! Test for possible wraparounds.
6748 : 0 : max_displ = MAX(max_displ,tmp_off)
6749 : 0 : block_displ (el+1) = tmp_off
6750 : 0 : block_type (el+1) = mpi_type_elm
6751 : 0 : block_length(el+1) = 1
6752 : : !write(std_out,*)" iglob, jglob, tmp_off ",iglob, jglob, tmp_off
6753 : : end do
6754 : : !write(std_out,*)" MAX displ is ",MAXVAL(block_displ)
6755 : :
6756 [ # # ]: 0 : if (offset_err/=0) then ! just warn, let the caller handle the exception.
6757 : : write(msg,"(3a)")&
6758 : 0 : " Global position index cannot be stored in standard Fortran integer ",ch10,&
6759 : 0 : " scaLAPACK matrix cannot be read with a single MPI-IO call ."
6760 : 0 : ABI_WARNING(msg)
6761 : : end if
6762 : :
6763 : 0 : block_length(my_nel+2) = 1
6764 : 0 : block_displ (my_nel+2) = max_displ
6765 : 0 : block_type (my_nel+2) = MPI_UB
6766 : :
6767 : 0 : call xmpio_type_struct(my_nel+2,block_length,block_displ,block_type,slk_type,mpi_err)
6768 : 0 : ABI_CHECK_MPI(mpi_err,"MPI_type_STRUCT")
6769 : :
6770 : 0 : ABI_FREE(block_length)
6771 : 0 : ABI_FREE(block_displ)
6772 : 0 : ABI_FREE(block_type)
6773 : :
6774 : 0 : call MPI_type_COMMIT(slk_type,mpi_err)
6775 : 0 : ABI_CHECK_MPI(mpi_err,"MPI_type_COMMIT")
6776 : :
6777 : : #else
6778 : : ABI_ERROR("MPI-IO is mandatatory in slk_single_fview_read_mask")
6779 : : #endif
6780 : :
6781 : 0 : end subroutine slk_single_fview_read_mask
6782 : : !!***
6783 : :
6784 : : !----------------------------------------------------------------------
6785 : :
6786 : : !!****f* m_slk/slkmat_dp_symmetrize
6787 : : !! NAME
6788 : : !! slkmat_dp_symmetrize
6789 : : !!
6790 : : !! FUNCTION
6791 : : !! Symmetrize a square scaLAPACK matrix.
6792 : : !!
6793 : : !! INPUTS
6794 : : !! uplo=String specifying whether only the upper or lower triangular part of the global matrix has been read
6795 : : !! = "U": Upper triangular has been read.
6796 : : !! = "L": Lower triangular has been read.
6797 : : !! = "A": Full matrix (used for general complex matrices)
6798 : : !! symtype=Symmetry type of the matrix (used only if uplo = "L" or "A").
6799 : : !! = "H" for Hermitian matrix
6800 : : !! = "S" for symmetric matrix.
6801 : : !! = "N" if matrix has no symmetry (not compatible with uplo="L" or uplo="U").
6802 : : !!
6803 : : !! SIDE EFFECTS
6804 : : !! Slk_mat<slkmat_dp_t>=Structured datatype defining the scaLAPACK distribution with the local buffer
6805 : : !! supposed to be allocated.
6806 : : !! %buffer_cplx=Local buffer containing the distributed matrix. On exit, the missing triangle is filled.
6807 : : !!
6808 : : !! SOURCE
6809 : :
6810 : 0 : subroutine slkmat_dp_symmetrize(Slk_mat, uplo, symtype)
6811 : :
6812 : : !Arguments ------------------------------------
6813 : : !scalars
6814 : : class(slkmat_dp_t),intent(inout) :: Slk_mat
6815 : : character(len=*),intent(in) :: uplo, symtype
6816 : :
6817 : : !Local variables ------------------------------
6818 : : !scalars
6819 : : integer :: jloc,iloc,iglob,jglob,ij_loc
6820 : : logical :: is_hermitian,is_real,is_cplx,is_symmetric
6821 : : character(len=500) :: msg
6822 : : !************************************************************************
6823 : :
6824 : 0 : is_cplx = (allocated(Slk_mat%buffer_cplx))
6825 : 0 : is_real = (allocated(Slk_mat%buffer_real))
6826 : :
6827 : : ! One and only one buffer should be allocated.
6828 [ # # ]: 0 : if (is_real .and. is_cplx) then
6829 : 0 : write(msg,'(a,2l1)')" ScaLAPACK buffers are not allocated correctly, is_real, is_cplx ",is_real,is_cplx
6830 : 0 : ABI_ERROR(msg)
6831 : : end if
6832 : :
6833 [ # # ]: 0 : if (is_real) RETURN
6834 : :
6835 : 0 : is_hermitian=.FALSE.; is_symmetric=.FALSE.
6836 : : select case (symtype(1:1))
6837 : : case ("H", "h")
6838 : : is_hermitian = .TRUE.
6839 : : case ("S","s")
6840 : 0 : is_symmetric = .TRUE.
6841 : : case("N","n")
6842 [ # # # # : 0 : if (ALL(uplo(1:1) /= ["A","a"])) then
# # ]
6843 [ # # ]: 0 : msg = " Found symtype= "//TRIM(symtype)//", but uplo= "//TRIM(uplo)
6844 : 0 : ABI_ERROR(msg)
6845 : : end if
6846 : 0 : RETURN ! Nothing to do.
6847 : : case default
6848 [ # # # # : 0 : ABI_ERROR("Wrong symtype "//TRIM(symtype))
# # ]
6849 : : end select
6850 : :
6851 : : !write(std_out,*)"is_cplx",is_cplx
6852 : : !write(std_out,*)"is_hermitian",is_hermitian
6853 : :
6854 : : select case (uplo(1:1))
6855 : : case ("A","a")
6856 : : ! Full global matrix has been read, nothing to do.
6857 : 0 : return
6858 : :
6859 : : case ("U", "u")
6860 : : ! Only the upper triangle of the global matrix was read.
6861 [ # # # # ]: 0 : if (is_cplx .and. is_hermitian) then
6862 : 0 : ij_loc=0
6863 [ # # ]: 0 : do jloc=1,Slk_mat%size_local(2)
6864 [ # # ]: 0 : do iloc=1,Slk_mat%size_local(1)
6865 : 0 : call slk_mat%loc2glob(iloc, jloc, iglob, jglob)
6866 : 0 : ij_loc = ij_loc+1
6867 [ # # ]: 0 : if (jglob < iglob) then
6868 : : ! Diagonal elements are not forced to be real.
6869 : 0 : Slk_mat%buffer_cplx(iloc,jloc) = DCONJG(Slk_mat%buffer_cplx(iloc,jloc))
6870 : : end if
6871 : : !if (iglob==jglob) Slk_mat%buffer_cplx(iloc,jloc) = real(Slk_mat%buffer_cplx(iloc,jloc))
6872 : : end do
6873 : : end do
6874 : : end if
6875 : :
6876 : : case ("L", "l")
6877 : : ! Only the lower triangle of the global matrix was read.
6878 [ # # # # ]: 0 : if (is_cplx .and. is_hermitian) then
6879 : 0 : ij_loc=0
6880 [ # # ]: 0 : do jloc=1,Slk_mat%size_local(2)
6881 [ # # ]: 0 : do iloc=1,Slk_mat%size_local(1)
6882 : 0 : call slk_mat%loc2glob(iloc, jloc, iglob, jglob)
6883 : 0 : ij_loc = ij_loc+1
6884 [ # # ]: 0 : if (jglob>iglob) then ! diagonal elements are not forced to be real.
6885 : 0 : Slk_mat%buffer_cplx(iloc,jloc) = DCONJG(Slk_mat%buffer_cplx(iloc,jloc))
6886 : : end if
6887 : : !if (iglob==jglob) Slk_mat%buffer_cplx(iloc,jloc) = real(Slk_mat%buffer_cplx(iloc,jloc))
6888 : : end do
6889 : : end do
6890 : : end if
6891 : :
6892 : : case default
6893 [ # # # # : 0 : ABI_BUG(" Wrong uplo: "//TRIM(uplo))
# # ]
6894 : : end select
6895 : :
6896 : : end subroutine slkmat_dp_symmetrize
6897 : : !!***
6898 : :
6899 : : !----------------------------------------------------------------------
6900 : :
6901 : : !!****f* m_slk/slk_single_fview_read
6902 : : !! NAME
6903 : : !! slk_single_fview_read
6904 : : !!
6905 : : !! FUNCTION
6906 : : !! Return an MPI datatype that can be used to read a scaLAPACK distributed matrix from
6907 : : !! a binary file using MPI-IO.
6908 : : !!
6909 : : !! INPUTS
6910 : : !! Slk_mat<slkmat_dp_t>=Structured datatype defining the scaLAPACK distribution with the local buffer.
6911 : : !! uplo=String specifying whether only the upper or lower triangular part of the global matrix is stored on disk:
6912 : : !! = "U": Upper triangular is stored
6913 : : !! = "L": Lower triangular is stored
6914 : : !! = "A": Full matrix (used for general complex matrices)
6915 : : !! [is_fortran_file]=.FALSE. is C stream is used. .TRUE. for writing Fortran binary files
6916 : : !! with record markers. In this case etype is set xmpio_mpi_type_frm provided that
6917 : : !! the mpi_type of the matrix element is commensurate with xmpio_mpi_type_frm. Defaults to .TRUE.
6918 : : !!
6919 : : !! OUTPUT
6920 : : !! etype=Elementary data type (handle) defining the elementary unit used to access the file.
6921 : : !! slk_type=New MPI type that can be used to instantiate the MPI-IO view for the Fortran file.
6922 : : !! Note that the view assumes that the file pointer points to the FIRST Fortran record marker.
6923 : : !! offset_err=Error code. A non-zero value signals that the global matrix is too large
6924 : : !! for a single MPI-IO access (see notes below).
6925 : : !!
6926 : : !! NOTES
6927 : : !! With (signed) Fortran integers, the maximum size of the file that
6928 : : !! that can be read in one-shot is around 2Gb when etype is set to byte.
6929 : : !! Using a larger etype might create portability problems (real data on machines using
6930 : : !! integer*16 for the marker) since etype must be a multiple of the Fortran record marker
6931 : : !! Due to the above reason, block_displ is given in bytes and must be stored in a integer
6932 : : !! of kind XMPI_ADDRESS_KIND. If the displacement is too large, the routine returns
6933 : : !! offset_err=1 so that the caller will know that several MPI-IO reads are needed to
6934 : : !! read the local buffer.
6935 : : !!
6936 : : !! SOURCE
6937 : :
6938 : 0 : subroutine slk_single_fview_read(Slk_mat,uplo,etype,slk_type,offset_err,is_fortran_file)
6939 : :
6940 : : !Arguments ------------------------------------
6941 : : !scalars
6942 : : class(slkmat_dp_t),intent(in) :: Slk_mat
6943 : : integer,intent(out) :: offset_err,slk_type,etype
6944 : : character(len=*),intent(in) :: uplo
6945 : : logical,optional,intent(in) :: is_fortran_file
6946 : :
6947 : : !Local variables ------------------------------
6948 : : !scalars
6949 : : integer :: jloc,iloc,iglob,jglob,nrows_glob,ncols_glob,mpi_err,nel
6950 : : integer :: bsize_frm,mpi_type_elm,ij_loc,bsize_etype,bsize_elm
6951 : : integer(XMPI_OFFSET_KIND) :: ijp_glob,my_offset,cpad_frm
6952 : : !arrays
6953 : : character(len=500) :: msg
6954 : 0 : integer,allocatable :: block_length(:),block_type(:)
6955 : 0 : integer(XMPI_ADDRESS_KIND),allocatable :: block_displ(:)
6956 : : !************************************************************************
6957 : :
6958 : : #ifdef HAVE_MPI_IO
6959 : : !@slkmat_dp_t
6960 : 0 : bsize_frm = xmpio_bsize_frm ! Byte size of the Fortran record marker.
6961 [ # # ]: 0 : if (PRESENT(is_fortran_file)) then
6962 [ # # ]: 0 : if (.not.is_fortran_file) bsize_frm = 0
6963 : : end if
6964 : :
6965 : 0 : call slk_mat%bsize_and_type(bsize_elm, mpi_type_elm)
6966 : :
6967 : : ! Global dimensions.
6968 : 0 : nrows_glob=Slk_mat%size_global(1)
6969 : 0 : ncols_glob=Slk_mat%size_global(2)
6970 : :
6971 : : ! Number of matrix elements treated by this node.
6972 [ # # ]: 0 : nel = PRODUCT(Slk_mat%size_local(1:2))
6973 : :
6974 : : !Cannot use MPI_type_CREATE_INDEXED_BLOCK since it is not correctly implemented in several MPI libraries.
6975 : : !etype has to be set to MPI_BYTE, since the displacement in MPI structures is always in byte.
6976 : : ! ABI_WARNING("Using MPI_type_STRUCT for the MPI-IO file view")
6977 : :
6978 : 0 : etype = MPI_BYTE
6979 : 0 : call MPI_type_SIZE(etype,bsize_etype,mpi_err)
6980 : :
6981 : : ! Define the mapping between scaLAPACK buffer and the storage on file.
6982 [ # # # # : 0 : ABI_MALLOC(block_length, (nel+2))
# # # # #
# # # #
# ]
6983 [ # # # # : 0 : ABI_MALLOC(block_displ, (nel+2))
# # # # #
# # # ]
6984 [ # # # # : 0 : ABI_MALLOC(block_type, (nel+2))
# # # # ]
6985 : 0 : block_length(1)=1
6986 : 0 : block_displ (1)=0
6987 : 0 : block_type (1)=MPI_LB
6988 : :
6989 : : ! Note that the view assumes that the file pointer points to the first Fortran record marker.
6990 : 0 : offset_err=0
6991 : 0 : select case (uplo(1:1))
6992 : : case ("A","a")
6993 : : ! The entire global matrix is stored on disk.
6994 : : ! TODO can use contiguous vectors for better access.
6995 : 0 : ij_loc=0
6996 [ # # ]: 0 : do jloc=1,Slk_mat%size_local(2)
6997 [ # # ]: 0 : do iloc=1,Slk_mat%size_local(1)
6998 : 0 : call slk_mat%loc2glob(iloc, jloc, iglob, jglob)
6999 : 0 : ij_loc = ij_loc+1
7000 : 0 : my_offset = 2*(jglob-1)*bsize_frm + bsize_frm + (jglob-1)*nrows_glob*bsize_elm + (iglob-1) * bsize_elm
7001 : 0 : my_offset = my_offset / bsize_etype
7002 [ # # ]: 0 : if (xmpio_max_address(my_offset)) offset_err=1 ! Test for possible wraparounds
7003 : 0 : block_displ (ij_loc+1) = my_offset
7004 : 0 : block_type (ij_loc+1) = mpi_type_elm
7005 : 0 : block_length(ij_loc+1) = 1
7006 : : end do
7007 : : end do
7008 : :
7009 : : case ("U","u")
7010 : : ! Only the upper triangle of the global matrix is stored on disk.
7011 : 0 : ij_loc=0
7012 [ # # ]: 0 : do jloc=1,Slk_mat%size_local(2)
7013 [ # # ]: 0 : do iloc=1,Slk_mat%size_local(1)
7014 : 0 : call slk_mat%loc2glob(iloc, jloc, iglob, jglob)
7015 [ # # ]: 0 : if (jglob>=iglob) then
7016 : 0 : ijp_glob = iglob + jglob*(jglob-1)/2 ! Index for packed form
7017 : 0 : cpad_frm = 2*(jglob-1)*bsize_frm
7018 : : else
7019 : 0 : ijp_glob = jglob + iglob*(iglob-1)/2 ! Index for packed form
7020 : 0 : cpad_frm = 2*(iglob-1)*bsize_frm
7021 : : end if
7022 : 0 : ij_loc = ij_loc+1
7023 : 0 : my_offset = cpad_frm + bsize_frm + (ijp_glob-1) * bsize_elm
7024 : 0 : my_offset = my_offset / bsize_etype
7025 [ # # ]: 0 : if (xmpio_max_address(my_offset)) offset_err=1 ! Test for possible wraparounds
7026 : 0 : block_displ (ij_loc+1) = my_offset
7027 : 0 : block_type (ij_loc+1) = mpi_type_elm
7028 : 0 : block_length(ij_loc+1) = 1
7029 : : end do
7030 : : end do
7031 : :
7032 : : case ("L","l")
7033 : : ! Only the lower triangle of the global matrix is stored on disk.
7034 : 0 : ij_loc=0
7035 [ # # ]: 0 : do jloc=1,Slk_mat%size_local(2)
7036 [ # # ]: 0 : do iloc=1,Slk_mat%size_local(1)
7037 : 0 : call slk_mat%loc2glob(iloc, jloc, iglob, jglob)
7038 [ # # ]: 0 : if (jglob<=iglob) then
7039 : 0 : ijp_glob = iglob + (jglob-1)*(2*nrows_glob-jglob)/2 ! Index for packed form
7040 : 0 : cpad_frm = 2*(jglob-1)*bsize_frm
7041 : : else
7042 : 0 : ijp_glob = jglob + (iglob-1)*(2*nrows_glob-iglob)/2 ! Index for packed form
7043 : 0 : cpad_frm = 2*(iglob-1)*bsize_frm
7044 : : end if
7045 : 0 : ij_loc = ij_loc+1
7046 : 0 : my_offset = cpad_frm + bsize_frm + (ijp_glob-1) * bsize_elm
7047 : 0 : my_offset = my_offset / bsize_etype
7048 [ # # ]: 0 : if (xmpio_max_address(my_offset)) offset_err=1 ! block_displ is usually integer*4. Test for possible wraparounds
7049 : 0 : block_displ (ij_loc+1) = my_offset
7050 : 0 : block_type (ij_loc+1) = mpi_type_elm
7051 : 0 : block_length(ij_loc+1) = 1
7052 : : end do
7053 : : end do
7054 : :
7055 [ # # ]: 0 : if (offset_err/=0) then ! just warn, let the caller handle the exception.
7056 : : write(msg,"(3a)")&
7057 : 0 : " Global position index cannot be stored in standard Fortran integer ",ch10,&
7058 : 0 : " scaLAPACK matrix cannot be read with a single MPI-IO call ."
7059 : 0 : ABI_WARNING(msg)
7060 : : end if
7061 : :
7062 : : case default
7063 [ # # # # : 0 : ABI_BUG(" Wrong uplo: "//TRIM(uplo))
# # ]
7064 : : end select
7065 : :
7066 : 0 : block_length(nel+2)= 1
7067 : 0 : block_displ (nel+2)= ncols_glob * (nrows_glob*bsize_elm + 2*bsize_frm) / bsize_etype
7068 : 0 : block_type (nel+2)= MPI_UB
7069 : :
7070 : 0 : call xmpio_type_struct(nel+2,block_length,block_displ,block_type,slk_type,mpi_err)
7071 : 0 : ABI_CHECK_MPI(mpi_err,"MPI_type_STRUCT")
7072 : :
7073 : 0 : ABI_FREE(block_length)
7074 : 0 : ABI_FREE(block_displ)
7075 : 0 : ABI_FREE(block_type)
7076 : :
7077 : 0 : call MPI_type_COMMIT(slk_type,mpi_err)
7078 : 0 : ABI_CHECK_MPI(mpi_err,"MPI_type_COMMIT")
7079 : :
7080 : : #else
7081 : : ABI_ERROR("MPI-IO is mandatatory in slk_single_fview_read")
7082 : : #endif
7083 : :
7084 : 0 : end subroutine slk_single_fview_read
7085 : : !!***
7086 : :
7087 : : !----------------------------------------------------------------------
7088 : :
7089 : : !!****f* m_slk/slk_single_fview_write
7090 : : !! NAME
7091 : : !! slk_single_fview_write
7092 : : !!
7093 : : !! FUNCTION
7094 : : !! Returns an MPI datatype that can be used to write a scaLAPACK distributed matrix to
7095 : : !! a binary file using MPI-IO.
7096 : : !!
7097 : : !! INPUTS
7098 : : !! Slk_mat<slkmat_dp_t>=Structured datatype defining the scaLAPACK distribution with the local buffer.
7099 : : !! uplo=String specifying whether only the upper or lower triangular part of the global matrix is stored on disk:
7100 : : !! = "U": Upper triangular is stored
7101 : : !! = "L": Lower triangular is stored
7102 : : !! = "A": Full matrix (used for general complex matrices)
7103 : : !! [is_fortran_file]=.FALSE. is C stream is used. .TRUE. for writing Fortran binary files
7104 : : !! with record marker. In this case etype is set xmpio_mpi_type_frm provided that
7105 : : !! the mpi_type of the matrix element is commensurate with xmpio_mpi_type_frm. Defaults to .TRUE.
7106 : : !! glob_subarray(2,2) = Used to select the subarray of the global matrix. Used only when uplo="All"
7107 : : !! glob_subarray(:,1)=starting global coordinates of the subarray in each dimension
7108 : : !! (array of nonnegative integers >=1, <=array_of_sizes)
7109 : : !! glob_subarray(:,2)=Number of elements in each dimension of the subarray (array of positive integers)
7110 : : !!
7111 : : !! OUTPUT
7112 : : !! nelw=Number of elements to be written.
7113 : : !! etype=Elementary data type (handle) defining the elementary unit used to access the file.
7114 : : !! slk_type=New MPI type that can be used to instantiate the MPI-IO view for the Fortran file.
7115 : : !! Note that the view assumes that the file pointer points to the FIRST Fortran record marker.
7116 : : !! offset_err=Error code. A non-zero value signals that the global matrix is too large
7117 : : !! for a single MPI-IO access (see notes below).
7118 : : !!
7119 : : !! SIDE EFFECTS
7120 : : !! elw2slk(:,:) =
7121 : : !! input: pointer to null().
7122 : : !! output: elw2slk(2,nelw) contains the local coordinates of the matrix elements to be written.
7123 : : !! (useful only if the upper or lower triangle of the global matrix has to be written or when
7124 : : !! uplo="all" but a global subarray is written.
7125 : : !!
7126 : : !! NOTES
7127 : : !! With (signed) Fortran integers, the maximum size of the file that
7128 : : !! that can be read in one-shot is around 2Gb when etype is set to byte.
7129 : : !! Using a larger etype might create portability problems (real data on machines using
7130 : : !! integer*16 for the marker) since etype must be a multiple of the Fortran record marker
7131 : : !! Due to the above reason, block_displ is given in bytes and must be stored in a Fortran
7132 : : !! integer of kind XMPI_ADDRESS_KIND. If the displacement is too large, the routine returns
7133 : : !! offset_err=1 so that the caller will know that several MPI-IO reads are needed to
7134 : : !! write the local buffer.
7135 : : !!
7136 : : !! SOURCE
7137 : :
7138 : 0 : subroutine slk_single_fview_write(Slk_mat,uplo,nelw,elw2slk,etype,slk_type,offset_err,is_fortran_file,glob_subarray)
7139 : :
7140 : : !Arguments ------------------------------------
7141 : : !scalars
7142 : : class(slkmat_dp_t),intent(in) :: Slk_mat
7143 : : integer,intent(out) :: offset_err,slk_type,etype,nelw
7144 : : character(len=*),intent(in) :: uplo
7145 : : logical,optional,intent(in) :: is_fortran_file
7146 : : !arrays
7147 : : integer,pointer :: elw2slk(:,:)
7148 : : integer,optional,intent(in) :: glob_subarray(2,2)
7149 : :
7150 : : !Local variables ------------------------------
7151 : : !scalars
7152 : : integer :: jloc,iloc,iglob,jglob,nrows_glob,ncols_glob,mpi_err,nel_max
7153 : : integer :: grow_min,grow_max,gcol_min,gcol_max
7154 : : integer :: bsize_frm,mpi_type_elm,ij_loc,bsize_elm
7155 : : integer(XMPI_OFFSET_KIND) :: ijp_glob,my_offset,cpad_frm
7156 : : !arrays
7157 : : character(len=500) :: msg
7158 : 0 : integer,allocatable :: block_length(:),block_type(:)
7159 : 0 : integer(XMPI_ADDRESS_KIND),allocatable :: block_displ(:)
7160 : : !************************************************************************
7161 : :
7162 : : #ifdef HAVE_MPI_IO
7163 : : !@slkmat_dp_t
7164 : 0 : bsize_frm = xmpio_bsize_frm ! Byte size of the Fortran record marker.
7165 [ # # ]: 0 : if (PRESENT(is_fortran_file)) then
7166 [ # # ]: 0 : if (.not.is_fortran_file) bsize_frm = 0
7167 : : end if
7168 : :
7169 [ # # # # : 0 : if (PRESENT(glob_subarray).and..not.firstchar(uplo, ["A"])) then
# # ]
7170 : 0 : ABI_ERROR("glob_subarray should not be used when uplo/=All")
7171 : : end if
7172 : :
7173 : 0 : call slk_mat%bsize_and_type(bsize_elm, mpi_type_elm)
7174 : :
7175 : : ! Global dimensions.
7176 : 0 : nrows_glob=Slk_mat%size_global(1)
7177 : 0 : ncols_glob=Slk_mat%size_global(2)
7178 : :
7179 : : ! Number of matrix elements treated by this node.
7180 [ # # ]: 0 : nel_max = PRODUCT(Slk_mat%size_local(1:2))
7181 : :
7182 [ # # # # : 0 : ABI_MALLOC(elw2slk,(2,nel_max))
# # # # #
# # # ]
7183 [ # # # # ]: 0 : elw2slk=0
7184 : :
7185 : : ! Cannot use MPI_type_CREATE_INDEXED_BLOCK since it is not correctly implemented in several MPI libraries.
7186 : : ! etype has to be set to MPI_BYTE, since the displacement in MPI structures is always in byte.
7187 : 0 : etype = MPI_BYTE
7188 : :
7189 : : ! Define the mapping between scaLAPACK buffer and the storage on file.
7190 [ # # # # : 0 : ABI_MALLOC(block_length, (nel_max+2))
# # # # #
# # # #
# ]
7191 [ # # # # : 0 : ABI_MALLOC(block_displ, (nel_max+2))
# # # # #
# # # ]
7192 [ # # # # : 0 : ABI_MALLOC(block_type, (nel_max+2))
# # # # ]
7193 : 0 : block_length(1)=1
7194 : 0 : block_displ (1)=0
7195 : 0 : block_type (1)=MPI_LB
7196 : :
7197 : : ! Note that the view assumes that the file pointer points to the first Fortran record marker.
7198 : 0 : offset_err=0
7199 : :
7200 : 0 : select case (uplo(1:1))
7201 : : case ("A","a")
7202 : : ! The entire global matrix is written on disk. TODO can use contiguous vectors for better access.
7203 : 0 : grow_min=1; grow_max=nrows_glob
7204 : 0 : gcol_min=1; gcol_max=ncols_glob
7205 [ # # ]: 0 : if (PRESENT(glob_subarray)) then ! subarray access.
7206 : 0 : grow_min = glob_subarray(1,1)
7207 : 0 : gcol_min = glob_subarray(2,1)
7208 : 0 : grow_max = grow_min + glob_subarray(1,2) -1
7209 : 0 : gcol_max = gcol_min + glob_subarray(2,2) -1
7210 : : end if
7211 : :
7212 : 0 : ij_loc=0
7213 [ # # ]: 0 : do jloc=1,Slk_mat%size_local(2)
7214 [ # # ]: 0 : do iloc=1,Slk_mat%size_local(1)
7215 : 0 : call slk_mat%loc2glob(iloc, jloc, iglob, jglob)
7216 : : if (iglob>=grow_min.and.iglob<=grow_max .and. & ! glob_subarray element.
7217 [ # # # # : 0 : jglob>=gcol_min.and.jglob<=gcol_max) then
# # # # ]
7218 : 0 : ij_loc = ij_loc+1
7219 : 0 : my_offset = 2*(jglob-1)*bsize_frm + bsize_frm + (jglob-1)*nrows_glob*bsize_elm + (iglob-1) * bsize_elm
7220 [ # # ]: 0 : if (xmpio_max_address(my_offset)) offset_err=1 ! Test for possible wraparounds
7221 : 0 : block_displ (ij_loc+1) = my_offset
7222 : 0 : block_type (ij_loc+1) = mpi_type_elm
7223 : 0 : block_length(ij_loc+1) = 1
7224 [ # # ]: 0 : elw2slk(:,ij_loc) = (/iloc,jloc/) ! useless when subarray are not used but oh well!
7225 : : end if
7226 : : end do
7227 : : end do
7228 : :
7229 : : case ("U","u")
7230 : : ! Only the upper triangle of the global matrix is stored on disk.
7231 : 0 : ij_loc=0
7232 [ # # ]: 0 : do jloc=1,Slk_mat%size_local(2)
7233 [ # # ]: 0 : do iloc=1,Slk_mat%size_local(1)
7234 : 0 : call slk_mat%loc2glob(iloc, jloc, iglob, jglob)
7235 [ # # ]: 0 : if (jglob>=iglob) then
7236 : 0 : ijp_glob = iglob + jglob*(jglob-1)/2 ! Index for packed form
7237 : 0 : cpad_frm = 2*(jglob-1)*bsize_frm
7238 : 0 : ij_loc = ij_loc+1
7239 : 0 : my_offset = cpad_frm + bsize_frm + (ijp_glob-1) * bsize_elm
7240 [ # # ]: 0 : if (xmpio_max_address(my_offset)) offset_err=1 ! Test for possible wraparounds
7241 : 0 : block_displ (ij_loc+1) = my_offset
7242 : 0 : block_type (ij_loc+1) = mpi_type_elm
7243 : 0 : block_length(ij_loc+1) = 1
7244 [ # # ]: 0 : elw2slk(:,ij_loc) = (/iloc,jloc/)
7245 : : end if
7246 : : end do
7247 : : end do
7248 : :
7249 : : case ("L","l")
7250 : : ! Only the lower triangle of the global matrix is stored on disk.
7251 : 0 : ij_loc=0
7252 [ # # ]: 0 : do jloc=1,Slk_mat%size_local(2)
7253 [ # # ]: 0 : do iloc=1,Slk_mat%size_local(1)
7254 : 0 : call slk_mat%loc2glob(iloc, jloc, iglob, jglob)
7255 [ # # ]: 0 : if (jglob<=iglob) then
7256 : 0 : ijp_glob = iglob + (jglob-1)*(2*nrows_glob-jglob)/2 ! Index for packed form
7257 : 0 : cpad_frm = 2*(jglob-1)*bsize_frm
7258 : 0 : ij_loc = ij_loc+1
7259 : 0 : my_offset = cpad_frm + bsize_frm + (ijp_glob-1) * bsize_elm
7260 [ # # ]: 0 : if (xmpio_max_address(my_offset)) offset_err=1 ! block_displ is usually integer*4. Test for possible wraparounds
7261 : 0 : block_displ (ij_loc+1) = my_offset
7262 : 0 : block_type (ij_loc+1) = mpi_type_elm
7263 : 0 : block_length(ij_loc+1) = 1
7264 [ # # ]: 0 : elw2slk(:,ij_loc) = (/iloc,jloc/)
7265 : : end if
7266 : : end do
7267 : : end do
7268 : :
7269 : : case default
7270 [ # # # # : 0 : ABI_BUG(" Wrong uplo: "//TRIM(uplo))
# # ]
7271 : : end select
7272 : :
7273 [ # # ]: 0 : if (offset_err/=0) then ! just warn, let the caller handle the exception.
7274 : : write(msg,"(3a)")&
7275 : 0 : "Global position index cannot be stored in standard Fortran integer ",ch10,&
7276 : 0 : "scaLAPACK matrix cannot be read with a single MPI-IO call ."
7277 : 0 : ABI_WARNING(msg)
7278 : : end if
7279 : :
7280 : : ! Final number of matrix elements that will be written by this node.
7281 : 0 : nelw = ij_loc
7282 : :
7283 : 0 : block_length(nelw+2)= 1
7284 : 0 : block_displ (nelw+2)= ncols_glob * (nrows_glob*bsize_elm + 2*bsize_frm)
7285 : 0 : block_type (nelw+2)= MPI_UB
7286 : :
7287 : 0 : call xmpio_type_struct(nelw+2,block_length,block_displ,block_type,slk_type,mpi_err)
7288 : 0 : ABI_CHECK_MPI(mpi_err, "MPI_type_STRUCT")
7289 : :
7290 : 0 : ABI_FREE(block_length)
7291 : 0 : ABI_FREE(block_displ)
7292 : 0 : ABI_FREE(block_type)
7293 : :
7294 : 0 : call MPI_type_COMMIT(slk_type,mpi_err)
7295 : 0 : ABI_CHECK_MPI(mpi_err, "MPI_type_COMMIT")
7296 : :
7297 : : #else
7298 : : ABI_ERROR("MPI-IO is mandatatory in slk_single_fview_read")
7299 : : #endif
7300 : :
7301 : 0 : end subroutine slk_single_fview_write
7302 : : !!***
7303 : :
7304 : : !----------------------------------------------------------------------
7305 : :
7306 : : !!****f* m_slk/slkmat_dp_bsize_and_type
7307 : : !! NAME
7308 : : !! slkmat_dp_bsize_and_type
7309 : : !!
7310 : : !! FUNCTION
7311 : : !! Returns the byte size and the MPI datatype associated to the matrix elements
7312 : : !! that are stored in the ScaLAPACK_matrix
7313 : : !!
7314 : : !! INPUTS
7315 : : !! Slk_mat<slkmat_dp_t>=Structured datatype defining the scaLAPACK distribution with the local buffer
7316 : : !!
7317 : : !! OUTPUT
7318 : : !! bsize_elm=Byte size of the matrix element.
7319 : : !! mpi_type_elm=MPI datatype of the matrix element.
7320 : : !!
7321 : : !! SOURCE
7322 : :
7323 : 0 : subroutine slkmat_dp_bsize_and_type(Slk_mat, bsize_elm, mpi_type_elm)
7324 : :
7325 : : !Arguments ------------------------------------
7326 : : !scalars
7327 : : class(slkmat_dp_t),intent(in) :: Slk_mat
7328 : : integer,intent(out) :: bsize_elm,mpi_type_elm
7329 : :
7330 : : !Local variables ------------------------------
7331 : : !scalars
7332 : : integer :: ierr
7333 : : character(len=500) :: msg
7334 : : ! ************************************************************************
7335 : :
7336 : 0 : ierr=0
7337 : : #ifdef HAVE_MPI
7338 [ # # ]: 0 : if (allocated(Slk_mat%buffer_cplx)) then
7339 : 0 : ierr = ierr + 1
7340 : 0 : mpi_type_elm = MPI_DOUBLE_COMPLEX
7341 : 0 : bsize_elm = xmpi_bsize_dpc
7342 : : end if
7343 : :
7344 [ # # ]: 0 : if (allocated(Slk_mat%buffer_real)) then
7345 : 0 : ierr = ierr + 1
7346 : 0 : mpi_type_elm = MPI_DOUBLE_PRECISION
7347 : 0 : bsize_elm = xmpi_bsize_dp
7348 : : end if
7349 : : #endif
7350 : :
7351 : : ! One and only one buffer should be allocated.
7352 [ # # ]: 0 : if (ierr /= 1) then
7353 : 0 : write(msg,'(a,i0)')" ScaLAPACK buffers are not allocated correctly, ierr= ",ierr
7354 : 0 : ABI_ERROR(msg)
7355 : : end if
7356 : :
7357 : 0 : end subroutine slkmat_dp_bsize_and_type
7358 : : !!***
7359 : :
7360 : : !----------------------------------------------------------------------
7361 : :
7362 : : !!****f* m_slk/slkmat_sp_svd
7363 : : !! NAME
7364 : : !! slkmat_sp_svd
7365 : : !!
7366 : : !! FUNCTION
7367 : : !! Compute Singular Value Decomposition of matrix in_mat (single precision version).
7368 : : !!
7369 : : !! SIDE EFFECTS
7370 : : !! in_mat: matrix is destroyed in output.
7371 : : !!
7372 : : !! SOURCE
7373 : :
7374 [ # # # # ]: 0 : subroutine slkmat_sp_svd(in_mat, jobu, jobvt, u_mat, s_vals, vt_mat)
7375 : :
7376 : : !Arguments ------------------------------------
7377 : : class(slkmat_sp_t),intent(inout) :: in_mat
7378 : : character(len=1),intent(in) :: jobu, jobvt
7379 : : class(slkmat_sp_t),intent(out) :: u_mat, vt_mat
7380 : : real(sp),allocatable, intent(out) :: s_vals(:)
7381 : :
7382 : : #ifdef HAVE_LINALG_SCALAPACK
7383 : : !Local variables ------------------------------
7384 : : !scalars
7385 : : integer :: info, lwork, lrwork, isize, mm, nn
7386 : : !array
7387 : : real(sp),allocatable :: rwork_sp(:)
7388 : : complex(sp),allocatable :: cwork_sp(:)
7389 : : !************************************************************************
7390 : :
7391 : : if (in_mat%desc(MB_) /= in_mat%desc(NB_)) then
7392 : : ABI_ERROR("PCGESVD requires square block decomposition i.e MB_A = NB_A.")
7393 : : end if
7394 : :
7395 : : mm = in_mat%size_global(1); nn = in_mat%size_global(2); isize = min(mm, nn)
7396 : :
7397 : : ! Allocate singular values and output matrices.
7398 : :
7399 : : ABI_CALLOC(s_vals, (isize))
7400 : : if (jobu == "V") call u_mat%init(mm, isize, in_mat%processor, in_mat%istwf_k)
7401 : : if (jobvt == "V") call vt_mat%init(isize, nn, in_mat%processor, in_mat%istwf_k)
7402 : :
7403 : : if (allocated(in_mat%buffer_cplx)) then
7404 : : ! Query optimal workspace size
7405 : : lwork = -1
7406 : : ABI_MALLOC(cwork_sp, (1))
7407 : : ABI_MALLOC(rwork_sp, (1))
7408 : :
7409 : : call PCGESVD(jobu, jobvt, &
7410 : : mm, nn, in_mat%buffer_cplx, 1, 1, in_mat%desc, s_vals, &
7411 : : u_mat%buffer_cplx, 1, 1, u_mat%desc, &
7412 : : vt_mat%buffer_cplx, 1, 1, vt_mat%desc, &
7413 : : cwork_sp, lwork, rwork_sp, info)
7414 : :
7415 : : ABI_CHECK(info == 0, sjoin("CZGESVD returned info:", itoa(info)))
7416 : :
7417 : : ! Allocate optimal workspace
7418 : : lwork = nint(real(cwork_sp(1)))
7419 : : lrwork = nint(rwork_sp(1))
7420 : : ABI_FREE(cwork_sp)
7421 : : ABI_FREE(rwork_sp)
7422 : :
7423 : : ABI_MALLOC(cwork_sp, (lwork))
7424 : : ABI_MALLOC(rwork_sp, (lrwork))
7425 : :
7426 : : ! Perform SVD
7427 : : call PCGESVD(jobu, jobvt, &
7428 : : mm, nn, in_mat%buffer_cplx, 1, 1, in_mat%desc, s_vals, &
7429 : : u_mat%buffer_cplx, 1, 1, u_mat%desc, &
7430 : : vt_mat%buffer_cplx, 1, 1, vt_mat%desc, &
7431 : : cwork_sp, lwork, rwork_sp, info)
7432 : :
7433 : : ABI_FREE(cwork_sp)
7434 : : ABI_FREE(rwork_sp)
7435 : : ABI_CHECK(info == 0, sjoin("PCGESVD returned info:", itoa(info)))
7436 : :
7437 : : else if (allocated(in_mat%buffer_real)) then
7438 : : ABI_ERROR("SVD for real matrices not coded!")
7439 : : else
7440 : : ABI_ERROR("Neither complex nor real buffer are allocated!")
7441 : : end if
7442 : : #endif
7443 : :
7444 : 0 : end subroutine slkmat_sp_svd
7445 : : !!***
7446 : :
7447 : : !----------------------------------------------------------------------
7448 : :
7449 : : !!****f* m_slk/slkmat_dp_svd
7450 : : !! NAME
7451 : : !! slkmat_dp_svd
7452 : : !!
7453 : : !! FUNCTION
7454 : : !! Compute Singular Value Decomposition of matrix in_mat (double precision version).
7455 : : !!
7456 : : !! SIDE EFFECTS
7457 : : !! in_mat: matrix is destroyed in output.
7458 : : !!
7459 : : !! SOURCE
7460 : :
7461 [ # # # # ]: 0 : subroutine slkmat_dp_svd(in_mat, jobu, jobvt, u_mat, s_vals, vt_mat)
7462 : :
7463 : : !Arguments ------------------------------------
7464 : : class(slkmat_dp_t),intent(inout) :: in_mat
7465 : : character(len=1),intent(in) :: jobu, jobvt
7466 : : class(slkmat_dp_t),intent(out) :: u_mat, vt_mat
7467 : : real(dp),allocatable, intent(out) :: s_vals(:)
7468 : :
7469 : : #ifdef HAVE_LINALG_SCALAPACK
7470 : : !Local variables ------------------------------
7471 : : !scalars
7472 : : integer :: info, lwork, lrwork, isize, mm, nn
7473 : : !array
7474 : : real(dp),allocatable :: rwork_dp(:)
7475 : : complex(dp),allocatable :: cwork_dp(:)
7476 : : !************************************************************************
7477 : :
7478 : : if (in_mat%desc(MB_) /= in_mat%desc(NB_)) then
7479 : : ABI_ERROR("PZGESVD requires square block decomposition i.e MB_A = NB_A.")
7480 : : end if
7481 : :
7482 : : mm = in_mat%size_global(1); nn = in_mat%size_global(2); isize = min(mm, nn)
7483 : :
7484 : : ! Allocate singular values and output matrices.
7485 : : ABI_CALLOC(s_vals, (isize))
7486 : :
7487 : : if (jobu == "V") call u_mat%init(mm, isize, in_mat%processor, in_mat%istwf_k)
7488 : : if (jobvt == "V") call vt_mat%init(isize, nn, in_mat%processor, in_mat%istwf_k)
7489 : :
7490 : : if (allocated(in_mat%buffer_cplx)) then
7491 : : ! Query optimal workspace size
7492 : : lwork = -1
7493 : : ABI_MALLOC(cwork_dp, (1))
7494 : : ABI_MALLOC(rwork_dp, (1))
7495 : :
7496 : : call PZGESVD(jobu, jobvt, &
7497 : : mm, nn, in_mat%buffer_cplx, 1, 1, in_mat%desc, s_vals, &
7498 : : u_mat%buffer_cplx, 1, 1, u_mat%desc, &
7499 : : vt_mat%buffer_cplx, 1, 1, vt_mat%desc, &
7500 : : cwork_dp, lwork, rwork_dp, info)
7501 : :
7502 : : ABI_CHECK(info == 0, sjoin("CZGESVD returned info:", itoa(info)))
7503 : :
7504 : : ! Allocate optimal workspace
7505 : : lwork = nint(real(cwork_dp(1)))
7506 : : lrwork = nint(rwork_dp(1))
7507 : : ABI_FREE(cwork_dp)
7508 : : ABI_FREE(rwork_dp)
7509 : :
7510 : : ABI_MALLOC(cwork_dp, (lwork))
7511 : : ABI_MALLOC(rwork_dp, (lrwork))
7512 : :
7513 : : ! Perform SVD
7514 : : call PZGESVD(jobu, jobvt, &
7515 : : mm, nn, in_mat%buffer_cplx, 1, 1, in_mat%desc, s_vals, &
7516 : : u_mat%buffer_cplx, 1, 1, u_mat%desc, &
7517 : : vt_mat%buffer_cplx, 1, 1, vt_mat%desc, &
7518 : : cwork_dp, lwork, rwork_dp, info)
7519 : :
7520 : : ABI_FREE(cwork_dp)
7521 : : ABI_FREE(rwork_dp)
7522 : : ABI_CHECK(info == 0, sjoin("CZGESVD returned info:", itoa(info)))
7523 : :
7524 : : else if (allocated(in_mat%buffer_real)) then
7525 : : ABI_ERROR("SVD for real matrices not coded!")
7526 : : else
7527 : : ABI_ERROR("Neither complex nor real buffer are allocated!")
7528 : : end if
7529 : : #endif
7530 : :
7531 : 0 : end subroutine slkmat_dp_svd
7532 : : !!***
7533 : :
7534 [ # # # # : 0 : end module m_slk
# # # # #
# # # # #
# # # # #
# # # #
# ]
7535 : : !!***
|