(git:ccc2433)
preconditioner_solvers.F
Go to the documentation of this file.
1 !--------------------------------------------------------------------------------------------------!
2 ! CP2K: A general program to perform molecular dynamics simulations !
3 ! Copyright 2000-2024 CP2K developers group <https://cp2k.org> !
4 ! !
5 ! SPDX-License-Identifier: GPL-2.0-or-later !
6 !--------------------------------------------------------------------------------------------------!
7 
8 ! **************************************************************************************************
9 !> \brief solves the preconditioner, contains to utility function for
10 !> fm<->dbcsr transfers, should be moved soon
11 !> \par History
12 !> - [UB] 2009-05-13 Adding stable approximate inverse (full and sparse)
13 !> \author Joost VandeVondele (09.2002)
14 ! **************************************************************************************************
16  USE arnoldi_api, ONLY: arnoldi_data_type,&
17  arnoldi_ev,&
18  deallocate_arnoldi_data,&
19  get_selected_ritz_val,&
20  setup_arnoldi_data
21  USE bibliography, ONLY: schiffmann2015,&
22  cite_reference
23  USE cp_blacs_env, ONLY: cp_blacs_env_type
31  cp_fm_struct_type
32  USE cp_fm_types, ONLY: cp_fm_create,&
33  cp_fm_release,&
35  cp_fm_type
36  USE dbcsr_api, ONLY: &
37  dbcsr_create, dbcsr_filter, dbcsr_get_info, dbcsr_get_occupation, dbcsr_init_p, &
38  dbcsr_p_type, dbcsr_release, dbcsr_type, dbcsr_type_no_symmetry, dbcsr_type_real_8
44  USE kinds, ONLY: dp
45  USE message_passing, ONLY: mp_para_env_type
46  USE preconditioner_types, ONLY: preconditioner_type
47 #include "./base/base_uses.f90"
48 
49  IMPLICIT NONE
50 
51  PRIVATE
52 
53  CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'preconditioner_solvers'
54 
56 
57 CONTAINS
58 
59 ! **************************************************************************************************
60 !> \brief ...
61 !> \param my_solver_type ...
62 !> \param preconditioner_env ...
63 !> \param matrix_s ...
64 !> \param matrix_h ...
65 ! **************************************************************************************************
66  SUBROUTINE solve_preconditioner(my_solver_type, preconditioner_env, matrix_s, &
67  matrix_h)
68  INTEGER :: my_solver_type
69  TYPE(preconditioner_type) :: preconditioner_env
70  TYPE(dbcsr_type), OPTIONAL, POINTER :: matrix_s
71  TYPE(dbcsr_type), POINTER :: matrix_h
72 
73  REAL(dp) :: occ_matrix
74 
75 ! here comes the solver
76 
77  SELECT CASE (my_solver_type)
79  !
80  ! compute the full inverse
81  preconditioner_env%solver = ot_precond_solver_inv_chol
82  CALL make_full_inverse_cholesky(preconditioner_env, matrix_s)
84  !
85  ! prepare for the direct solver
86  preconditioner_env%solver = ot_precond_solver_direct
87  CALL make_full_fact_cholesky(preconditioner_env, matrix_s)
89  !
90  ! uses an update of the full inverse (needs to be computed the first time)
91  ! make sure preconditioner_env is not destroyed in between
92  occ_matrix = 1.0_dp
93  IF (ASSOCIATED(preconditioner_env%sparse_matrix)) THEN
94  IF (preconditioner_env%condition_num < 0.0_dp) &
95  CALL estimate_cond_num(preconditioner_env%sparse_matrix, preconditioner_env%condition_num)
96  CALL dbcsr_filter(preconditioner_env%sparse_matrix, &
97  1.0_dp/preconditioner_env%condition_num*0.01_dp)
98  occ_matrix = dbcsr_get_occupation(preconditioner_env%sparse_matrix)
99  END IF
100  ! check whether we are in the first step and if it is a good idea to use cholesky (matrix sparsity)
101  IF (preconditioner_env%solver .NE. ot_precond_solver_update .AND. occ_matrix > 0.5_dp) THEN
102  preconditioner_env%solver = ot_precond_solver_update
103  CALL make_full_inverse_cholesky(preconditioner_env, matrix_s)
104  ELSE
105  preconditioner_env%solver = ot_precond_solver_update
106  CALL make_inverse_update(preconditioner_env, matrix_h)
107  END IF
109  preconditioner_env%solver = ot_precond_solver_default
110  CASE DEFAULT
111  !
112  cpabort("Doesn't know this type of solver")
113  END SELECT
114 
115  END SUBROUTINE solve_preconditioner
116 
117 ! **************************************************************************************************
118 !> \brief Compute the inverse using cholseky factorization
119 !> \param preconditioner_env ...
120 !> \param matrix_s ...
121 ! **************************************************************************************************
122  SUBROUTINE make_full_inverse_cholesky(preconditioner_env, matrix_s)
123 
124  TYPE(preconditioner_type) :: preconditioner_env
125  TYPE(dbcsr_type), OPTIONAL, POINTER :: matrix_s
126 
127  CHARACTER(len=*), PARAMETER :: routinen = 'make_full_inverse_cholesky'
128 
129  INTEGER :: handle, info
130  TYPE(cp_fm_type) :: fm_work
131  TYPE(cp_fm_type), POINTER :: fm
132 
133  CALL timeset(routinen, handle)
134 
135  ! Maybe we will get a sparse Cholesky at a given point then this can go,
136  ! if stuff was stored in fm anyway this simple returns
137  CALL transfer_dbcsr_to_fm(preconditioner_env%sparse_matrix, preconditioner_env%fm, &
138  preconditioner_env%para_env, preconditioner_env%ctxt)
139  fm => preconditioner_env%fm
140 
141  CALL cp_fm_create(fm_work, fm%matrix_struct, name="fm_work")
142  !
143  ! compute the inverse of SPD matrix fm using the Cholesky factorization
144  CALL cp_fm_cholesky_decompose(fm, info_out=info)
145 
146  !
147  ! if fm not SPD we go with the overlap matrix
148  IF (info /= 0) THEN
149  !
150  ! just the overlap matrix
151  IF (PRESENT(matrix_s)) THEN
152  CALL copy_dbcsr_to_fm(matrix_s, fm)
153  CALL cp_fm_cholesky_decompose(fm)
154  ELSE
155  CALL cp_fm_set_all(fm, alpha=0._dp, beta=1._dp)
156  END IF
157  END IF
158  CALL cp_fm_cholesky_invert(fm)
159 
160  CALL cp_fm_upper_to_full(fm, fm_work)
161  CALL cp_fm_release(fm_work)
162 
163  CALL timestop(handle)
164 
165  END SUBROUTINE make_full_inverse_cholesky
166 
167 ! **************************************************************************************************
168 !> \brief Only perform the factorization, can be used later to solve the linear
169 !> system on the fly
170 !> \param preconditioner_env ...
171 !> \param matrix_s ...
172 ! **************************************************************************************************
173  SUBROUTINE make_full_fact_cholesky(preconditioner_env, matrix_s)
174 
175  TYPE(preconditioner_type) :: preconditioner_env
176  TYPE(dbcsr_type), OPTIONAL, POINTER :: matrix_s
177 
178  CHARACTER(len=*), PARAMETER :: routinen = 'make_full_fact_cholesky'
179 
180  INTEGER :: handle, info_out
181  TYPE(cp_fm_type), POINTER :: fm
182 
183  CALL timeset(routinen, handle)
184 
185  ! Maybe we will get a sparse Cholesky at a given point then this can go,
186  ! if stuff was stored in fm anyway this simple returns
187  CALL transfer_dbcsr_to_fm(preconditioner_env%sparse_matrix, preconditioner_env%fm, &
188  preconditioner_env%para_env, preconditioner_env%ctxt)
189 
190  fm => preconditioner_env%fm
191  !
192  ! compute the inverse of SPD matrix fm using the Cholesky factorization
193  CALL cp_fm_cholesky_decompose(fm, info_out=info_out)
194  !
195  ! if fm not SPD we go with the overlap matrix
196  IF (info_out .NE. 0) THEN
197  !
198  ! just the overlap matrix
199  IF (PRESENT(matrix_s)) THEN
200  CALL copy_dbcsr_to_fm(matrix_s, fm)
201  CALL cp_fm_cholesky_decompose(fm)
202  ELSE
203  CALL cp_fm_set_all(fm, alpha=0._dp, beta=1._dp)
204  END IF
205  END IF
206 
207  CALL timestop(handle)
208 
209  END SUBROUTINE make_full_fact_cholesky
210 
211 ! **************************************************************************************************
212 !> \brief computes an approximate inverse using Hotelling iterations
213 !> \param preconditioner_env ...
214 !> \param matrix_h as S is not always present this is a safe template for the transfer
215 ! **************************************************************************************************
216  SUBROUTINE make_inverse_update(preconditioner_env, matrix_h)
217  TYPE(preconditioner_type) :: preconditioner_env
218  TYPE(dbcsr_type), POINTER :: matrix_h
219 
220  CHARACTER(len=*), PARAMETER :: routinen = 'make_inverse_update'
221 
222  INTEGER :: handle
223  LOGICAL :: use_guess
224  REAL(kind=dp) :: filter_eps
225 
226  CALL timeset(routinen, handle)
227  use_guess = .true.
228  !
229  ! uses an update of the full inverse (needs to be computed the first time)
230  ! make sure preconditioner_env is not destroyed in between
231 
232  CALL cite_reference(schiffmann2015)
233 
234  ! Maybe I gonna add a fm Hotelling, ... for now the same as above make sure we are dbcsr
235  CALL transfer_fm_to_dbcsr(preconditioner_env%fm, preconditioner_env%sparse_matrix, matrix_h)
236  IF (.NOT. ASSOCIATED(preconditioner_env%dbcsr_matrix)) THEN
237  use_guess = .false.
238  CALL dbcsr_init_p(preconditioner_env%dbcsr_matrix)
239  CALL dbcsr_create(preconditioner_env%dbcsr_matrix, "prec_dbcsr", &
240  template=matrix_h, matrix_type=dbcsr_type_no_symmetry)
241  END IF
242 
243  ! Try to get a reasonbale guess for the filtering threshold
244  filter_eps = 1.0_dp/preconditioner_env%condition_num*0.1_dp
245 
246  ! Aggressive filtering on the initial guess is needed to avoid fill ins and retain sparsity
247  CALL dbcsr_filter(preconditioner_env%dbcsr_matrix, filter_eps*100.0_dp)
248  ! We don't need a high accuracy for the inverse so 0.4 is reasonable for convergence
249  CALL invert_hotelling(preconditioner_env%dbcsr_matrix, preconditioner_env%sparse_matrix, filter_eps*10.0_dp, &
250  use_inv_as_guess=use_guess, norm_convergence=0.4_dp, filter_eps=filter_eps)
251 
252  CALL timestop(handle)
253 
254  END SUBROUTINE make_inverse_update
255 
256 ! **************************************************************************************************
257 !> \brief computes an approximation to the condition number of a matrix using
258 !> arnoldi iterations
259 !> \param matrix ...
260 !> \param cond_num ...
261 ! **************************************************************************************************
262  SUBROUTINE estimate_cond_num(matrix, cond_num)
263  TYPE(dbcsr_type), POINTER :: matrix
264  REAL(kind=dp) :: cond_num
265 
266  CHARACTER(len=*), PARAMETER :: routinen = 'estimate_cond_num'
267 
268  INTEGER :: handle
269  REAL(kind=dp) :: max_ev, min_ev
270  TYPE(arnoldi_data_type) :: my_arnoldi
271  TYPE(dbcsr_p_type), DIMENSION(:), POINTER :: matrices
272 
273  CALL timeset(routinen, handle)
274 
275  ! its better to do 2 calculations as the maximum should quickly converge and the minimum won't need iram
276  ALLOCATE (matrices(1))
277  matrices(1)%matrix => matrix
278  ! compute the minimum ev
279  CALL setup_arnoldi_data(my_arnoldi, matrices, max_iter=20, threshold=5.0e-4_dp, selection_crit=2, &
280  nval_request=1, nrestarts=15, generalized_ev=.false., iram=.false.)
281  CALL arnoldi_ev(matrices, my_arnoldi)
282  max_ev = real(get_selected_ritz_val(my_arnoldi, 1), dp)
283  CALL deallocate_arnoldi_data(my_arnoldi)
284 
285  CALL setup_arnoldi_data(my_arnoldi, matrices, max_iter=20, threshold=5.0e-4_dp, selection_crit=3, &
286  nval_request=1, nrestarts=15, generalized_ev=.false., iram=.false.)
287  CALL arnoldi_ev(matrices, my_arnoldi)
288  min_ev = real(get_selected_ritz_val(my_arnoldi, 1), dp)
289  CALL deallocate_arnoldi_data(my_arnoldi)
290 
291  cond_num = max_ev/min_ev
292  DEALLOCATE (matrices)
293 
294  CALL timestop(handle)
295  END SUBROUTINE estimate_cond_num
296 
297 ! **************************************************************************************************
298 !> \brief transfers a full matrix to a dbcsr
299 !> \param fm_matrix a full matrix gets deallocated in the end
300 !> \param dbcsr_matrix a dbcsr matrix, gets create from a template
301 !> \param template_mat the template which is used for the structure
302 ! **************************************************************************************************
303  SUBROUTINE transfer_fm_to_dbcsr(fm_matrix, dbcsr_matrix, template_mat)
304 
305  TYPE(cp_fm_type), POINTER :: fm_matrix
306  TYPE(dbcsr_type), POINTER :: dbcsr_matrix, template_mat
307 
308  CHARACTER(len=*), PARAMETER :: routinen = 'transfer_fm_to_dbcsr'
309 
310  INTEGER :: handle
311 
312  CALL timeset(routinen, handle)
313  IF (ASSOCIATED(fm_matrix)) THEN
314  IF (.NOT. ASSOCIATED(dbcsr_matrix)) THEN
315  CALL dbcsr_init_p(dbcsr_matrix)
316  CALL dbcsr_create(dbcsr_matrix, template=template_mat, &
317  name="preconditioner_env%dbcsr_matrix", &
318  matrix_type=dbcsr_type_no_symmetry, &
319  nze=0, data_type=dbcsr_type_real_8)
320  END IF
321  CALL copy_fm_to_dbcsr(fm_matrix, dbcsr_matrix)
322  CALL cp_fm_release(fm_matrix)
323  DEALLOCATE (fm_matrix)
324  NULLIFY (fm_matrix)
325  END IF
326 
327  CALL timestop(handle)
328 
329  END SUBROUTINE transfer_fm_to_dbcsr
330 
331 ! **************************************************************************************************
332 !> \brief transfers a dbcsr to a full matrix
333 !> \param dbcsr_matrix a dbcsr matrix, gets deallocated at the end
334 !> \param fm_matrix a full matrix gets created if not yet done
335 !> \param para_env the para_env
336 !> \param context the blacs context
337 ! **************************************************************************************************
338  SUBROUTINE transfer_dbcsr_to_fm(dbcsr_matrix, fm_matrix, para_env, context)
339 
340  TYPE(dbcsr_type), POINTER :: dbcsr_matrix
341  TYPE(cp_fm_type), POINTER :: fm_matrix
342  TYPE(mp_para_env_type), POINTER :: para_env
343  TYPE(cp_blacs_env_type), POINTER :: context
344 
345  CHARACTER(len=*), PARAMETER :: routinen = 'transfer_dbcsr_to_fm'
346 
347  INTEGER :: handle, n
348  TYPE(cp_fm_struct_type), POINTER :: fm_struct_tmp
349 
350  CALL timeset(routinen, handle)
351  IF (ASSOCIATED(dbcsr_matrix)) THEN
352  NULLIFY (fm_struct_tmp)
353 
354  IF (ASSOCIATED(fm_matrix)) THEN
355  CALL cp_fm_release(fm_matrix)
356  DEALLOCATE (fm_matrix)
357  END IF
358 
359  CALL dbcsr_get_info(dbcsr_matrix, nfullrows_total=n)
360  CALL cp_fm_struct_create(fm_struct_tmp, nrow_global=n, ncol_global=n, &
361  context=context, para_env=para_env)
362  ALLOCATE (fm_matrix)
363  CALL cp_fm_create(fm_matrix, fm_struct_tmp)
364  CALL cp_fm_struct_release(fm_struct_tmp)
365 
366  CALL copy_dbcsr_to_fm(dbcsr_matrix, fm_matrix)
367  CALL dbcsr_release(dbcsr_matrix)
368  DEALLOCATE (dbcsr_matrix)
369  END IF
370 
371  CALL timestop(handle)
372 
373  END SUBROUTINE transfer_dbcsr_to_fm
374 
375 END MODULE preconditioner_solvers
arnoldi iteration using dbcsr
Definition: arnoldi_api.F:15
subroutine, public arnoldi_ev(matrix, arnoldi_data)
Driver routine for different arnoldi eigenvalue methods the selection which one is to be taken is mad...
Definition: arnoldi_api.F:69
collects all references to literature in CP2K as new algorithms / method are included from literature...
Definition: bibliography.F:28
integer, save, public schiffmann2015
Definition: bibliography.F:43
methods related to the blacs parallel environment
Definition: cp_blacs_env.F:15
DBCSR operations in CP2K.
subroutine, public copy_dbcsr_to_fm(matrix, fm)
Copy a DBCSR matrix to a BLACS matrix.
subroutine, public copy_fm_to_dbcsr(fm, matrix, keep_sparsity)
Copy a BLACS matrix to a dbcsr matrix.
basic linear algebra operations for full matrices
subroutine, public cp_fm_upper_to_full(matrix, work)
given an upper triangular matrix computes the corresponding full matrix
various cholesky decomposition related routines
subroutine, public cp_fm_cholesky_invert(matrix, n, info_out)
used to replace the cholesky decomposition by the inverse
subroutine, public cp_fm_cholesky_decompose(matrix, n, info_out)
used to replace a symmetric positive def. matrix M with its cholesky decomposition U: M = U^T * U,...
represent the structure of a full matrix
Definition: cp_fm_struct.F:14
subroutine, public cp_fm_struct_create(fmstruct, para_env, context, nrow_global, ncol_global, nrow_block, ncol_block, descriptor, first_p_pos, local_leading_dimension, template_fmstruct, square_blocks, force_block)
allocates and initializes a full matrix structure
Definition: cp_fm_struct.F:125
subroutine, public cp_fm_struct_release(fmstruct)
releases a full matrix structure
Definition: cp_fm_struct.F:320
represent a full matrix distributed on many processors
Definition: cp_fm_types.F:15
subroutine, public cp_fm_set_all(matrix, alpha, beta)
set all elements of a matrix to the same value, and optionally the diagonal to a different one
Definition: cp_fm_types.F:535
subroutine, public cp_fm_create(matrix, matrix_struct, name, use_sp)
creates a new full matrix with the given structure
Definition: cp_fm_types.F:167
collects all constants needed in input so that they can be used without circular dependencies
integer, parameter, public ot_precond_solver_default
integer, parameter, public ot_precond_solver_inv_chol
integer, parameter, public ot_precond_solver_update
integer, parameter, public ot_precond_solver_direct
Routines useful for iterative matrix calculations.
subroutine, public invert_hotelling(matrix_inverse, matrix, threshold, use_inv_as_guess, norm_convergence, filter_eps, accelerator_order, max_iter_lanczos, eps_lanczos, silent)
invert a symmetric positive definite matrix by Hotelling's method explicit symmetrization makes this ...
Defines the basic variable types.
Definition: kinds.F:23
integer, parameter, public dp
Definition: kinds.F:34
Interface to the message passing library MPI.
solves the preconditioner, contains to utility function for fm<->dbcsr transfers, should be moved soo...
subroutine, public transfer_dbcsr_to_fm(dbcsr_matrix, fm_matrix, para_env, context)
transfers a dbcsr to a full matrix
subroutine, public solve_preconditioner(my_solver_type, preconditioner_env, matrix_s, matrix_h)
...
subroutine, public transfer_fm_to_dbcsr(fm_matrix, dbcsr_matrix, template_mat)
transfers a full matrix to a dbcsr
types of preconditioners