ddx 0.6.0
Libary for domain-decomposition methods for polarizable continuum models
ddx_lpb.f90
1
11
13module ddx_lpb
14! Get ddx-operators
16implicit none
17
19
20contains
21
35subroutine lpb_setup(params, constants, workspace, state, phi_cav, &
36 & e_cav, psi, ddx_error)
37 implicit none
38 type(ddx_params_type), intent(in) :: params
39 type(ddx_constants_type), intent(inout) :: constants
40 type(ddx_workspace_type), intent(inout) :: workspace
41 type(ddx_state_type), intent(inout) :: state
42 type(ddx_error_type), intent(inout) :: ddx_error
43 real(dp), intent(in) :: phi_cav(constants % ncav)
44 real(dp), intent(in) :: e_cav(3, constants % ncav)
45 real(dp), intent(in) :: psi(constants % nbasis, params % nsph)
46
47 state % psi = psi
48 state % rhs_adj_lpb(:, :, 1) = psi
49 ! in ddLPB the polarization density X is expanded according to a
50 ! slightly different definition which lacks the 4pi/(2l + 1) factors
51 ! which are present in ddCOSMO and ddPCM. This affect the adjoint
52 ! density as well. To have a consistent adjoint density with ddCOSMO
53 ! and ddPCM, we have to scale the RHS.
54 call convert_ddcosmo(params, constants, -1, state % rhs_adj_lpb(:,:,1))
55 state % rhs_adj_lpb(:, :, 2) = 0.0d0
56
57 !! Setting initial values to zero
58 state % g_lpb = zero
59 state % f_lpb = zero
60 state % phi_grid = zero
61
62 ! Unwrap sparsely stored potential at cavity points phi_cav into phi_grid
63 ! and multiply it by characteristic function at cavity points ui
64 call ddcav_to_grid_work(params % ngrid, params % nsph, &
65 & constants % ncav, constants % icav_ia, &
66 & constants % icav_ja, phi_cav, state % phi_grid)
67 workspace % tmp_cav = phi_cav * constants % ui_cav
68 call ddcav_to_grid_work(params % ngrid, params % nsph, &
69 & constants % ncav, constants % icav_ia, &
70 & constants % icav_ja, workspace % tmp_cav, &
71 & workspace % tmp_grid)
72 state % g_lpb = - workspace % tmp_grid
73
74 ! store the gradient of the potential (- electric field)
75 state % gradphi_cav = - e_cav
76
77 ! wghpot_f : Intermediate computation of F_0 Eq.(75) from QSM19.SISC
78 call wghpot_f(params, constants, workspace, state % gradphi_cav, &
79 & state % f_lpb, ddx_error)
80
81 ! Setting of the local variables
82 state % rhs_lpb = zero
83
84 ! integrate RHS
85 call ddintegrate(params % nsph, constants % nbasis, &
86 & params % ngrid, constants % vwgrid, &
87 & constants % vgrid_nbasis, state % g_lpb, state % rhs_lpb(:,:,1))
88 call ddintegrate(params % nsph, constants % nbasis, &
89 & params % ngrid, constants % vwgrid, &
90 & constants % vgrid_nbasis, state % f_lpb, state % rhs_lpb(:,:,2))
91 state % rhs_lpb(:,:,1) = state % rhs_lpb(:,:,1) + state % rhs_lpb(:,:,2)
92
93end subroutine lpb_setup
94
103subroutine lpb_energy(constants, state, esolv, ddx_error)
104 implicit none
105 type(ddx_constants_type), intent(in) :: constants
106 type(ddx_state_type), intent(in) :: state
107 type(ddx_error_type), intent(inout) :: ddx_error
108 real(dp), intent(out) :: esolv
109 real(dp), external :: ddot
110
111 ! dummy operation on unused interface arguments
112 if (ddx_error % flag .eq. 0) continue
113
114 esolv = pt5*ddot(constants % n, state % x_lpb(:,:,1), 1, state % psi, 1)
115end subroutine lpb_energy
116
127subroutine lpb_guess(params, constants, workspace, state, tol, ddx_error)
128 implicit none
129 type(ddx_params_type), intent(in) :: params
130 type(ddx_constants_type), intent(inout) :: constants
131 type(ddx_workspace_type), intent(inout) :: workspace
132 type(ddx_state_type), intent(inout) :: state
133 real(dp), intent(in) :: tol
134 type(ddx_error_type), intent(inout) :: ddx_error
135
136 ! set the inner tolerance
137 constants % inner_tol = sqrt(tol)
138
139 ! guess
140 workspace % ddcosmo_guess = zero
141 workspace % hsp_guess = zero
142 call prec_tx(params, constants, workspace, state % rhs_lpb, &
143 & state % x_lpb, ddx_error)
144
145end subroutine lpb_guess
146
157subroutine lpb_guess_adjoint(params, constants, workspace, state, tol, &
158 & ddx_error)
159 implicit none
160 type(ddx_params_type), intent(in) :: params
161 type(ddx_constants_type), intent(inout) :: constants
162 type(ddx_workspace_type), intent(inout) :: workspace
163 type(ddx_state_type), intent(inout) :: state
164 real(dp), intent(in) :: tol
165 type(ddx_error_type), intent(inout) :: ddx_error
166
167 ! set the inner tolerance
168 constants % inner_tol = sqrt(tol)
169
170 ! guess
171 workspace % ddcosmo_guess = zero
172 workspace % hsp_guess = zero
173 call prec_tstarx(params, constants, workspace, state % rhs_adj_lpb, &
174 & state % x_adj_lpb, ddx_error)
175
176end subroutine lpb_guess_adjoint
177
188subroutine lpb_solve(params, constants, workspace, state, tol, ddx_error)
189 implicit none
190 type(ddx_params_type), intent(in) :: params
191 type(ddx_constants_type), intent(inout) :: constants
192 type(ddx_workspace_type), intent(inout) :: workspace
193 type(ddx_state_type), intent(inout) :: state
194 real(dp), intent(in) :: tol
195 type(ddx_error_type), intent(inout) :: ddx_error
196 real(dp) :: start_time
197
198 state % x_lpb_niter = params % maxiter
199 workspace % xs_time = zero
200 workspace % hsp_time = zero
201
202 ! solve LS using Jacobi/DIIS
203 start_time = omp_get_wtime()
204 call jacobi_diis_external(params, constants, workspace, &
205 & 2*constants % n, tol, state % rhs_lpb, state % x_lpb, &
206 & state % x_lpb_niter, state % x_lpb_rel_diff, cx, prec_tx, rmsnorm, &
207 & ddx_error)
208 if (ddx_error % flag.ne. 0) then
209 call update_error(ddx_error, 'Jacobi solver failed to converge " // &
210 & "in ddlpb_solve')
211 return
212 end if
213 state % x_lpb_time = omp_get_wtime() - start_time
214 ! in ddLPB the polarization density X is expanded according to a
215 ! slightly different definition which lacks the 4pi/(2l + 1) factors
216 ! which are present in ddCOSMO and ddPCM. Before exiting, we scale
217 ! the density so that it is consistent with the ddCOSMO and ddPCM
218 ! ones.
219 call convert_ddcosmo(params, constants, -1, state % x_lpb(:,:,1))
220
221 ! put the timings in the right places
222 state % xs_time = workspace % xs_time
223 state % hsp_time = workspace % hsp_time
224end subroutine lpb_solve
225
236subroutine lpb_solve_adjoint(params, constants, workspace, state, tol, ddx_error)
237 implicit none
238 type(ddx_params_type), intent(in) :: params
239 type(ddx_constants_type), intent(inout) :: constants
240 type(ddx_workspace_type), intent(inout) :: workspace
241 type(ddx_state_type), intent(inout) :: state
242 real(dp), intent(in) :: tol
243 type(ddx_error_type), intent(inout) :: ddx_error
244
245 real(dp) :: start_time
246
247 state % x_adj_lpb_niter = params % maxiter
248 workspace % s_time = zero
249 workspace % hsp_adj_time = zero
250
251 ! solve adjoint LS using Jacobi/DIIS
252 start_time = omp_get_wtime()
253 call jacobi_diis_external(params, constants, workspace, &
254 & 2*constants % n, tol, state % rhs_adj_lpb, state % x_adj_lpb, &
255 & state % x_adj_lpb_niter, state % x_adj_lpb_rel_diff, &
256 & cstarx, prec_tstarx, rmsnorm, ddx_error)
257 if (ddx_error % flag .ne. 0) then
258 call update_error(ddx_error, 'Jacobi solver failed to ' // &
259 & 'converge in ddlpb_solve_adjoint')
260 return
261 end if
262 state % x_adj_lpb_time = omp_get_wtime() - start_time
263
264 state % q = state % x_adj_lpb(:, :, 1)
265
266 ! put the timings in the right places
267 state % s_time = workspace % s_time
268 state % hsp_adj_time = workspace % hsp_adj_time
269
270 call lpb_derivative_setup(params, constants, workspace, state, ddx_error)
271
272end subroutine lpb_solve_adjoint
273
286subroutine lpb_solvation_force_terms(params, constants, workspace, &
287 & state, hessianphi_cav, force, ddx_error)
288 implicit none
289 type(ddx_params_type), intent(in) :: params
290 type(ddx_constants_type), intent(in) :: constants
291 type(ddx_workspace_type), intent(inout) :: workspace
292 type(ddx_state_type), intent(inout) :: state
293 real(dp), intent(in) :: hessianphi_cav(3, 3, constants % ncav)
294 real(dp), intent(out) :: force(3, params % nsph)
295 type(ddx_error_type), intent(inout) :: ddx_error
296
297 ! local
298 real(dp), dimension(constants % nbasis) :: basloc, vplm
299 real(dp), dimension(3, constants % nbasis) :: dbasloc
300 real(dp), dimension(params % lmax + 1) :: vsin, vcos
301
302 ! large local are allocatable
303 real(dp), allocatable :: normal_hessian_cav(:,:), &
304 & diff_re(:,:), scaled_xr(:,:)
305 integer :: isph, icav, igrid, istat
306 integer :: i
307 real(dp), external :: ddot, dnrm2
308 real(dp) :: start_time, finish_time
309
310 start_time = omp_get_wtime()
311 allocate(normal_hessian_cav(3, constants % ncav), &
312 & diff_re(constants % nbasis, params % nsph), &
313 & scaled_xr(constants % nbasis, params % nsph), stat=istat)
314 if (istat.ne.0) then
315 call update_error(ddx_error, 'Allocation failed in ddlpb_force_worker')
316 return
317 end if
318
319 ! we have to insert again the factor that we removed in ddlpb_solve
320 call convert_ddcosmo(params, constants, 1, state % x_lpb(:, :, 1))
321
322 diff_re = zero
323 vsin = zero
324 vcos = zero
325 vplm = zero
326 basloc = zero
327 dbasloc = zero
328 force = zero
329
330 ! Compute the derivative of the normal derivative of psi_0
331 normal_hessian_cav = zero
332 icav = 0
333 do isph = 1, params % nsph
334 do igrid = 1, params % ngrid
335 if(constants % ui(igrid, isph) .gt. zero) then
336 icav = icav + 1
337 do i = 1, 3
338 normal_hessian_cav(:, icav) = normal_hessian_cav(:,icav) +&
339 & hessianphi_cav(:,i,icav)*constants % cgrid(i,igrid)
340 end do
341 end if
342 end do
343 end do
344
345 ! Scale by the factor of 1/(4Pi/(2l+1))
346 scaled_xr = state % x_lpb(:,:,1)
347 call convert_ddcosmo(params, constants, -1, scaled_xr)
348
349 !$omp parallel do default(none) shared(params,constants,workspace, &
350 !$omp scaled_xr,state,force) private(isph,basloc,dbasloc,vplm,vcos,vsin) &
351 !$omp schedule(static,1)
352 do isph = 1, params % nsph
353 ! Compute A^k*Xadj_r, using Subroutine from ddCOSMO
354 call contract_grad_l(params, constants, isph, scaled_xr, &
355 & state % x_adj_r_grid, basloc, dbasloc, vplm, vcos, vsin, &
356 & force(:,isph))
357 ! Compute B^k*Xadj_e
358 call contract_grad_b(params, constants, isph, state % x_lpb(:,:,2), &
359 & state % x_adj_e_grid, force(:, isph))
360 ! Computation of G0
361 call contract_grad_u(params, constants, isph, state % x_adj_r_grid, &
362 & state % phi_grid, force(:, isph))
363 end do
364 ! Compute C1 and C2 contributions
365 diff_re = zero
366 call contract_grad_c(params, constants, workspace, state % x_lpb(:,:,1), &
367 & state % x_lpb(:,:,2), state % x_adj_r_grid, state % x_adj_e_grid, &
368 & state % x_adj_lpb(:,:,1), state % x_adj_lpb(:,:,2), force, &
369 & diff_re, ddx_error)
370 if (ddx_error % flag .ne. 0) then
371 call update_error(ddx_error, &
372 & "contract_grad_C returned an error, exiting")
373 return
374 end if
375 ! Computation of G0 continued
376
377 ! NOTE: contract_grad_U returns a positive summation
378 force = -force
379 icav = 0
380 do isph = 1, params % nsph
381 do igrid = 1, params % ngrid
382 if(constants % ui(igrid, isph) .ne. zero) then
383 icav = icav + 1
384 state % zeta(icav) = constants % wgrid(igrid) * &
385 & constants % ui(igrid, isph) * ddot(constants % nbasis, &
386 & constants % vgrid(1, igrid), 1, &
387 & state % x_adj_lpb(1, isph, 1), 1)
388 end if
389 end do
390 end do
391
392 ! Computation of F0
393 call contract_grad_f(params, constants, workspace, &
394 & state % x_adj_lpb(:,:,1) + state % x_adj_lpb(:,:,2), &
395 & state % x_adj_re_grid, state % gradphi_cav, &
396 & normal_hessian_cav, force, state, ddx_error)
397 if (ddx_error % flag .ne. 0) then
398 call update_error(ddx_error, &
399 & "contract_grad_f returned an error, exiting")
400 return
401 end if
402
403 force = pt5*force
404
405 deallocate(normal_hessian_cav, diff_re, scaled_xr, stat=istat)
406 if (istat.ne.0) then
407 call update_error(ddx_error, 'Deallocation failed in ddlpb_force_worker')
408 return
409 end if
410
411 ! and now we remove again the factor, so that the density is
412 ! restored.
413 call convert_ddcosmo(params, constants, -1, state % x_lpb(:, :, 1))
414
415 ! Finally add the zeta - electric field contribution. Since the
416 ! subroutine takes as input the electric field, we temporarily swap
417 ! the sign of the gradient of the potential (this trick avoids
418 ! dangerous memory allocation)
419 state % gradphi_cav = - state % gradphi_cav
420 call zeta_grad(params, constants, state, state % gradphi_cav, force)
421 state % gradphi_cav = - state % gradphi_cav
422
423 finish_time = omp_get_wtime()
424 state % force_time = finish_time - start_time
425
426end subroutine lpb_solvation_force_terms
427
437subroutine lpb_derivative_setup(params, constants, workspace, state, ddx_error)
438 implicit none
439 type(ddx_params_type), intent(in) :: params
440 type(ddx_constants_type), intent(in) :: constants
441 type(ddx_workspace_type), intent(inout) :: workspace
442 type(ddx_state_type), intent(inout) :: state
443 type(ddx_error_type), intent(inout) :: ddx_error
444
445 real(dp), allocatable :: coefy_d(:, :, :)
446 integer :: istat, jsph, igrid0, icav, isph, igrid, l0, m0, ind0, &
447 & inode, indl
448 real(dp) :: rijn, sum_int, fac
449 real(dp) :: work(constants % lmax0 + 1), coef(constants % nbasis0), &
450 & vij(3), vtij(3), sij(3)
451 complex(dp) :: work_complex(constants % lmax0 + 1)
452
453 ! expand the adjoint solutions at the Lebedev points and compute
454 ! their sum
455 call dgemm('T', 'N', params % ngrid, params % nsph, &
456 & constants % nbasis, one, constants % vgrid, &
457 & constants % vgrid_nbasis, state % x_adj_lpb(:, :, 1), &
458 & constants % nbasis, zero, state % x_adj_r_grid, params % ngrid)
459 call dgemm('T', 'N', params % ngrid, params % nsph, &
460 & constants % nbasis, one, constants % vgrid, &
461 & constants % vgrid_nbasis, state % x_adj_lpb(:, :, 2), &
462 & constants % nbasis, zero, state % x_adj_e_grid, params % ngrid)
463 state % x_adj_re_grid = state % x_adj_r_grid + state % x_adj_e_grid
464
465 if (params % fmm .eq. 0) then
466 allocate(coefy_d(constants % ncav, params % ngrid, params % nsph), &
467 & stat=istat)
468 if (istat.ne.0) then
469 call update_error(ddx_error, "allocation ddx_error in " // &
470 & "ddlpb_derivative_setup")
471 return
472 end if
473 coefy_d = zero
474 do jsph = 1, params % nsph
475 do igrid0 = 1, params % ngrid
476 icav = zero
477 do isph = 1, params % nsph
478 do igrid = 1, params % ngrid
479 if(constants % ui(igrid, isph) .gt. zero) then
480 icav = icav + 1
481 vij = params % csph(:,isph) + params % rsph(isph) &
482 & *constants % cgrid(:,igrid) &
483 & - params % csph(:,jsph)
484 rijn = sqrt(dot_product(vij,vij))
485 if (rijn.ne.zero) then
486 sij = vij/rijn
487 else
488 sij = one
489 end if
490 do l0 = 0, constants % lmax0
491 do m0 = -l0, l0
492 ind0 = l0**2 + l0 + m0 + 1
493 coef(ind0) = &
494 & constants % vgrid(ind0, igrid0) * &
495 & constants % C_ik(l0, jsph)
496 end do
497 end do
498 vtij = vij*params % kappa
499 call fmm_m2p_bessel_work(vtij, constants % lmax0, &
500 & constants % vscales, &
501 & constants % sk_ri(:, jsph), one, &
502 & coef, zero, coefy_d(icav, igrid0, jsph), &
503 & work_complex, work)
504 end if
505 end do
506 end do
507 end do
508 end do
509 do jsph = 1, params % nsph
510 do igrid0 = 1, params % ngrid
511 icav = zero
512 sum_int = zero
513 do isph = 1, params % nsph
514 do igrid = 1, params % ngrid
515 if(constants % ui(igrid, isph) .gt. zero) then
516 icav = icav + 1
517 sum_int = sum_int + coefy_d(icav, igrid0, jsph) &
518 & *state % x_adj_re_grid(igrid, isph) &
519 & *constants % wgrid(igrid) &
520 & *constants % ui(igrid, isph)
521 end if
522 end do
523 end do
524 state % phi_n(igrid0, jsph) = &
525 & - (params % epsp/params % eps)*sum_int
526 end do
527 end do
528 else
529 ! Adjoint integration from spherical harmonics to grid points is not needed
530 ! here as ygrid already contains grid values, we just need to scale it by
531 ! weights of grid points
532 do isph = 1, params % nsph
533 workspace % tmp_grid(:, isph) = state % x_adj_re_grid(:, isph) * &
534 & constants % wgrid(:) * constants % ui(:, isph)
535 end do
536 ! Adjoint FMM
537 call tree_m2p_bessel_adj(params, constants, constants % lmax0, one, &
538 & workspace % tmp_grid, zero, params % lmax, workspace % tmp_sph)
539 call tree_l2p_bessel_adj(params, constants, one, &
540 & workspace % tmp_grid, zero, workspace % tmp_node_l)
541 call tree_l2l_bessel_rotation_adj(params, constants, &
542 & workspace % tmp_node_l)
543 call tree_m2l_bessel_rotation_adj(params, constants, &
544 & workspace % tmp_node_l, workspace % tmp_node_m)
545 call tree_m2m_bessel_rotation_adj(params, constants, &
546 & workspace % tmp_node_m)
547 ! Properly load adjoint multipole harmonics into tmp_sph
548 if(constants % lmax0 .lt. params % pm) then
549 do isph = 1, params % nsph
550 inode = constants % snode(isph)
551 workspace % tmp_sph(1:constants % nbasis0, isph) = &
552 & workspace % tmp_sph(1:constants % nbasis0, isph) + &
553 & workspace % tmp_node_m(1:constants % nbasis0, inode)
554 end do
555 else
556 indl = (params % pm+1)**2
557 do isph = 1, params % nsph
558 inode = constants % snode(isph)
559 workspace % tmp_sph(1:indl, isph) = &
560 & workspace % tmp_sph(1:indl, isph) + &
561 & workspace % tmp_node_m(:, inode)
562 end do
563 end if
564 ! Scale by C_ik
565 do isph = 1, params % nsph
566 do l0 = 0, constants % lmax0
567 ind0 = l0*l0 + l0 + 1
568 workspace % tmp_sph(ind0-l0:ind0+l0, isph) = &
569 & workspace % tmp_sph(ind0-l0:ind0+l0, isph) * &
570 & constants % C_ik(l0, isph)
571 end do
572 end do
573 ! Multiply by vgrid
574 call dgemm('T', 'N', params % ngrid, params % nsph, &
575 & constants % nbasis0, -params % epsp/params % eps, &
576 & constants % vgrid, constants % vgrid_nbasis, &
577 & workspace % tmp_sph, constants % nbasis, zero, &
578 & state % phi_n, params % ngrid)
579 end if
580
581 ! assemble the pseudo-dipoles
582 icav = 0
583 do isph = 1, params % nsph
584 do igrid = 1, params % ngrid
585 if (constants % ui(igrid, isph) .gt. zero) then
586 icav = icav + 1
587 fac = - constants % ui(igrid, isph)*constants % wgrid(igrid) &
588 & *state % phi_n(igrid, isph)
589 state % zeta_dip(1, icav) = fac*constants % cgrid(1, igrid)
590 state % zeta_dip(2, icav) = fac*constants % cgrid(2, igrid)
591 state % zeta_dip(3, icav) = fac*constants % cgrid(3, igrid)
592 end if
593 end do
594 end do
595
596 if (allocated(coefy_d)) then
597 deallocate(coefy_d, stat=istat)
598 if (istat.ne.0) then
599 call update_error(ddx_error, &
600 & "deallocation ddx_error in ddlpb_derivative_setup")
601 return
602 end if
603 end if
604
605end subroutine lpb_derivative_setup
606
607end module ddx_lpb
subroutine lpb_solve(params, constants, workspace, state, tol, ddx_error)
Solve the ddLPB primal linear system.
Definition: ddx_lpb.f90:189
subroutine lpb_energy(constants, state, esolv, ddx_error)
Compute the ddLPB energy.
Definition: ddx_lpb.f90:104
subroutine lpb_setup(params, constants, workspace, state, phi_cav, e_cav, psi, ddx_error)
Given the potential and the electric field at the cavity points, assemble the RHS for ddLPB.
Definition: ddx_lpb.f90:37
subroutine lpb_solve_adjoint(params, constants, workspace, state, tol, ddx_error)
Solve the adjoint ddLPB linear system.
Definition: ddx_lpb.f90:237
subroutine lpb_guess_adjoint(params, constants, workspace, state, tol, ddx_error)
Do a guess for the adjoint ddLPB linear system.
Definition: ddx_lpb.f90:159
subroutine lpb_guess(params, constants, workspace, state, tol, ddx_error)
Do a guess for the primal ddLPB linear system.
Definition: ddx_lpb.f90:128
subroutine lpb_solvation_force_terms(params, constants, workspace, state, hessianphi_cav, force, ddx_error)
Compute the solvation terms of the forces (solute aspecific). This must be summed to the solute speci...
Definition: ddx_lpb.f90:288
High-level subroutines for ddlpb.
Definition: ddx_lpb.f90:13
Operators shared among ddX methods.
subroutine prec_tx(params, constants, workspace, x, y, ddx_error)
Apply the preconditioner to the primal ddLPB linear system |Yr| = |A^-1 0 |*|Xr| |Ye| |0 B^-1 | |Xe|.
subroutine cstarx(params, constants, workspace, x, y, ddx_error)
ddLPB adjoint matrix-vector product
subroutine cx(params, constants, workspace, x, y, ddx_error)
ddLPB matrix-vector product
subroutine prec_tstarx(params, constants, workspace, x, y, ddx_error)
Apply the preconditioner to the ddLPB adjoint linear system.