ddx 0.6.0
Libary for domain-decomposition methods for polarizable continuum models
ddx_solvers.f90
Go to the documentation of this file.
1
11
14! Get the core-routines
15use ddx_core
16
17! Disable implicit types
18implicit none
19
20! Interfaces
21interface
22 ! Interface for the matrix-vector product function
23 subroutine matvec_interface(params, constants, workspace, x, y, ddx_error)
24 !! Add definitions for derived types
25 use ddx_core
26 !! Inputs
27 type(ddx_params_type), intent(in) :: params
28 type(ddx_constants_type), intent(in) :: constants
29 real(dp), intent(in) :: x(constants % nbasis, params % nsph)
30 !! Temporaries
31 type(ddx_workspace_type), intent(inout) :: workspace
32 ! Output
33 real(dp), intent(out) :: y(constants % nbasis, params % nsph)
34 type(ddx_error_type), intent(inout) :: ddx_error
35 end subroutine matvec_interface
36
37 ! Interface for the matrix-vector product function for external jacobi_diis.
38 ! note that the dimension of x and y is double for ddlpb.
39 subroutine matvec_interface_external(params, constants, workspace, x, y, &
40 & ddx_error)
41 !! Add definitions for derived types
42 use ddx_core
43 !! Inputs
44 type(ddx_params_type), intent(in) :: params
45 type(ddx_constants_type), intent(in) :: constants
46 real(dp), intent(in) :: x(constants % nbasis, params % nsph, 2)
47 !! Temporaries
48 type(ddx_workspace_type), intent(inout) :: workspace
49 ! Output
50 real(dp), intent(out) :: y(constants % nbasis, params % nsph, 2)
51 type(ddx_error_type), intent(inout) :: ddx_error
52 end subroutine matvec_interface_external
53
54 ! Interface for the norm calculating function
55 real(dp) function norm_interface(lmax, nbasis, nsph, x)
56 !! Add definition for real(dp)
58 !! Inputs
59 integer, intent(in) :: lmax, nbasis, nsph
60 real(dp), intent(in) :: x(nbasis, nsph)
61 end function norm_interface
62end interface
63
64contains
65
84subroutine jacobi_diis(params, constants, workspace, tol, rhs, x, niter, &
85 & x_rel_diff, matvec, dm1vec, norm_func, ddx_error)
86 !! Inputs
87 type(ddx_params_type), intent(in) :: params
88 type(ddx_constants_type), intent(in) :: constants
89 real(dp), intent(in) :: tol, rhs(constants % n)
90 !! Temporaries
91 type(ddx_workspace_type), intent(inout) :: workspace
92 !! Outputs
93 real(dp), intent(inout) :: x(constants % n)
94 integer, intent(inout) :: niter
95 real(dp), intent(out) :: x_rel_diff(niter)
96 type(ddx_error_type), intent(inout) :: ddx_error
97 !! External procedures
98 procedure(matvec_interface) :: matvec, dm1vec
99 procedure(norm_interface) :: norm_func
100 !! Local variables
101 integer :: it, nmat
102 real(dp) :: diff, norm, rel_diff
103 !! The code
104 ! DIIS variable
105 nmat = 1
106 ! Iterations
107 do it = 1, niter
108 ! y = rhs - O x
109 call matvec(params, constants, workspace, x, workspace % tmp_y, ddx_error)
110 workspace % tmp_y = rhs - workspace % tmp_y
111 if (ddx_error % flag .ne. 0) then
112 call update_error(ddx_error, 'Matvec error, exiting')
113 return
114 end if
115 ! x_new = D^-1 y
116 call dm1vec(params, constants, workspace, workspace % tmp_y, &
117 & workspace % tmp_x_new, ddx_error)
118 if (ddx_error % flag .ne. 0) then
119 call update_error(ddx_error, 'Preconditioning error, exiting')
120 return
121 end if
122 ! DIIS extrapolation
123 if (params % jacobi_ndiis .gt. 2) then
124 workspace % tmp_x_diis(:, nmat) = workspace % tmp_x_new
125 workspace % tmp_e_diis(:, nmat) = workspace % tmp_x_new - x
126 call diis(constants % n, nmat, params % jacobi_ndiis, &
127 & workspace % tmp_x_diis, workspace % tmp_e_diis, &
128 & workspace % tmp_bmat, workspace % tmp_x_new)
129 end if
130 ! Norm of increment
131 x = workspace % tmp_x_new - x
132 diff = norm_func(params % lmax, constants % nbasis, params % nsph, x)
133 norm = norm_func(params % lmax, constants % nbasis, params % nsph, &
134 & workspace % tmp_x_new)
135
136 ! compute rel_diff using the rule 0/0 = 0
137 if (diff.eq.zero .and. norm.eq.zero) then
138 rel_diff = zero
139 else
140 rel_diff = diff / norm
141 end if
142
143 if (params % verbose) then
144 write(params % iunit, '(A,I3,1X,A,E10.2)') &
145 & 'Iteration:', it, 'Rel. diff:', rel_diff
146 flush(params % iunit)
147 end if
148
149 x_rel_diff(it) = rel_diff
150 ! Update solution
151 x = workspace % tmp_x_new
152 ! Check stop condition
153 if (rel_diff .le. tol) then
154 niter = it
155 return
156 end if
157 end do
158 call update_error(ddx_error, "Jacobi solver did not converge")
159endsubroutine jacobi_diis
160
162subroutine diis(n, nmat, ndiis, x, e, b, xnew)
163 implicit none
164 integer, intent(in) :: n, ndiis
165 integer, intent(inout) :: nmat
166 real(dp), dimension(n,ndiis), intent(inout) :: x, e
167 real(dp), dimension(ndiis+1,ndiis+1), intent(inout) :: b
168 real(dp), dimension(n), intent(inout) :: xnew
169 integer :: nmat1, i, istatus, info
170 integer :: j, k
171 real(dp), allocatable :: bloc(:,:), cex(:)
172 integer, allocatable :: ipiv(:)
173 integer :: delta
174
175 delta = min(10, ndiis-1)
176
177 if (nmat.ge.ndiis) then
178 do j = 2, nmat - delta
179 do k = 2, nmat - delta
180 b(j, k) = b(j+delta, k+delta)
181 end do
182 end do
183 do j = 1, nmat - delta
184 x(:, j) = x(:, j+delta)
185 e(:, j) = e(:, j+delta)
186 end do
187 nmat = nmat - delta
188 end if
189 nmat1 = nmat + 1
190
191 allocate(bloc(nmat1, nmat1), cex(nmat1), ipiv(nmat1), stat=istatus)
192
193 call makeb(n, nmat, ndiis, e, b)
194 bloc = b(1:nmat1, 1:nmat1)
195 cex = zero
196 cex(1) = one
197 call dgesv(nmat1, 1, bloc, nmat1, ipiv, cex, nmat1, info)
198! call gjinv(nmat1, 1, bloc, cex, ok)
199 if (info.ne.0) then
200 nmat = 1
201 return
202 end if
203
204 xnew = zero
205 do i = 1, nmat
206 call daxpy(n,cex(i+1),x(:, i),1,xnew,1)
207 end do
208 nmat = nmat + 1
209
210 deallocate (bloc, cex, stat=istatus)
211end subroutine diis
212
214subroutine makeb(n, nmat, ndiis, e, b)
215 implicit none
216 integer, intent(in) :: n, nmat, ndiis
217 real(dp), dimension(n,ndiis), intent(in) :: e
218 real(dp), dimension(ndiis+1,ndiis+1), intent(inout) :: b
219
220 integer :: i
221 real(dp) :: bij
222
223 ! 1st built
224 if (nmat.eq.1) then
225
226 ! [ 0 | 1 ]
227 ! b = [ --+---- ]
228 ! [ 1 | e*e ]
229
230 b(1,1) = zero
231 b(1,2) = one
232 b(2,1) = one
233 b(2,2) = dot_product(e(:,1),e(:,1))
234
235 ! subsequent builts
236 else
237
238 ! first, update the lagrangian line:
239 b(nmat+1,1) = one
240 b(1,nmat+1) = one
241
242 ! now, compute the new matrix elements:
243 !$omp parallel do default(none) shared(nmat,e,b) &
244 !$omp private(i,bij) schedule(dynamic)
245 do i = 1, nmat - 1
246 bij = dot_product(e(:,i), e(:,nmat))
247 b(nmat+1, i+1) = bij
248 b(i+1, nmat+1) = bij
249 end do
250 b(nmat+1,nmat+1) = dot_product(e(:,nmat),e(:,nmat))
251 end if
252end subroutine makeb
253
263subroutine jacobi_diis_external(params, constants, workspace, n, tol, rhs, x, n_iter, &
264 & x_rel_diff, matvec, dm1vec, norm_func, ddx_error)
265 type(ddx_params_type), intent(in) :: params
266 type(ddx_constants_type), intent(inout) :: constants
267 type(ddx_workspace_type), intent(inout) :: workspace
268 ! Inputs
269 integer, intent(in) :: n
270 real(dp), intent(in) :: tol
271 real(dp), dimension(n), intent(in) :: rhs
272 ! Outputs
273 real(dp), dimension(n), intent(inout) :: x
274 integer, intent(inout) :: n_iter
275 type(ddx_error_type), intent(inout) :: ddx_error
276 real(dp), intent(out) :: x_rel_diff(n_iter)
277 external :: matvec, dm1vec
278 procedure(norm_interface) :: norm_func
279 ! Local variables
280 integer :: it, nmat, istatus, lenb, nsph_u
281 real(dp) :: diff, norm, rel_diff = zero
282 logical :: dodiis
283 real(dp), allocatable :: x_new(:), y(:), x_diis(:,:), e_diis(:,:), bmat(:,:)
284 ! DIIS extrapolation flag
285 dodiis = (params%jacobi_ndiis.gt.0)
286 ! extrapolation required
287 if (dodiis) then
288 ! allocate workspaces
289 lenb = params % jacobi_ndiis + 1
290 allocate( x_diis(n,params % jacobi_ndiis), e_diis(n,params % jacobi_ndiis), bmat(lenb,lenb) , stat=istatus )
291 if (istatus .ne. 0) then
292 call update_error(ddx_error, 'jacobi_diis_external: failed allocation (diis)')
293 return
294 endif
295 ! initialize the number of points for diis to one.
296 ! note that nmat is updated by diis.
297 nmat = 1
298 endif
299 ! allocate workspaces
300 allocate( x_new(n), y(n) , stat=istatus )
301 if (istatus .ne. 0) then
302 call update_error(ddx_error, 'jacobi_diis_external: failed allocation (diis)')
303 return
304 endif
305 ! Jacobi iterations
306 do it = 1, n_iter
307 ! y = rhs - O x
308 call matvec(params, constants, workspace, x, y, ddx_error)
309 y = rhs - y
310 ! x_new = D^-1 y
311 call dm1vec(params, constants, workspace, y, x_new, ddx_error)
312 ! DIIS extrapolation
313 if (dodiis) then
314 x_diis(:,nmat) = x_new
315 e_diis(:,nmat) = x_new - x
316 call diis(n,nmat,params % jacobi_ndiis,x_diis,e_diis,bmat,x_new)
317 endif
318 !increment
319 x = x_new - x
320 ! warning: in the following, we use a quick and dirty hack to pass
321 ! to the norm function arrays double the size than in non ddlpb calculations
322 ! by defining a fake number of spheres with is n/nbasis (i.e., 2*nsph in
323 ! ddlpb).
324 nsph_u = n/constants % nbasis
325 diff = norm_func(params % lmax, constants % nbasis, nsph_u, x)
326 norm = norm_func(params % lmax, constants % nbasis, nsph_u, x_new)
327 ! compute rel_diff using the rule 0/0 = 0
328 if (diff.eq.zero .and. norm.eq.zero) then
329 rel_diff = zero
330 else
331 rel_diff = diff / norm
332 end if
333 x_rel_diff(it) = rel_diff
334 constants % inner_tol = max(rel_diff*sqrt(tol), tol/100.0d0)
335 ! update
336 x = x_new
337 ! EXIT Jacobi loop here
338 if (rel_diff .le. tol) then
339 n_iter = it
340 return
341 end if
342 enddo
343 call update_error(ddx_error, 'Jacobi external solver failed to converge')
344endsubroutine jacobi_diis_external
345
346end module ddx_solvers
Core routines and parameters of the ddX software.
Definition: ddx_core.f90:13
Compile-time constants and definitions.
Core routines and parameters of ddX software.
Definition: ddx_solvers.f90:13
subroutine jacobi_diis_external(params, constants, workspace, n, tol, rhs, x, n_iter, x_rel_diff, matvec, dm1vec, norm_func, ddx_error)
jacobi_diis_solver_external WARNING: code duplication is bad, but here it makes a very specific sense...
subroutine diis(n, nmat, ndiis, x, e, b, xnew)
DIIS helper routine.
subroutine makeb(n, nmat, ndiis, e, b)
DIIS helper routine.
subroutine jacobi_diis(params, constants, workspace, tol, rhs, x, niter, x_rel_diff, matvec, dm1vec, norm_func, ddx_error)
Jacobi iterative solver.
Definition: ddx_solvers.f90:86