ddx 0.6.8
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
93 if (ddx_error % flag .eq. 0) then
94 state % rhs_done = .true.
95 state % adjoint_rhs_done = .true.
96 end if
97
98end subroutine lpb_setup
99
108subroutine lpb_energy(constants, state, esolv, ddx_error)
109 implicit none
110 type(ddx_constants_type), intent(in) :: constants
111 type(ddx_state_type), intent(in) :: state
112 type(ddx_error_type), intent(inout) :: ddx_error
113 real(dp), intent(out) :: esolv
114 real(dp), external :: ddot
115
116 ! dummy operation on unused interface arguments
117 if (ddx_error % flag .eq. 0) continue
118
119 esolv = pt5*ddot(constants % n, state % x_lpb(:,:,1), 1, state % psi, 1)
120end subroutine lpb_energy
121
132subroutine lpb_guess(params, constants, workspace, state, tol, ddx_error)
133 implicit none
134 type(ddx_params_type), intent(in) :: params
135 type(ddx_constants_type), intent(inout) :: constants
136 type(ddx_workspace_type), intent(inout) :: workspace
137 type(ddx_state_type), intent(inout) :: state
138 real(dp), intent(in) :: tol
139 type(ddx_error_type), intent(inout) :: ddx_error
140
141 ! set the inner tolerance
142 constants % inner_tol = sqrt(tol)
143
144 ! guess
145 workspace % ddcosmo_guess = zero
146 workspace % hsp_guess = zero
147 call prec_tx(params, constants, workspace, state % rhs_lpb, &
148 & state % x_lpb, ddx_error)
149
150end subroutine lpb_guess
151
162subroutine lpb_guess_adjoint(params, constants, workspace, state, tol, &
163 & ddx_error)
164 implicit none
165 type(ddx_params_type), intent(in) :: params
166 type(ddx_constants_type), intent(inout) :: constants
167 type(ddx_workspace_type), intent(inout) :: workspace
168 type(ddx_state_type), intent(inout) :: state
169 real(dp), intent(in) :: tol
170 type(ddx_error_type), intent(inout) :: ddx_error
171
172 ! set the inner tolerance
173 constants % inner_tol = sqrt(tol)
174
175 ! guess
176 workspace % ddcosmo_adj_guess = zero
177 workspace % hsp_adj_guess = zero
178 call prec_tstarx(params, constants, workspace, state % rhs_adj_lpb, &
179 & state % x_adj_lpb, ddx_error)
180
181end subroutine lpb_guess_adjoint
182
193subroutine lpb_solve(params, constants, workspace, state, tol, ddx_error)
194 implicit none
195 type(ddx_params_type), intent(in) :: params
196 type(ddx_constants_type), intent(inout) :: constants
197 type(ddx_workspace_type), intent(inout) :: workspace
198 type(ddx_state_type), intent(inout) :: state
199 real(dp), intent(in) :: tol
200 type(ddx_error_type), intent(inout) :: ddx_error
201 real(dp) :: start_time
202
203 state % x_lpb_niter = params % maxiter
204 workspace % xs_time = zero
205 workspace % hsp_time = zero
206
207 ! solve LS using Jacobi/DIIS
208 start_time = omp_get_wtime()
209 call jacobi_diis_external(params, constants, workspace, &
210 & 2*constants % n, tol, state % rhs_lpb, state % x_lpb, &
211 & state % x_lpb_niter, state % x_lpb_rel_diff, cx, prec_tx, rmsnorm, &
212 & ddx_error)
213 if (ddx_error % flag.ne. 0) then
214 call update_error(ddx_error, 'Jacobi solver failed to converge " // &
215 & "in ddlpb_solve')
216 return
217 end if
218 state % x_lpb_time = omp_get_wtime() - start_time
219 ! in ddLPB the polarization density X is expanded according to a
220 ! slightly different definition which lacks the 4pi/(2l + 1) factors
221 ! which are present in ddCOSMO and ddPCM. Before exiting, we scale
222 ! the density so that it is consistent with the ddCOSMO and ddPCM
223 ! ones.
224 call convert_ddcosmo(params, constants, -1, state % x_lpb(:,:,1))
225
226 ! put the timings in the right places
227 state % xs_time = workspace % xs_time
228 state % hsp_time = workspace % hsp_time
229end subroutine lpb_solve
230
241subroutine lpb_solve_adjoint(params, constants, workspace, state, tol, ddx_error)
242 implicit none
243 type(ddx_params_type), intent(in) :: params
244 type(ddx_constants_type), intent(inout) :: constants
245 type(ddx_workspace_type), intent(inout) :: workspace
246 type(ddx_state_type), intent(inout) :: state
247 real(dp), intent(in) :: tol
248 type(ddx_error_type), intent(inout) :: ddx_error
249
250 real(dp) :: start_time
251
252 state % x_adj_lpb_niter = params % maxiter
253 workspace % s_time = zero
254 workspace % hsp_adj_time = zero
255
256 ! solve adjoint LS using Jacobi/DIIS
257 start_time = omp_get_wtime()
258 call jacobi_diis_external(params, constants, workspace, &
259 & 2*constants % n, tol, state % rhs_adj_lpb, state % x_adj_lpb, &
260 & state % x_adj_lpb_niter, state % x_adj_lpb_rel_diff, &
261 & cstarx, prec_tstarx, rmsnorm, ddx_error)
262 if (ddx_error % flag .ne. 0) then
263 call update_error(ddx_error, 'Jacobi solver failed to ' // &
264 & 'converge in ddlpb_solve_adjoint')
265 return
266 end if
267 state % x_adj_lpb_time = omp_get_wtime() - start_time
268
269 state % q = state % x_adj_lpb(:, :, 1)
270
271 ! put the timings in the right places
272 state % s_time = workspace % s_time
273 state % hsp_adj_time = workspace % hsp_adj_time
274
275 call lpb_derivative_setup(params, constants, workspace, state, ddx_error)
276
277end subroutine lpb_solve_adjoint
278
291subroutine lpb_solvation_force_terms(params, constants, workspace, &
292 & state, hessianphi_cav, force, ddx_error)
293 implicit none
294 type(ddx_params_type), intent(in) :: params
295 type(ddx_constants_type), intent(in) :: constants
296 type(ddx_workspace_type), intent(inout) :: workspace
297 type(ddx_state_type), intent(inout) :: state
298 real(dp), intent(in) :: hessianphi_cav(3, 3, constants % ncav)
299 real(dp), intent(out) :: force(3, params % nsph)
300 type(ddx_error_type), intent(inout) :: ddx_error
301
302 ! local
303 real(dp), dimension(constants % nbasis) :: basloc, vplm
304 real(dp), dimension(3, constants % nbasis) :: dbasloc
305 real(dp), dimension(params % lmax + 1) :: vsin, vcos
306
307 ! large local are allocatable
308 real(dp), allocatable :: normal_hessian_cav(:,:), &
309 & diff_re(:,:), scaled_xr(:,:)
310 integer :: isph, icav, igrid, istat
311 integer :: i
312 real(dp), external :: ddot, dnrm2
313 real(dp) :: start_time, finish_time
314
315 start_time = omp_get_wtime()
316 allocate(normal_hessian_cav(3, constants % ncav), &
317 & diff_re(constants % nbasis, params % nsph), &
318 & scaled_xr(constants % nbasis, params % nsph), stat=istat)
319 if (istat.ne.0) then
320 call update_error(ddx_error, 'Allocation failed in ddlpb_force_worker')
321 return
322 end if
323
324 ! we have to insert again the factor that we removed in ddlpb_solve
325 call convert_ddcosmo(params, constants, 1, state % x_lpb(:, :, 1))
326
327 diff_re = zero
328 vsin = zero
329 vcos = zero
330 vplm = zero
331 basloc = zero
332 dbasloc = zero
333 force = zero
334
335 ! Compute the derivative of the normal derivative of psi_0
336 normal_hessian_cav = zero
337 icav = 0
338 do isph = 1, params % nsph
339 do igrid = 1, params % ngrid
340 if(constants % ui(igrid, isph) .gt. zero) then
341 icav = icav + 1
342 do i = 1, 3
343 normal_hessian_cav(:, icav) = normal_hessian_cav(:,icav) +&
344 & hessianphi_cav(:,i,icav)*constants % cgrid(i,igrid)
345 end do
346 end if
347 end do
348 end do
349
350 ! Scale by the factor of 1/(4Pi/(2l+1))
351 scaled_xr = state % x_lpb(:,:,1)
352 call convert_ddcosmo(params, constants, -1, scaled_xr)
353
354 !$omp parallel do default(none) shared(params,constants,workspace, &
355 !$omp scaled_xr,state,force) private(isph,basloc,dbasloc,vplm,vcos,vsin) &
356 !$omp schedule(static,1)
357 do isph = 1, params % nsph
358 ! Compute A^k*Xadj_r, using Subroutine from ddCOSMO
359 call contract_grad_l(params, constants, isph, scaled_xr, &
360 & state % x_adj_r_grid, basloc, dbasloc, vplm, vcos, vsin, &
361 & force(:,isph))
362 ! Compute B^k*Xadj_e
363 call contract_grad_b(params, constants, isph, state % x_lpb(:,:,2), &
364 & state % x_adj_e_grid, force(:, isph))
365 ! Computation of G0
366 call contract_grad_u(params, constants, isph, state % x_adj_r_grid, &
367 & state % phi_grid, force(:, isph))
368 end do
369 ! Compute C1 and C2 contributions
370 diff_re = zero
371 call contract_grad_c(params, constants, workspace, state % x_lpb(:,:,1), &
372 & state % x_lpb(:,:,2), state % x_adj_r_grid, state % x_adj_e_grid, &
373 & state % x_adj_lpb(:,:,1), state % x_adj_lpb(:,:,2), force, &
374 & diff_re, ddx_error)
375 if (ddx_error % flag .ne. 0) then
376 call update_error(ddx_error, &
377 & "contract_grad_C returned an error, exiting")
378 return
379 end if
380 ! Computation of G0 continued
381
382 ! NOTE: contract_grad_U returns a positive summation
383 force = -force
384 icav = 0
385 do isph = 1, params % nsph
386 do igrid = 1, params % ngrid
387 if(constants % ui(igrid, isph) .ne. zero) then
388 icav = icav + 1
389 state % zeta(icav) = constants % wgrid(igrid) * &
390 & constants % ui(igrid, isph) * ddot(constants % nbasis, &
391 & constants % vgrid(1, igrid), 1, &
392 & state % x_adj_lpb(1, isph, 1), 1)
393 end if
394 end do
395 end do
396
397 ! Computation of F0
398 call contract_grad_f(params, constants, workspace, &
399 & state % x_adj_lpb(:,:,1) + state % x_adj_lpb(:,:,2), &
400 & state % x_adj_re_grid, state % gradphi_cav, &
401 & normal_hessian_cav, force, state, ddx_error)
402 if (ddx_error % flag .ne. 0) then
403 call update_error(ddx_error, &
404 & "contract_grad_f returned an error, exiting")
405 return
406 end if
407
408 force = pt5*force
409
410 deallocate(normal_hessian_cav, diff_re, scaled_xr, stat=istat)
411 if (istat.ne.0) then
412 call update_error(ddx_error, 'Deallocation failed in ddlpb_force_worker')
413 return
414 end if
415
416 ! and now we remove again the factor, so that the density is
417 ! restored.
418 call convert_ddcosmo(params, constants, -1, state % x_lpb(:, :, 1))
419
420 ! Finally add the zeta - electric field contribution. Since the
421 ! subroutine takes as input the electric field, we temporarily swap
422 ! the sign of the gradient of the potential (this trick avoids
423 ! dangerous memory allocation)
424 state % gradphi_cav = - state % gradphi_cav
425 call zeta_grad(params, constants, state, state % gradphi_cav, force)
426 state % gradphi_cav = - state % gradphi_cav
427
428 finish_time = omp_get_wtime()
429 state % force_time = finish_time - start_time
430
431end subroutine lpb_solvation_force_terms
432
442subroutine lpb_derivative_setup(params, constants, workspace, state, ddx_error)
443 implicit none
444 type(ddx_params_type), intent(in) :: params
445 type(ddx_constants_type), intent(in) :: constants
446 type(ddx_workspace_type), intent(inout) :: workspace
447 type(ddx_state_type), intent(inout) :: state
448 type(ddx_error_type), intent(inout) :: ddx_error
449
450 real(dp), allocatable :: coefy_d(:, :, :)
451 integer :: istat, jsph, igrid0, icav, isph, igrid, l0, m0, ind0, &
452 & inode, indl
453 real(dp) :: rijn, sum_int, fac
454 real(dp) :: work(constants % lmax0 + 1), coef(constants % nbasis0), &
455 & vij(3), vtij(3), sij(3)
456 complex(dp) :: work_complex(constants % lmax0 + 1)
457
458 ! expand the adjoint solutions at the Lebedev points and compute
459 ! their sum
460 call dgemm('T', 'N', params % ngrid, params % nsph, &
461 & constants % nbasis, one, constants % vgrid, &
462 & constants % vgrid_nbasis, state % x_adj_lpb(:, :, 1), &
463 & constants % nbasis, zero, state % x_adj_r_grid, params % ngrid)
464 call dgemm('T', 'N', params % ngrid, params % nsph, &
465 & constants % nbasis, one, constants % vgrid, &
466 & constants % vgrid_nbasis, state % x_adj_lpb(:, :, 2), &
467 & constants % nbasis, zero, state % x_adj_e_grid, params % ngrid)
468 state % x_adj_re_grid = state % x_adj_r_grid + state % x_adj_e_grid
469
470 if (params % fmm .eq. 0) then
471 allocate(coefy_d(constants % ncav, params % ngrid, params % nsph), &
472 & stat=istat)
473 if (istat.ne.0) then
474 call update_error(ddx_error, "allocation ddx_error in " // &
475 & "ddlpb_derivative_setup")
476 return
477 end if
478 coefy_d = zero
479 do jsph = 1, params % nsph
480 do igrid0 = 1, params % ngrid
481 icav = zero
482 do isph = 1, params % nsph
483 do igrid = 1, params % ngrid
484 if(constants % ui(igrid, isph) .gt. zero) then
485 icav = icav + 1
486 vij = params % csph(:,isph) + params % rsph(isph) &
487 & *constants % cgrid(:,igrid) &
488 & - params % csph(:,jsph)
489 rijn = sqrt(dot_product(vij,vij))
490 if (rijn.ne.zero) then
491 sij = vij/rijn
492 else
493 sij = one
494 end if
495 do l0 = 0, constants % lmax0
496 do m0 = -l0, l0
497 ind0 = l0**2 + l0 + m0 + 1
498 coef(ind0) = &
499 & constants % vgrid(ind0, igrid0) * &
500 & constants % C_ik(l0, jsph)
501 end do
502 end do
503 vtij = vij*params % kappa
504 call fmm_m2p_bessel_work(vtij, constants % lmax0, &
505 & constants % vscales, &
506 & constants % sk_ri(:, jsph), one, &
507 & coef, zero, coefy_d(icav, igrid0, jsph), &
508 & work_complex, work)
509 end if
510 end do
511 end do
512 end do
513 end do
514 do jsph = 1, params % nsph
515 do igrid0 = 1, params % ngrid
516 icav = zero
517 sum_int = zero
518 do isph = 1, params % nsph
519 do igrid = 1, params % ngrid
520 if(constants % ui(igrid, isph) .gt. zero) then
521 icav = icav + 1
522 sum_int = sum_int + coefy_d(icav, igrid0, jsph) &
523 & *state % x_adj_re_grid(igrid, isph) &
524 & *constants % wgrid(igrid) &
525 & *constants % ui(igrid, isph)
526 end if
527 end do
528 end do
529 state % phi_n(igrid0, jsph) = &
530 & - (params % epsp/params % eps)*sum_int
531 end do
532 end do
533 else
534 ! Adjoint integration from spherical harmonics to grid points is not needed
535 ! here as ygrid already contains grid values, we just need to scale it by
536 ! weights of grid points
537 do isph = 1, params % nsph
538 workspace % tmp_grid(:, isph) = state % x_adj_re_grid(:, isph) * &
539 & constants % wgrid(:) * constants % ui(:, isph)
540 end do
541 ! Adjoint FMM
542 call tree_m2p_bessel_adj(params, constants, constants % lmax0, one, &
543 & workspace % tmp_grid, zero, params % lmax, workspace % tmp_sph)
544 call tree_l2p_bessel_adj(params, constants, one, &
545 & workspace % tmp_grid, zero, workspace % tmp_node_l)
546 call tree_l2l_bessel_rotation_adj(params, constants, &
547 & workspace % tmp_node_l)
548 call tree_m2l_bessel_rotation_adj(params, constants, &
549 & workspace % tmp_node_l, workspace % tmp_node_m)
550 call tree_m2m_bessel_rotation_adj(params, constants, &
551 & workspace % tmp_node_m)
552 ! Properly load adjoint multipole harmonics into tmp_sph
553 if(constants % lmax0 .lt. params % pm) then
554 do isph = 1, params % nsph
555 inode = constants % snode(isph)
556 workspace % tmp_sph(1:constants % nbasis0, isph) = &
557 & workspace % tmp_sph(1:constants % nbasis0, isph) + &
558 & workspace % tmp_node_m(1:constants % nbasis0, inode)
559 end do
560 else
561 indl = (params % pm+1)**2
562 do isph = 1, params % nsph
563 inode = constants % snode(isph)
564 workspace % tmp_sph(1:indl, isph) = &
565 & workspace % tmp_sph(1:indl, isph) + &
566 & workspace % tmp_node_m(:, inode)
567 end do
568 end if
569 ! Scale by C_ik
570 do isph = 1, params % nsph
571 do l0 = 0, constants % lmax0
572 ind0 = l0*l0 + l0 + 1
573 workspace % tmp_sph(ind0-l0:ind0+l0, isph) = &
574 & workspace % tmp_sph(ind0-l0:ind0+l0, isph) * &
575 & constants % C_ik(l0, isph)
576 end do
577 end do
578 ! Multiply by vgrid
579 call dgemm('T', 'N', params % ngrid, params % nsph, &
580 & constants % nbasis0, -params % epsp/params % eps, &
581 & constants % vgrid, constants % vgrid_nbasis, &
582 & workspace % tmp_sph, constants % nbasis, zero, &
583 & state % phi_n, params % ngrid)
584 end if
585
586 ! assemble the pseudo-dipoles
587 icav = 0
588 do isph = 1, params % nsph
589 do igrid = 1, params % ngrid
590 if (constants % ui(igrid, isph) .gt. zero) then
591 icav = icav + 1
592 fac = - constants % ui(igrid, isph)*constants % wgrid(igrid) &
593 & *state % phi_n(igrid, isph)
594 state % zeta_dip(1, icav) = fac*constants % cgrid(1, igrid)
595 state % zeta_dip(2, icav) = fac*constants % cgrid(2, igrid)
596 state % zeta_dip(3, icav) = fac*constants % cgrid(3, igrid)
597 end if
598 end do
599 end do
600
601 if (allocated(coefy_d)) then
602 deallocate(coefy_d, stat=istat)
603 if (istat.ne.0) then
604 call update_error(ddx_error, &
605 & "deallocation ddx_error in ddlpb_derivative_setup")
606 return
607 end if
608 end if
609
610end subroutine lpb_derivative_setup
611
612end module ddx_lpb
subroutine lpb_solve(params, constants, workspace, state, tol, ddx_error)
Solve the ddLPB primal linear system.
Definition: ddx_lpb.f90:194
subroutine lpb_energy(constants, state, esolv, ddx_error)
Compute the ddLPB energy.
Definition: ddx_lpb.f90:109
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:242
subroutine lpb_guess_adjoint(params, constants, workspace, state, tol, ddx_error)
Do a guess for the adjoint ddLPB linear system.
Definition: ddx_lpb.f90:164
subroutine lpb_guess(params, constants, workspace, state, tol, ddx_error)
Do a guess for the primal ddLPB linear system.
Definition: ddx_lpb.f90:133
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:293
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.