Line data Source code
1 : !--------------------------------------------------------------------------------------------------!
2 : ! CP2K: A general program to perform molecular dynamics simulations !
3 : ! Copyright 2000-2026 CP2K developers group <https://cp2k.org> !
4 : ! !
5 : ! SPDX-License-Identifier: GPL-2.0-or-later !
6 : !--------------------------------------------------------------------------------------------------!
7 :
8 : ! **************************************************************************************************
9 : !> \brief The implicit (generalized) Poisson solver
10 : !> \par History
11 : !> 06.2014 created [Hossein Bani-Hashemian]
12 : !> 11.2015 - dealt with missing grid points of periodic grids while performing dct;
13 : !> - revised solver for Neumann and mixed boundary setups.
14 : !> \author Hossein Bani-Hashemian
15 : ! **************************************************************************************************
16 : MODULE ps_implicit_methods
17 : USE bibliography, ONLY: BaniHashemian2016,&
18 : cite_reference
19 : USE cp_log_handling, ONLY: cp_get_default_logger,&
20 : cp_logger_get_default_unit_nr,&
21 : cp_logger_type
22 : USE dct, ONLY: &
23 : dct_type, dct_type_init, neumannX, neumannXY, neumannXYZ, neumannXZ, neumannY, neumannYZ, &
24 : neumannZ, pw_expand, pw_shrink
25 : USE dielectric_methods, ONLY: derive_fft,&
26 : dielectric_create
27 : USE dielectric_types, ONLY: dielectric_type
28 : USE dirichlet_bc_methods, ONLY: dirichlet_boundary_region_setup
29 : USE dirichlet_bc_types, ONLY: dbc_tile_release
30 : USE kahan_sum, ONLY: accurate_sum
31 : USE kinds, ONLY: dp,&
32 : int_8
33 : USE mathconstants, ONLY: fourpi,&
34 : pi
35 : USE ps_implicit_types, ONLY: MIXED_BC,&
36 : MIXED_PERIODIC_BC,&
37 : NEUMANN_BC,&
38 : PERIODIC_BC,&
39 : ps_implicit_type
40 : USE pw_grid_types, ONLY: pw_grid_type
41 : USE pw_methods, ONLY: pw_axpy,&
42 : pw_copy,&
43 : pw_integral_ab,&
44 : pw_scale,&
45 : pw_transfer,&
46 : pw_zero
47 : USE pw_poisson_types, ONLY: greens_fn_type,&
48 : pw_poisson_parameter_type,&
49 : pw_poisson_type
50 : USE pw_pool_types, ONLY: pw_pool_create,&
51 : pw_pool_release,&
52 : pw_pool_type
53 : USE pw_types, ONLY: pw_c1d_gs_type,&
54 : pw_r3d_rs_type
55 : #include "../base/base_uses.f90"
56 :
57 : IMPLICIT NONE
58 : PRIVATE
59 : CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'ps_implicit_methods'
60 :
61 : PUBLIC ps_implicit_create, &
62 : implicit_poisson_solver_periodic, &
63 : implicit_poisson_solver_neumann, &
64 : implicit_poisson_solver_mixed_periodic, &
65 : implicit_poisson_solver_mixed
66 :
67 : INTERFACE ps_implicit_compute_ehartree
68 : MODULE PROCEDURE compute_ehartree_periodic_bc, &
69 : compute_ehartree_mixed_bc
70 : END INTERFACE ps_implicit_compute_ehartree
71 :
72 : REAL(dp), PRIVATE, PARAMETER :: large_error = 1.0E4_dp
73 :
74 : CONTAINS
75 :
76 : ! **************************************************************************************************
77 : !> \brief Creates implicit Poisson solver environment
78 : !> \param pw_pool pool of pw grid
79 : !> \param poisson_params poisson_env parameters
80 : !> \param dct_pw_grid discrete cosine transform (extended) grid
81 : !> \param green green function for FFT based inverse Laplacian
82 : !> \param ps_implicit_env implicit env to be created
83 : !> \par History
84 : !> 06.2014 created [Hossein Bani-Hashemian]
85 : !> \author Mohammad Hossein Bani-Hashemian
86 : ! **************************************************************************************************
87 56 : SUBROUTINE ps_implicit_create(pw_pool, poisson_params, dct_pw_grid, green, ps_implicit_env)
88 :
89 : TYPE(pw_pool_type), INTENT(IN), POINTER :: pw_pool
90 : TYPE(pw_poisson_parameter_type), INTENT(INOUT) :: poisson_params
91 : TYPE(pw_grid_type), INTENT(IN), POINTER :: dct_pw_grid
92 : TYPE(greens_fn_type), INTENT(IN), POINTER :: green
93 : TYPE(ps_implicit_type), INTENT(INOUT), POINTER :: ps_implicit_env
94 :
95 : CHARACTER(LEN=*), PARAMETER :: routineN = 'ps_implicit_create'
96 :
97 : INTEGER :: boundary_condition, handle, j, &
98 : n_contacts, neumann_directions
99 : TYPE(pw_pool_type), POINTER :: pw_pool_xpndd
100 :
101 56 : CALL timeset(routineN, handle)
102 :
103 56 : CALL cite_reference(BaniHashemian2016)
104 :
105 56 : IF (.NOT. ASSOCIATED(ps_implicit_env)) THEN
106 2296 : ALLOCATE (ps_implicit_env)
107 :
108 56 : ps_implicit_env%do_dbc_cube = poisson_params%dbc_params%do_dbc_cube
109 56 : boundary_condition = poisson_params%ps_implicit_params%boundary_condition
110 56 : neumann_directions = poisson_params%ps_implicit_params%neumann_directions
111 :
112 : ! create dielectric
113 : NULLIFY (ps_implicit_env%dielectric)
114 34 : SELECT CASE (boundary_condition)
115 : CASE (PERIODIC_BC, MIXED_PERIODIC_BC)
116 34 : CALL dielectric_create(ps_implicit_env%dielectric, pw_pool, poisson_params%dielectric_params)
117 : CASE (NEUMANN_BC, MIXED_BC)
118 22 : CALL pw_pool_create(pw_pool_xpndd, pw_grid=dct_pw_grid)
119 22 : CALL dielectric_create(ps_implicit_env%dielectric, pw_pool_xpndd, poisson_params%dielectric_params)
120 78 : CALL pw_pool_release(pw_pool_xpndd)
121 : END SELECT
122 :
123 : ! initial guess
124 56 : NULLIFY (ps_implicit_env%initial_guess)
125 :
126 : ! v_eps
127 56 : NULLIFY (ps_implicit_env%v_eps)
128 56 : ALLOCATE (ps_implicit_env%v_eps)
129 56 : CALL pw_pool%create_pw(ps_implicit_env%v_eps)
130 56 : CALL pw_zero(ps_implicit_env%v_eps)
131 :
132 : ! constraint charge
133 56 : NULLIFY (ps_implicit_env%cstr_charge)
134 22 : SELECT CASE (boundary_condition)
135 : CASE (MIXED_PERIODIC_BC)
136 22 : ALLOCATE (ps_implicit_env%cstr_charge)
137 22 : CALL pw_pool%create_pw(ps_implicit_env%cstr_charge)
138 22 : CALL pw_zero(ps_implicit_env%cstr_charge)
139 : CASE (MIXED_BC)
140 16 : CALL pw_pool_create(pw_pool_xpndd, pw_grid=dct_pw_grid)
141 16 : ALLOCATE (ps_implicit_env%cstr_charge)
142 16 : CALL pw_pool_xpndd%create_pw(ps_implicit_env%cstr_charge)
143 16 : CALL pw_zero(ps_implicit_env%cstr_charge)
144 72 : CALL pw_pool_release(pw_pool_xpndd)
145 : END SELECT
146 :
147 : ! initialize energies
148 56 : ps_implicit_env%ehartree = 0.0_dp
149 56 : ps_implicit_env%electric_enthalpy = 0.0_dp
150 : ! times called
151 56 : ps_implicit_env%times_called = 0
152 :
153 : ! dct env
154 56 : IF (boundary_condition == MIXED_BC .OR. boundary_condition == NEUMANN_BC) THEN
155 22 : CALL dct_type_init(pw_pool%pw_grid, neumann_directions, ps_implicit_env%dct_env)
156 : END IF
157 :
158 : ! prepare dirichlet bc
159 56 : CALL dirichlet_boundary_region_setup(pw_pool, poisson_params, ps_implicit_env%contacts)
160 56 : CALL ps_implicit_prepare_blocks(pw_pool, dct_pw_grid, green, poisson_params, ps_implicit_env)
161 : ! release tiles if they are not supposed to be written into cube files
162 56 : IF ((boundary_condition == MIXED_PERIODIC_BC .OR. boundary_condition == MIXED_BC) .AND. &
163 : (.NOT. poisson_params%dbc_params%do_dbc_cube)) THEN
164 36 : n_contacts = SIZE(ps_implicit_env%contacts)
165 160 : DO j = 1, n_contacts
166 160 : CALL dbc_tile_release(ps_implicit_env%contacts(j)%dirichlet_bc, pw_pool)
167 : END DO
168 : END IF
169 :
170 : END IF
171 :
172 56 : CALL timestop(handle)
173 :
174 56 : END SUBROUTINE ps_implicit_create
175 :
176 : ! **************************************************************************************************
177 : !> \brief implicit Poisson solver for periodic boundary conditions
178 : !> \param poisson_env poisson environment
179 : !> \param density electron density
180 : !> \param v_new electrostatic potential
181 : !> \param ehartree Hartree energy
182 : !> \par History
183 : !> 07.2014 created [Hossein Bani-Hashemian]
184 : !> \author Mohammad Hossein Bani-Hashemian
185 : ! **************************************************************************************************
186 150 : SUBROUTINE implicit_poisson_solver_periodic(poisson_env, density, v_new, ehartree)
187 :
188 : TYPE(pw_poisson_type), INTENT(IN) :: poisson_env
189 : TYPE(pw_r3d_rs_type), INTENT(IN) :: density
190 : TYPE(pw_r3d_rs_type), INTENT(INOUT) :: v_new
191 : REAL(dp), INTENT(OUT), OPTIONAL :: ehartree
192 :
193 : CHARACTER(LEN=*), PARAMETER :: routineN = 'implicit_poisson_solver_periodic'
194 :
195 : INTEGER :: handle, iter, max_iter, outp_unit, &
196 : times_called
197 : LOGICAL :: reached_max_iter, reached_tol, &
198 : use_zero_initial_guess
199 : REAL(dp) :: nabs_error, omega, pres_error, tol
200 : TYPE(dielectric_type), POINTER :: dielectric
201 : TYPE(greens_fn_type), POINTER :: green
202 : TYPE(ps_implicit_type), POINTER :: ps_implicit_env
203 : TYPE(pw_pool_type), POINTER :: pw_pool
204 : TYPE(pw_r3d_rs_type) :: g, PxQAinvxres, QAinvxres, res_new, &
205 : res_old, v_old
206 :
207 150 : CALL timeset(routineN, handle)
208 :
209 150 : pw_pool => poisson_env%pw_pools(poisson_env%pw_level)%pool
210 150 : dielectric => poisson_env%implicit_env%dielectric
211 150 : green => poisson_env%green_fft
212 150 : ps_implicit_env => poisson_env%implicit_env
213 :
214 150 : tol = poisson_env%parameters%ps_implicit_params%tol
215 150 : omega = poisson_env%parameters%ps_implicit_params%omega
216 150 : max_iter = poisson_env%parameters%ps_implicit_params%max_iter
217 150 : use_zero_initial_guess = poisson_env%parameters%ps_implicit_params%zero_initial_guess
218 150 : times_called = ps_implicit_env%times_called
219 :
220 : ! check if this is the first scf iteration
221 150 : IF (times_called == 0) CALL ps_implicit_initial_guess_create(ps_implicit_env, pw_pool)
222 :
223 150 : CALL pw_pool%create_pw(g)
224 150 : CALL pw_pool%create_pw(v_old)
225 150 : CALL pw_pool%create_pw(res_old)
226 150 : CALL pw_pool%create_pw(res_new)
227 150 : CALL pw_pool%create_pw(QAinvxres)
228 150 : CALL pw_pool%create_pw(PxQAinvxres)
229 :
230 150 : IF (use_zero_initial_guess) THEN
231 0 : CALL pw_zero(v_old)
232 : ELSE
233 150 : CALL pw_copy(ps_implicit_env%initial_guess, v_old)
234 : END IF
235 :
236 22241170 : g%array = fourpi*density%array/dielectric%eps%array
237 :
238 : ! res_old = g - \Delta(v_old) - P(v_old)
239 150 : CALL apply_poisson_operator_fft(pw_pool, green, dielectric, v_old, res_old)
240 150 : CALL pw_scale(res_old, -1.0_dp)
241 150 : CALL pw_axpy(g, res_old)
242 :
243 : ! evaluate \Delta^-1(res_old)
244 150 : CALL apply_inv_laplace_operator_fft(pw_pool, green, res_old, QAinvxres)
245 :
246 150 : iter = 1
247 1472 : DO
248 :
249 : ! v_new = v_old + \omega * QAinvxres_old
250 736 : CALL pw_scale(QAinvxres, omega)
251 736 : CALL pw_copy(QAinvxres, v_new)
252 736 : CALL pw_axpy(v_old, v_new)
253 :
254 : ! res_new = res_old - \omega * ( \Delta(QAinvxres_old) + P(QAinvxres_old) )
255 : ! = (1 - \omega) * res_old - \omega * PxQAinvxres
256 736 : CALL apply_P_operator(pw_pool, dielectric, QAinvxres, PxQAinvxres)
257 736 : CALL pw_copy(PxQAinvxres, res_new)
258 736 : CALL pw_scale(res_new, -1.0_dp)
259 736 : CALL pw_axpy(res_old, res_new, 1.0_dp - omega)
260 :
261 : ! compute the error
262 : CALL ps_implicit_compute_error_fft(pw_pool, green, res_new, v_old, v_new, QAinvxres, &
263 736 : pres_error, nabs_error)
264 : ! output
265 736 : CALL ps_implicit_output(iter, pres_error, nabs_error, outp_unit)
266 736 : IF (PRESENT(ehartree)) THEN
267 736 : CALL ps_implicit_compute_ehartree(density, v_new, ehartree)
268 736 : CALL ps_implicit_report_ehartree(ps_implicit_env, outp_unit, ehartree)
269 736 : ps_implicit_env%ehartree = ehartree
270 : ELSE
271 0 : IF (outp_unit > 0) WRITE (outp_unit, '(A1,/)')
272 : END IF
273 :
274 736 : iter = iter + 1
275 736 : reached_max_iter = iter > max_iter
276 736 : reached_tol = pres_error <= tol
277 736 : IF (pres_error > large_error) THEN
278 0 : CPABORT("Poisson solver did not converge.")
279 : END IF
280 736 : ps_implicit_env%times_called = ps_implicit_env%times_called + 1
281 736 : IF (reached_max_iter .OR. reached_tol) EXIT
282 :
283 : ! v_old = v_new, res_old = res_new
284 586 : CALL pw_copy(v_new, v_old)
285 586 : CALL pw_copy(res_new, res_old)
286 :
287 : END DO
288 150 : CALL ps_implicit_print_convergence_msg(iter, max_iter, outp_unit)
289 :
290 150 : IF ((times_called /= 0) .AND. (.NOT. use_zero_initial_guess)) THEN
291 138 : CALL pw_copy(v_new, ps_implicit_env%initial_guess)
292 : END IF
293 :
294 150 : IF (PRESENT(ehartree)) ehartree = ps_implicit_env%ehartree
295 : ! compute the extra contribution to the Hamiltonian due to the presence of dielectric
296 : BLOCK
297 : TYPE(pw_r3d_rs_type) :: v_eps
298 150 : v_eps%pw_grid => ps_implicit_env%v_eps%pw_grid
299 150 : v_eps%array => ps_implicit_env%v_eps%array
300 150 : CALL ps_implicit_compute_veps(pw_pool, dielectric, v_new, v_eps)
301 : END BLOCK
302 :
303 150 : CALL pw_pool%give_back_pw(g)
304 150 : CALL pw_pool%give_back_pw(v_old)
305 150 : CALL pw_pool%give_back_pw(res_old)
306 150 : CALL pw_pool%give_back_pw(res_new)
307 150 : CALL pw_pool%give_back_pw(QAinvxres)
308 150 : CALL pw_pool%give_back_pw(PxQAinvxres)
309 :
310 150 : CALL timestop(handle)
311 :
312 150 : END SUBROUTINE implicit_poisson_solver_periodic
313 :
314 : ! **************************************************************************************************
315 : !> \brief implicit Poisson solver: zero-average solution of the Poisson equation
316 : !> subject to homogeneous Neumann boundary conditions
317 : !> \param poisson_env poisson environment
318 : !> \param density electron density
319 : !> \param v_new electrostatic potential
320 : !> \param ehartree Hartree energy
321 : !> \par History
322 : !> 02.2015 created [Hossein Bani-Hashemian]
323 : !> 11.2015 revised [Hossein Bani-Hashemian]
324 : !> \author Mohammad Hossein Bani-Hashemian
325 : ! **************************************************************************************************
326 24 : SUBROUTINE implicit_poisson_solver_neumann(poisson_env, density, v_new, ehartree)
327 :
328 : TYPE(pw_poisson_type), INTENT(IN) :: poisson_env
329 : TYPE(pw_r3d_rs_type), INTENT(IN) :: density
330 : TYPE(pw_r3d_rs_type), INTENT(INOUT) :: v_new
331 : REAL(dp), INTENT(OUT), OPTIONAL :: ehartree
332 :
333 : CHARACTER(LEN=*), PARAMETER :: routineN = 'implicit_poisson_solver_neumann'
334 :
335 : INTEGER :: handle, iter, max_iter, &
336 : neumann_directions, outp_unit, &
337 : times_called
338 : LOGICAL :: reached_max_iter, reached_tol, &
339 : use_zero_initial_guess
340 : REAL(dp) :: nabs_error, omega, pres_error, tol, &
341 : vol_scfac
342 : TYPE(dct_type), POINTER :: dct_env
343 : TYPE(dielectric_type), POINTER :: dielectric
344 : TYPE(greens_fn_type), POINTER :: green
345 : TYPE(ps_implicit_type), POINTER :: ps_implicit_env
346 : TYPE(pw_pool_type), POINTER :: pw_pool, pw_pool_xpndd
347 : TYPE(pw_r3d_rs_type) :: density_xpndd, g, PxQAinvxres, &
348 : QAinvxres, res_new, res_old, &
349 : v_eps_xpndd, v_new_xpndd, v_old
350 :
351 24 : CALL timeset(routineN, handle)
352 :
353 24 : pw_pool => poisson_env%pw_pools(poisson_env%pw_level)%pool
354 24 : dielectric => poisson_env%implicit_env%dielectric
355 24 : green => poisson_env%green_fft
356 24 : ps_implicit_env => poisson_env%implicit_env
357 24 : dct_env => ps_implicit_env%dct_env
358 :
359 24 : tol = poisson_env%parameters%ps_implicit_params%tol
360 24 : omega = poisson_env%parameters%ps_implicit_params%omega
361 24 : max_iter = poisson_env%parameters%ps_implicit_params%max_iter
362 24 : use_zero_initial_guess = poisson_env%parameters%ps_implicit_params%zero_initial_guess
363 24 : neumann_directions = poisson_env%parameters%ps_implicit_params%neumann_directions
364 24 : times_called = ps_implicit_env%times_called
365 :
366 8 : SELECT CASE (neumann_directions)
367 : CASE (neumannXYZ)
368 8 : vol_scfac = 8.0_dp
369 : CASE (neumannXY, neumannXZ, neumannYZ)
370 0 : vol_scfac = 4.0_dp
371 : CASE (neumannX, neumannY, neumannZ)
372 24 : vol_scfac = 2.0_dp
373 : END SELECT
374 :
375 24 : CALL pw_pool_create(pw_pool_xpndd, pw_grid=poisson_env%dct_pw_grid)
376 :
377 : ! check if this is the first scf iteration
378 24 : IF (times_called == 0) CALL ps_implicit_initial_guess_create(ps_implicit_env, pw_pool_xpndd)
379 :
380 24 : CALL pw_pool_xpndd%create_pw(g)
381 24 : CALL pw_pool_xpndd%create_pw(v_old)
382 24 : CALL pw_pool_xpndd%create_pw(res_old)
383 24 : CALL pw_pool_xpndd%create_pw(res_new)
384 24 : CALL pw_pool_xpndd%create_pw(QAinvxres)
385 24 : CALL pw_pool_xpndd%create_pw(PxQAinvxres)
386 24 : CALL pw_pool_xpndd%create_pw(density_xpndd)
387 24 : CALL pw_pool_xpndd%create_pw(v_new_xpndd)
388 24 : CALL pw_pool_xpndd%create_pw(v_eps_xpndd)
389 :
390 24 : IF (use_zero_initial_guess) THEN
391 0 : CALL pw_zero(v_old)
392 : ELSE
393 24 : CALL pw_copy(ps_implicit_env%initial_guess, v_old)
394 : END IF
395 :
396 : CALL pw_expand(neumann_directions, &
397 : dct_env%recv_msgs_bnds, dct_env%dests_expand, dct_env%srcs_expand, &
398 24 : dct_env%flipg_stat, dct_env%bounds_shftd, density, density_xpndd)
399 : CALL pw_expand(neumann_directions, &
400 : dct_env%recv_msgs_bnds, dct_env%dests_expand, dct_env%srcs_expand, &
401 24 : dct_env%flipg_stat, dct_env%bounds_shftd, v_new, v_new_xpndd)
402 :
403 8535864 : g%array = fourpi*density_xpndd%array/dielectric%eps%array
404 :
405 : ! res_old = g - \Delta(v_old) - P(v_old)
406 24 : CALL apply_poisson_operator_dct(pw_pool_xpndd, green, dielectric, v_old, res_old)
407 24 : CALL pw_scale(res_old, -1.0_dp)
408 24 : CALL pw_axpy(g, res_old)
409 :
410 : ! evaluate \Delta^-1(res_old)
411 24 : CALL apply_inv_laplace_operator_dct(pw_pool_xpndd, green, res_old, QAinvxres)
412 :
413 24 : iter = 1
414 96 : DO
415 :
416 : ! v_new = v_old + \omega * QAinvxres_old
417 48 : CALL pw_scale(QAinvxres, omega)
418 48 : CALL pw_copy(QAinvxres, v_new_xpndd)
419 48 : CALL pw_axpy(v_old, v_new_xpndd)
420 :
421 : ! res_new = res_old - \omega * ( \Delta(QAinvxres_old) + P(QAinvxres_old) )
422 : ! = (1 - \omega) * res_old - \omega * PxQAinvxres
423 48 : CALL apply_P_operator(pw_pool_xpndd, dielectric, QAinvxres, PxQAinvxres)
424 48 : CALL pw_copy(PxQAinvxres, res_new)
425 48 : CALL pw_scale(res_new, -1.0_dp)
426 48 : CALL pw_axpy(res_old, res_new, 1.0_dp - omega)
427 :
428 : ! compute the error
429 : CALL ps_implicit_compute_error_dct(pw_pool_xpndd, green, res_new, v_old, v_new_xpndd, QAinvxres, &
430 48 : pres_error, nabs_error)
431 : ! output
432 48 : CALL ps_implicit_output(iter, pres_error, nabs_error, outp_unit)
433 48 : IF (PRESENT(ehartree)) THEN
434 48 : CALL ps_implicit_compute_ehartree(density_xpndd, v_new_xpndd, ehartree)
435 48 : CALL ps_implicit_report_ehartree(ps_implicit_env, outp_unit, ehartree/vol_scfac)
436 48 : ps_implicit_env%ehartree = ehartree/vol_scfac
437 : ELSE
438 0 : IF (outp_unit > 0) WRITE (outp_unit, '(A1,/)')
439 : END IF
440 :
441 48 : iter = iter + 1
442 48 : reached_max_iter = iter > max_iter
443 48 : reached_tol = pres_error <= tol
444 48 : IF (pres_error > large_error) THEN
445 0 : CPABORT("Poisson solver did not converge.")
446 : END IF
447 48 : ps_implicit_env%times_called = ps_implicit_env%times_called + 1
448 48 : IF (reached_max_iter .OR. reached_tol) EXIT
449 :
450 : ! v_old = v_new, res_old = res_new
451 24 : CALL pw_copy(v_new_xpndd, v_old)
452 24 : CALL pw_copy(res_new, res_old)
453 :
454 : END DO
455 24 : CALL ps_implicit_print_convergence_msg(iter, max_iter, outp_unit)
456 :
457 : CALL pw_shrink(neumann_directions, dct_env%dests_shrink, dct_env%srcs_shrink, &
458 24 : dct_env%bounds_local_shftd, v_new_xpndd, v_new)
459 :
460 24 : IF ((times_called /= 0) .AND. (.NOT. use_zero_initial_guess)) THEN
461 18 : CALL pw_copy(v_new_xpndd, ps_implicit_env%initial_guess)
462 : END IF
463 :
464 24 : IF (PRESENT(ehartree)) ehartree = ps_implicit_env%ehartree
465 : ! compute the extra contribution to the Hamiltonian due to the presence of dielectric
466 : ! veps has to be computed for the expanded data and then shrunk otherwise we loose accuracy
467 24 : CALL ps_implicit_compute_veps(pw_pool_xpndd, dielectric, v_new_xpndd, v_eps_xpndd)
468 : BLOCK
469 : TYPE(pw_r3d_rs_type) :: v_eps
470 24 : v_eps%pw_grid => ps_implicit_env%v_eps%pw_grid
471 24 : v_eps%array => ps_implicit_env%v_eps%array
472 : CALL pw_shrink(neumann_directions, dct_env%dests_shrink, dct_env%srcs_shrink, &
473 24 : dct_env%bounds_local_shftd, v_eps_xpndd, v_eps)
474 : END BLOCK
475 :
476 24 : CALL pw_pool_xpndd%give_back_pw(g)
477 24 : CALL pw_pool_xpndd%give_back_pw(v_old)
478 24 : CALL pw_pool_xpndd%give_back_pw(res_old)
479 24 : CALL pw_pool_xpndd%give_back_pw(res_new)
480 24 : CALL pw_pool_xpndd%give_back_pw(QAinvxres)
481 24 : CALL pw_pool_xpndd%give_back_pw(PxQAinvxres)
482 24 : CALL pw_pool_xpndd%give_back_pw(density_xpndd)
483 24 : CALL pw_pool_xpndd%give_back_pw(v_new_xpndd)
484 24 : CALL pw_pool_xpndd%give_back_pw(v_eps_xpndd)
485 24 : CALL pw_pool_release(pw_pool_xpndd)
486 :
487 24 : CALL timestop(handle)
488 :
489 24 : END SUBROUTINE implicit_poisson_solver_neumann
490 :
491 : ! **************************************************************************************************
492 : !> \brief implicit Poisson solver for mixed-periodic boundary conditions (periodic + Dirichlet)
493 : !> \param poisson_env poisson environment
494 : !> \param density electron density
495 : !> \param v_new electrostatic potential
496 : !> \param electric_enthalpy electric enthalpy
497 : !> \par History
498 : !> 07.2014 created [Hossein Bani-Hashemian]
499 : !> \author Mohammad Hossein Bani-Hashemian
500 : ! **************************************************************************************************
501 192 : SUBROUTINE implicit_poisson_solver_mixed_periodic(poisson_env, density, v_new, electric_enthalpy)
502 :
503 : TYPE(pw_poisson_type), INTENT(IN) :: poisson_env
504 : TYPE(pw_r3d_rs_type), INTENT(IN) :: density
505 : TYPE(pw_r3d_rs_type), INTENT(INOUT) :: v_new
506 : REAL(dp), INTENT(OUT), OPTIONAL :: electric_enthalpy
507 :
508 : CHARACTER(LEN=*), PARAMETER :: routineN = 'implicit_poisson_solver_mixed_periodic'
509 :
510 : INTEGER :: data_size, handle, iter, j, lb1, lb2, lb3, max_iter, n_contacts, n_tiles_tot, ng, &
511 : ngpts_local, nt, nt_tot, outp_unit, times_called, ub1, ub2, ub3
512 : INTEGER(KIND=int_8) :: ngpts
513 : INTEGER, DIMENSION(2, 3) :: bounds_local
514 : INTEGER, DIMENSION(3) :: npts_local
515 : LOGICAL :: reached_max_iter, reached_tol, &
516 : use_zero_initial_guess
517 : REAL(dp) :: Axvbar_avg, ehartree, eta, g_avg, &
518 : gminusAxvbar_avg, nabs_error, omega, &
519 : pres_error, tol
520 192 : REAL(dp), ALLOCATABLE, DIMENSION(:) :: Btxlambda_new, Btxlambda_old, Bxv_bar, Bxv_new, &
521 192 : lambda0, lambda_new, lambda_newNeta, lambda_old, QSxlambda, v_bar1D, v_D, v_new1D, w
522 192 : REAL(dp), ALLOCATABLE, DIMENSION(:, :) :: B, Bt, QS, Rinv
523 192 : REAL(dp), ALLOCATABLE, DIMENSION(:, :, :) :: Btxlambda_new3D, Btxlambda_old3D
524 : TYPE(dielectric_type), POINTER :: dielectric
525 : TYPE(greens_fn_type), POINTER :: green
526 : TYPE(ps_implicit_type), POINTER :: ps_implicit_env
527 : TYPE(pw_grid_type), POINTER :: pw_grid
528 : TYPE(pw_pool_type), POINTER :: pw_pool
529 : TYPE(pw_r3d_rs_type) :: Axvbar, g, PxQAinvxres, QAinvxres, &
530 : res_new, res_old, v_old
531 :
532 192 : CALL timeset(routineN, handle)
533 :
534 192 : pw_pool => poisson_env%pw_pools(poisson_env%pw_level)%pool
535 192 : pw_grid => pw_pool%pw_grid
536 192 : dielectric => poisson_env%implicit_env%dielectric
537 192 : green => poisson_env%green_fft
538 192 : ps_implicit_env => poisson_env%implicit_env
539 :
540 192 : ngpts_local = pw_grid%ngpts_local
541 192 : ngpts = pw_grid%ngpts
542 768 : npts_local = pw_grid%npts_local
543 1920 : bounds_local = pw_grid%bounds_local
544 192 : tol = poisson_env%parameters%ps_implicit_params%tol
545 192 : omega = poisson_env%parameters%ps_implicit_params%omega
546 192 : max_iter = poisson_env%parameters%ps_implicit_params%max_iter
547 192 : use_zero_initial_guess = poisson_env%parameters%ps_implicit_params%zero_initial_guess
548 192 : times_called = ps_implicit_env%times_called
549 :
550 192 : n_contacts = SIZE(ps_implicit_env%contacts)
551 192 : n_tiles_tot = 0
552 912 : DO j = 1, n_contacts
553 912 : n_tiles_tot = n_tiles_tot + ps_implicit_env%contacts(j)%dirichlet_bc%n_tiles
554 : END DO
555 :
556 192 : IF (pw_grid%para%blocked) THEN
557 0 : data_size = PRODUCT(npts_local)
558 192 : ELSE IF (pw_grid%para%ray_distribution) THEN
559 192 : data_size = ngpts_local
560 : ELSE ! parallel run with np = 1
561 0 : data_size = PRODUCT(npts_local)
562 : END IF
563 :
564 : ! check if this is the first scf iteration
565 192 : IF (times_called == 0) CALL ps_implicit_initial_guess_create(ps_implicit_env, pw_pool)
566 :
567 768 : ALLOCATE (B(n_tiles_tot, data_size))
568 576 : ALLOCATE (Bt(data_size, n_tiles_tot))
569 768 : ALLOCATE (QS(n_tiles_tot, n_tiles_tot))
570 768 : ALLOCATE (Rinv(n_tiles_tot + 1, n_tiles_tot + 1))
571 :
572 180559884 : B(:, :) = ps_implicit_env%B
573 154108352 : Bt(:, :) = ps_implicit_env%Bt
574 28576 : QS(:, :) = ps_implicit_env%QS
575 31856 : Rinv(:, :) = ps_implicit_env%Rinv
576 : CALL get_voltage(poisson_env%parameters%dbc_params%time, ps_implicit_env%v_D, ps_implicit_env%osc_frac, &
577 192 : ps_implicit_env%frequency, ps_implicit_env%phase, v_D)
578 :
579 192 : lb1 = bounds_local(1, 1); ub1 = bounds_local(2, 1)
580 192 : lb2 = bounds_local(1, 2); ub2 = bounds_local(2, 2)
581 192 : lb3 = bounds_local(1, 3); ub3 = bounds_local(2, 3)
582 :
583 960 : ALLOCATE (lambda0(n_tiles_tot), lambda_old(n_tiles_tot), lambda_new(n_tiles_tot))
584 768 : ALLOCATE (Btxlambda_old(data_size), Btxlambda_new(data_size))
585 1536 : ALLOCATE (Btxlambda_old3D(lb1:ub1, lb2:ub2, lb3:ub3), Btxlambda_new3D(lb1:ub1, lb2:ub2, lb3:ub3))
586 384 : ALLOCATE (QSxlambda(n_tiles_tot))
587 576 : ALLOCATE (w(n_tiles_tot + 1))
588 384 : ALLOCATE (lambda_newNeta(n_tiles_tot + 1))
589 384 : ALLOCATE (v_bar1D(data_size))
590 384 : ALLOCATE (Bxv_bar(n_tiles_tot))
591 :
592 384 : ALLOCATE (v_new1D(data_size))
593 384 : ALLOCATE (Bxv_new(n_tiles_tot))
594 :
595 192 : CALL pw_pool%create_pw(g)
596 192 : CALL pw_pool%create_pw(v_old)
597 192 : CALL pw_pool%create_pw(res_old)
598 192 : CALL pw_pool%create_pw(res_new)
599 192 : CALL pw_pool%create_pw(QAinvxres)
600 192 : CALL pw_pool%create_pw(PxQAinvxres)
601 192 : CALL pw_pool%create_pw(Axvbar)
602 :
603 192 : IF (use_zero_initial_guess) THEN
604 0 : CALL pw_zero(v_old)
605 0 : lambda0 = 0.0_dp
606 : ELSE
607 192 : CALL pw_copy(ps_implicit_env%initial_guess, v_old)
608 1640 : lambda0(:) = ps_implicit_env%initial_lambda
609 : END IF
610 :
611 27267060 : g%array = fourpi*density%array/dielectric%eps%array
612 192 : g_avg = accurate_sum(g%array)/ngpts
613 :
614 1640 : lambda_old(:) = lambda0
615 :
616 : ! res_old = g - \Delta(v_old) - P(v_old) - B^t * \lambda_old
617 192 : CALL apply_poisson_operator_fft(pw_pool, green, dielectric, v_old, res_old)
618 192 : CALL pw_scale(res_old, -1.0_dp)
619 192 : CALL pw_axpy(g, res_old)
620 192 : IF (data_size /= 0) THEN
621 192 : CALL DGEMV('N', data_size, n_tiles_tot, 1.0_dp, Bt, data_size, lambda_old, 1, 0.0_dp, Btxlambda_old, 1)
622 : END IF
623 192 : CALL convert_1dto3d(ps_implicit_env%idx_1dto3d, Btxlambda_old, Btxlambda_old3D)
624 27267060 : res_old%array = res_old%array - Btxlambda_old3D
625 :
626 : ! evaluate \Delta^-1(res_old)
627 192 : CALL apply_inv_laplace_operator_fft(pw_pool, green, res_old, QAinvxres)
628 :
629 192 : iter = 1
630 3036 : DO
631 :
632 : ! v_new (v_bar) = v_old + \omega * QAinvxres_old
633 1518 : CALL pw_scale(QAinvxres, omega)
634 1518 : CALL pw_copy(QAinvxres, v_new)
635 1518 : CALL pw_axpy(v_old, v_new)
636 :
637 : ! evaluate 1^t * (g - \Delta(\bar{v}) - P(\bar{v}))
638 : ! = 1^t * (g - P(\bar{v}))
639 1518 : CALL apply_P_operator(pw_pool, dielectric, v_new, Axvbar)
640 1518 : Axvbar_avg = accurate_sum(Axvbar%array)/ngpts
641 1518 : gminusAxvbar_avg = g_avg - Axvbar_avg
642 1518 : CALL pw_grid%para%group%sum(gminusAxvbar_avg)
643 :
644 : ! evaluate Q_S * \lambda + v_D - B * \bar{v}
645 1518 : CALL DGEMV('N', n_tiles_tot, n_tiles_tot, 1.0_dp, QS, n_tiles_tot, lambda_old, 1, 0.0_dp, QSxlambda, 1)
646 358699548 : v_bar1D(ps_implicit_env%idx_1dto3d) = RESHAPE(v_new%array, [data_size])
647 1518 : CALL DGEMV('N', n_tiles_tot, data_size, 1.0_dp, B, n_tiles_tot, v_bar1D, 1, 0.0_dp, Bxv_bar, 1)
648 1518 : CALL pw_grid%para%group%sum(Bxv_bar)
649 : ! solve R [\lambda; \eta] = [Q_S * \lambda + v_D - B * \bar{v}; 1^t * (g - \Delta(\bar{v}) - P(\bar{v}))]
650 1518 : w = 0.0_dp
651 23284 : w(:) = [QSxlambda + v_D - Bxv_bar, gminusAxvbar_avg]
652 1518 : CALL DGEMV('N', n_tiles_tot + 1, n_tiles_tot + 1, 1.0_dp, Rinv, n_tiles_tot + 1, w, 1, 0.0_dp, lambda_newNeta, 1)
653 11642 : lambda_new(:) = lambda_newNeta(1:n_tiles_tot)
654 1518 : eta = lambda_newNeta(n_tiles_tot + 1)
655 :
656 : ! v_new = v_bar + 1 * \eta
657 185199954 : v_new%array = v_new%array + eta/ngpts
658 :
659 : ! evaluate B^t * \lambda_new
660 1518 : IF (data_size /= 0) THEN
661 1518 : CALL DGEMV('N', data_size, n_tiles_tot, 1.0_dp, Bt, data_size, lambda_new, 1, 0.0_dp, Btxlambda_new, 1)
662 : END IF
663 1518 : CALL convert_1dto3d(ps_implicit_env%idx_1dto3d, Btxlambda_new, Btxlambda_new3D)
664 :
665 : ! res_new = res_old - \omega * ( \Delta(QAinvxres_old) + P(QAinvxres_old) ) - B^t * ( \lambda_new - \lambda_old )
666 : ! = (1 - \omega) * res_old - \omega * P(QAinvxres_old) - B^t * ( \lambda_new - \lambda_old )
667 1518 : CALL pw_zero(res_new)
668 1518 : CALL apply_P_operator(pw_pool, dielectric, QAinvxres, PxQAinvxres)
669 1518 : CALL pw_axpy(PxQAinvxres, res_new, -1.0_dp)
670 1518 : CALL pw_axpy(res_old, res_new, 1.0_dp - omega)
671 185199954 : res_new%array = res_new%array + Btxlambda_old3D - Btxlambda_new3D
672 :
673 : ! compute the error
674 : CALL ps_implicit_compute_error_fft(pw_pool, green, res_new, v_old, v_new, QAinvxres, &
675 1518 : pres_error, nabs_error)
676 : ! output
677 1518 : CALL ps_implicit_output(iter, pres_error, nabs_error, outp_unit)
678 1518 : IF (PRESENT(electric_enthalpy)) THEN
679 1518 : CALL ps_implicit_compute_ehartree(dielectric, density, Btxlambda_new3D, v_new, ehartree, electric_enthalpy)
680 1518 : CALL ps_implicit_report_ehartree(ps_implicit_env, outp_unit, ehartree)
681 1518 : ps_implicit_env%ehartree = ehartree
682 1518 : ps_implicit_env%electric_enthalpy = electric_enthalpy
683 : ELSE
684 0 : IF (outp_unit > 0) WRITE (outp_unit, '(A1,/)')
685 : END IF
686 :
687 : ! verbose output
688 1518 : IF (poisson_env%parameters%dbc_params%verbose_output) THEN
689 0 : v_new1D(ps_implicit_env%idx_1dto3d) = RESHAPE(v_new%array, [data_size])
690 0 : CALL DGEMV('N', n_tiles_tot, data_size, 1.0_dp, B, n_tiles_tot, v_new1D, 1, 0.0_dp, Bxv_new, 1)
691 0 : CALL pw_grid%para%group%sum(Bxv_new)
692 0 : IF (outp_unit > 0) THEN
693 0 : WRITE (outp_unit, '(T3,A,A)') "======== verbose ", REPEAT('=', 61)
694 0 : WRITE (outp_unit, '(T20,A)') "Drgn tile vhartree lambda "
695 0 : WRITE (outp_unit, '(T19,A)') REPEAT('-', 46)
696 0 : nt_tot = 1
697 0 : DO ng = 1, n_contacts
698 0 : DO nt = 1, ps_implicit_env%contacts(ng)%dirichlet_bc%n_tiles
699 0 : WRITE (outp_unit, '(T17,I6,5X,I6,3X,E13.4,E13.4)') ng, nt, Bxv_new(nt_tot), lambda_new(nt_tot)
700 0 : nt_tot = nt_tot + 1
701 : END DO
702 : END DO
703 0 : WRITE (outp_unit, '(T3,A)') REPEAT('=', 78)
704 : END IF
705 : END IF
706 :
707 : ! check the convergence
708 1518 : iter = iter + 1
709 1518 : reached_max_iter = iter > max_iter
710 1518 : reached_tol = pres_error <= tol
711 1518 : ps_implicit_env%times_called = ps_implicit_env%times_called + 1
712 1518 : IF (pres_error > large_error) THEN
713 0 : CPABORT("Poisson solver did not converge.")
714 : END IF
715 1518 : IF (reached_max_iter .OR. reached_tol) EXIT
716 :
717 : ! update
718 1326 : CALL pw_copy(v_new, v_old)
719 10002 : lambda_old(:) = lambda_new
720 1326 : CALL pw_copy(res_new, res_old)
721 157933086 : Btxlambda_old3D(:, :, :) = Btxlambda_new3D
722 :
723 : END DO
724 192 : CALL ps_implicit_print_convergence_msg(iter, max_iter, outp_unit)
725 :
726 192 : IF ((times_called /= 0) .AND. (.NOT. use_zero_initial_guess)) THEN
727 170 : CALL pw_copy(v_new, ps_implicit_env%initial_guess)
728 1444 : ps_implicit_env%initial_lambda(:) = lambda_new
729 : END IF
730 :
731 27267060 : ps_implicit_env%cstr_charge%array = Btxlambda_new3D
732 192 : IF (PRESENT(electric_enthalpy)) electric_enthalpy = ps_implicit_env%electric_enthalpy
733 : ! compute the extra contribution to the Hamiltonian due to the presence of dielectric
734 : BLOCK
735 : TYPE(pw_r3d_rs_type) :: tmp
736 192 : tmp%pw_grid => ps_implicit_env%v_eps%pw_grid
737 192 : tmp%array => ps_implicit_env%v_eps%array
738 192 : CALL ps_implicit_compute_veps(pw_pool, dielectric, v_new, tmp)
739 : END BLOCK
740 :
741 192 : CALL pw_pool%give_back_pw(g)
742 192 : CALL pw_pool%give_back_pw(v_old)
743 192 : CALL pw_pool%give_back_pw(res_old)
744 192 : CALL pw_pool%give_back_pw(res_new)
745 192 : CALL pw_pool%give_back_pw(QAinvxres)
746 192 : CALL pw_pool%give_back_pw(PxQAinvxres)
747 192 : CALL pw_pool%give_back_pw(Axvbar)
748 :
749 192 : CALL timestop(handle)
750 :
751 384 : END SUBROUTINE implicit_poisson_solver_mixed_periodic
752 :
753 : ! **************************************************************************************************
754 : !> \brief implicit Poisson solver for mixed boundary conditions (Neumann + Dirichlet)
755 : !> \param poisson_env poisson environment
756 : !> \param density electron density
757 : !> \param v_new electrostatic potential
758 : !> \param electric_enthalpy electric enthalpy
759 : !> \par History
760 : !> 10.2014 created [Hossein Bani-Hashemian]
761 : !> 11.2015 revised [Hossein Bani-Hashemian]
762 : !> \author Mohammad Hossein Bani-Hashemian
763 : ! **************************************************************************************************
764 132 : SUBROUTINE implicit_poisson_solver_mixed(poisson_env, density, v_new, electric_enthalpy)
765 :
766 : TYPE(pw_poisson_type), INTENT(IN) :: poisson_env
767 : TYPE(pw_r3d_rs_type), INTENT(IN) :: density
768 : TYPE(pw_r3d_rs_type), INTENT(INOUT) :: v_new
769 : REAL(dp), INTENT(OUT), OPTIONAL :: electric_enthalpy
770 :
771 : CHARACTER(LEN=*), PARAMETER :: routineN = 'implicit_poisson_solver_mixed'
772 :
773 : INTEGER :: data_size, handle, iter, j, lb1, lb2, lb3, max_iter, n_contacts, n_tiles_tot, &
774 : neumann_directions, ng, ngpts_local, nt, nt_tot, outp_unit, times_called, ub1, ub2, ub3
775 : INTEGER(KIND=int_8) :: ngpts
776 : INTEGER, DIMENSION(2, 3) :: bounds_local
777 : INTEGER, DIMENSION(3) :: npts_local
778 : LOGICAL :: reached_max_iter, reached_tol, &
779 : use_zero_initial_guess
780 : REAL(dp) :: Axvbar_avg, ehartree, eta, g_avg, &
781 : gminusAxvbar_avg, nabs_error, omega, &
782 : pres_error, tol, vol_scfac
783 132 : REAL(dp), ALLOCATABLE, DIMENSION(:) :: Btxlambda_new, Btxlambda_old, Bxv_bar, Bxv_new, &
784 132 : lambda0, lambda_new, lambda_newNeta, lambda_old, QSxlambda, v_bar1D, v_D, v_new1D, w
785 132 : REAL(dp), ALLOCATABLE, DIMENSION(:, :) :: B, Bt, QS, Rinv
786 132 : REAL(dp), ALLOCATABLE, DIMENSION(:, :, :) :: Btxlambda_new3D, Btxlambda_old3D
787 : TYPE(dct_type), POINTER :: dct_env
788 : TYPE(dielectric_type), POINTER :: dielectric
789 : TYPE(greens_fn_type), POINTER :: green
790 : TYPE(ps_implicit_type), POINTER :: ps_implicit_env
791 : TYPE(pw_grid_type), POINTER :: dct_pw_grid, pw_grid
792 : TYPE(pw_pool_type), POINTER :: pw_pool, pw_pool_xpndd
793 : TYPE(pw_r3d_rs_type) :: Axvbar, density_xpndd, g, PxQAinvxres, &
794 : QAinvxres, res_new, res_old, &
795 : v_eps_xpndd, v_new_xpndd, v_old
796 :
797 132 : CALL timeset(routineN, handle)
798 :
799 132 : pw_pool => poisson_env%pw_pools(poisson_env%pw_level)%pool
800 132 : pw_grid => pw_pool%pw_grid
801 132 : dielectric => poisson_env%implicit_env%dielectric
802 132 : green => poisson_env%green_fft
803 132 : ps_implicit_env => poisson_env%implicit_env
804 132 : dct_env => ps_implicit_env%dct_env
805 :
806 132 : dct_pw_grid => poisson_env%dct_pw_grid
807 132 : ngpts_local = dct_pw_grid%ngpts_local
808 132 : ngpts = dct_pw_grid%ngpts
809 528 : npts_local = dct_pw_grid%npts_local
810 1320 : bounds_local = dct_pw_grid%bounds_local
811 132 : tol = poisson_env%parameters%ps_implicit_params%tol
812 132 : omega = poisson_env%parameters%ps_implicit_params%omega
813 132 : max_iter = poisson_env%parameters%ps_implicit_params%max_iter
814 132 : use_zero_initial_guess = poisson_env%parameters%ps_implicit_params%zero_initial_guess
815 132 : neumann_directions = poisson_env%parameters%ps_implicit_params%neumann_directions
816 132 : times_called = ps_implicit_env%times_called
817 :
818 124 : SELECT CASE (neumann_directions)
819 : CASE (neumannXYZ)
820 124 : vol_scfac = 8.0_dp
821 : CASE (neumannXY, neumannXZ, neumannYZ)
822 8 : vol_scfac = 4.0_dp
823 : CASE (neumannX, neumannY, neumannZ)
824 132 : vol_scfac = 2.0_dp
825 : END SELECT
826 :
827 132 : n_contacts = SIZE(ps_implicit_env%contacts)
828 132 : n_tiles_tot = 0
829 432 : DO j = 1, n_contacts
830 432 : n_tiles_tot = n_tiles_tot + ps_implicit_env%contacts(j)%dirichlet_bc%n_tiles
831 : END DO
832 :
833 132 : IF (dct_pw_grid%para%blocked) THEN
834 0 : data_size = PRODUCT(npts_local)
835 132 : ELSE IF (dct_pw_grid%para%ray_distribution) THEN
836 132 : data_size = ngpts_local
837 : ELSE ! parallel run with np = 1
838 0 : data_size = PRODUCT(npts_local)
839 : END IF
840 :
841 132 : CALL pw_pool_create(pw_pool_xpndd, pw_grid=dct_pw_grid)
842 :
843 : ! check if this is the first scf iteration
844 132 : IF (times_called == 0) CALL ps_implicit_initial_guess_create(ps_implicit_env, pw_pool_xpndd)
845 :
846 528 : ALLOCATE (B(n_tiles_tot, data_size))
847 396 : ALLOCATE (Bt(data_size, n_tiles_tot))
848 528 : ALLOCATE (QS(n_tiles_tot, n_tiles_tot))
849 528 : ALLOCATE (Rinv(n_tiles_tot + 1, n_tiles_tot + 1))
850 :
851 254331924 : B(:, :) = ps_implicit_env%B
852 197516632 : Bt(:, :) = ps_implicit_env%Bt
853 2652 : QS(:, :) = ps_implicit_env%QS
854 3884 : Rinv(:, :) = ps_implicit_env%Rinv
855 : CALL get_voltage(poisson_env%parameters%dbc_params%time, ps_implicit_env%v_D, ps_implicit_env%osc_frac, &
856 132 : ps_implicit_env%frequency, ps_implicit_env%phase, v_D)
857 :
858 132 : lb1 = bounds_local(1, 1); ub1 = bounds_local(2, 1)
859 132 : lb2 = bounds_local(1, 2); ub2 = bounds_local(2, 2)
860 132 : lb3 = bounds_local(1, 3); ub3 = bounds_local(2, 3)
861 :
862 660 : ALLOCATE (lambda0(n_tiles_tot), lambda_old(n_tiles_tot), lambda_new(n_tiles_tot))
863 528 : ALLOCATE (Btxlambda_old(data_size), Btxlambda_new(data_size))
864 1056 : ALLOCATE (Btxlambda_old3D(lb1:ub1, lb2:ub2, lb3:ub3), Btxlambda_new3D(lb1:ub1, lb2:ub2, lb3:ub3))
865 264 : ALLOCATE (QSxlambda(n_tiles_tot))
866 396 : ALLOCATE (w(n_tiles_tot + 1))
867 264 : ALLOCATE (lambda_newNeta(n_tiles_tot + 1))
868 264 : ALLOCATE (v_bar1D(data_size))
869 264 : ALLOCATE (Bxv_bar(n_tiles_tot))
870 :
871 264 : ALLOCATE (v_new1D(data_size))
872 264 : ALLOCATE (Bxv_new(n_tiles_tot))
873 :
874 132 : CALL pw_pool_xpndd%create_pw(g)
875 132 : CALL pw_pool_xpndd%create_pw(v_old)
876 132 : CALL pw_pool_xpndd%create_pw(res_old)
877 132 : CALL pw_pool_xpndd%create_pw(res_new)
878 132 : CALL pw_pool_xpndd%create_pw(QAinvxres)
879 132 : CALL pw_pool_xpndd%create_pw(PxQAinvxres)
880 132 : CALL pw_pool_xpndd%create_pw(Axvbar)
881 132 : CALL pw_pool_xpndd%create_pw(density_xpndd)
882 132 : CALL pw_pool_xpndd%create_pw(v_new_xpndd)
883 132 : CALL pw_pool_xpndd%create_pw(v_eps_xpndd)
884 :
885 132 : IF (use_zero_initial_guess) THEN
886 0 : CALL pw_zero(v_old)
887 0 : lambda0 = 0.0_dp
888 : ELSE
889 132 : CALL pw_copy(ps_implicit_env%initial_guess, v_old)
890 616 : lambda0(:) = ps_implicit_env%initial_lambda
891 : END IF
892 :
893 : CALL pw_expand(neumann_directions, &
894 : dct_env%recv_msgs_bnds, dct_env%dests_expand, dct_env%srcs_expand, &
895 132 : dct_env%flipg_stat, dct_env%bounds_shftd, density, density_xpndd)
896 : CALL pw_expand(neumann_directions, &
897 : dct_env%recv_msgs_bnds, dct_env%dests_expand, dct_env%srcs_expand, &
898 132 : dct_env%flipg_stat, dct_env%bounds_shftd, v_new, v_new_xpndd)
899 :
900 57935060 : g%array = fourpi*density_xpndd%array/dielectric%eps%array
901 132 : g_avg = accurate_sum(g%array)/ngpts
902 :
903 616 : lambda_old(:) = lambda0
904 :
905 : ! res_old = g - \Delta(v_old) - P(v_old) - B^t * \lambda_old
906 132 : CALL apply_poisson_operator_dct(pw_pool_xpndd, green, dielectric, v_old, res_old)
907 132 : CALL pw_scale(res_old, -1.0_dp)
908 132 : CALL pw_axpy(g, res_old)
909 132 : IF (data_size /= 0) THEN
910 132 : CALL DGEMV('N', data_size, n_tiles_tot, 1.0_dp, Bt, data_size, lambda_old, 1, 0.0_dp, Btxlambda_old, 1)
911 : END IF
912 132 : CALL convert_1dto3d(ps_implicit_env%idx_1dto3d, Btxlambda_old, Btxlambda_old3D)
913 57935060 : res_old%array = res_old%array - Btxlambda_old3D
914 :
915 : ! evaluate \Delta^-1(res_old)
916 132 : CALL apply_inv_laplace_operator_dct(pw_pool_xpndd, green, res_old, QAinvxres)
917 :
918 132 : iter = 1
919 440 : DO
920 :
921 : ! v_new (v_bar) = v_old + \omega * QAinvxres_old
922 220 : CALL pw_scale(QAinvxres, omega)
923 220 : CALL pw_copy(QAinvxres, v_new_xpndd)
924 220 : CALL pw_axpy(v_old, v_new_xpndd)
925 :
926 : ! evaluate 1^t * (g - \Delta(\bar{v}) - P(\bar{v}))
927 : ! = 1^t * (g - P(\bar{v}))
928 220 : CALL apply_P_operator(pw_pool_xpndd, dielectric, v_new_xpndd, Axvbar)
929 220 : Axvbar_avg = accurate_sum(Axvbar%array)/ngpts
930 220 : gminusAxvbar_avg = g_avg - Axvbar_avg
931 220 : CALL dct_pw_grid%para%group%sum(gminusAxvbar_avg)
932 :
933 : ! evaluate Q_S * \lambda + v_D - B * \bar{v}
934 220 : CALL DGEMV('N', n_tiles_tot, n_tiles_tot, 1.0_dp, QS, n_tiles_tot, lambda_old, 1, 0.0_dp, QSxlambda, 1)
935 201398840 : v_bar1D(ps_implicit_env%idx_1dto3d) = RESHAPE(v_new_xpndd%array, [data_size])
936 220 : CALL DGEMV('N', n_tiles_tot, data_size, 1.0_dp, B, n_tiles_tot, v_bar1D, 1, 0.0_dp, Bxv_bar, 1)
937 220 : CALL dct_pw_grid%para%group%sum(Bxv_bar)
938 : ! solve R [\lambda; \eta] = [Q_S * \lambda + v_D - B * \bar{v}; 1^t * (g - \Delta(\bar{v}) - P(\bar{v}))]
939 220 : w = 0.0_dp
940 1984 : w(:) = [QSxlambda + v_D - Bxv_bar, gminusAxvbar_avg]
941 220 : CALL DGEMV('N', n_tiles_tot + 1, n_tiles_tot + 1, 1.0_dp, Rinv, n_tiles_tot + 1, w, 1, 0.0_dp, lambda_newNeta, 1)
942 992 : lambda_new(:) = lambda_newNeta(1:n_tiles_tot)
943 220 : eta = lambda_newNeta(n_tiles_tot + 1)
944 :
945 : ! v_new = v_bar + 1 * \eta
946 102694940 : v_new_xpndd%array = v_new_xpndd%array + eta/ngpts
947 :
948 : ! evaluate B^t * \lambda_new
949 220 : IF (data_size /= 0) THEN
950 220 : CALL DGEMV('N', data_size, n_tiles_tot, 1.0_dp, Bt, data_size, lambda_new, 1, 0.0_dp, Btxlambda_new, 1)
951 : END IF
952 220 : CALL convert_1dto3d(ps_implicit_env%idx_1dto3d, Btxlambda_new, Btxlambda_new3D)
953 :
954 : ! res_new = res_old - \omega * ( \Delta(QAinvxres_old) + P(QAinvxres_old) ) - B^t * ( \lambda_new - \lambda_old )
955 : ! = (1 - \omega) * res_old - \omega * P(QAinvxres_old) - B^t * ( \lambda_new - \lambda_old )
956 220 : CALL pw_zero(res_new)
957 220 : CALL apply_P_operator(pw_pool_xpndd, dielectric, QAinvxres, PxQAinvxres)
958 220 : CALL pw_axpy(PxQAinvxres, res_new, -1.0_dp)
959 220 : CALL pw_axpy(res_old, res_new, 1.0_dp - omega)
960 102694940 : res_new%array = res_new%array - Btxlambda_new3D + Btxlambda_old3D
961 :
962 : ! compute the error
963 : CALL ps_implicit_compute_error_dct(pw_pool_xpndd, green, res_new, v_old, v_new_xpndd, QAinvxres, &
964 220 : pres_error, nabs_error)
965 : ! output
966 220 : CALL ps_implicit_output(iter, pres_error, nabs_error, outp_unit)
967 220 : IF (PRESENT(electric_enthalpy)) THEN
968 : CALL ps_implicit_compute_ehartree(dielectric, density_xpndd, Btxlambda_new3D, v_new_xpndd, &
969 220 : ehartree, electric_enthalpy)
970 220 : CALL ps_implicit_report_ehartree(ps_implicit_env, outp_unit, ehartree/vol_scfac)
971 220 : ps_implicit_env%ehartree = ehartree/vol_scfac
972 220 : ps_implicit_env%electric_enthalpy = electric_enthalpy/vol_scfac
973 : ELSE
974 0 : IF (outp_unit > 0) WRITE (outp_unit, '(A1,/)')
975 : END IF
976 :
977 : ! verbose output
978 220 : IF (poisson_env%parameters%dbc_params%verbose_output) THEN
979 0 : v_new1D(ps_implicit_env%idx_1dto3d) = RESHAPE(v_new_xpndd%array, [data_size])
980 0 : CALL DGEMV('N', n_tiles_tot, data_size, 1.0_dp, B, n_tiles_tot, v_new1D, 1, 0.0_dp, Bxv_new, 1)
981 0 : CALL pw_grid%para%group%sum(Bxv_new)
982 0 : IF (outp_unit > 0) THEN
983 0 : WRITE (outp_unit, '(T3,A)') "======== verbose "//REPEAT('=', 61)
984 0 : WRITE (outp_unit, '(T20,A)') "Drgn tile vhartree lambda "
985 0 : WRITE (outp_unit, '(T19,A)') REPEAT('-', 46)
986 0 : nt_tot = 1
987 0 : DO ng = 1, n_contacts
988 0 : DO nt = 1, ps_implicit_env%contacts(ng)%dirichlet_bc%n_tiles
989 0 : WRITE (outp_unit, '(T17,I6,5X,I6,3X,E13.4,E13.4)') ng, nt, Bxv_new(nt_tot), lambda_new(nt_tot)
990 0 : nt_tot = nt_tot + 1
991 : END DO
992 : END DO
993 0 : WRITE (outp_unit, '(T3,A)') REPEAT('=', 78)
994 : END IF
995 : END IF
996 :
997 : ! check the convergence
998 220 : iter = iter + 1
999 220 : reached_max_iter = iter > max_iter
1000 220 : reached_tol = pres_error <= tol
1001 220 : ps_implicit_env%times_called = ps_implicit_env%times_called + 1
1002 220 : IF (pres_error > large_error) THEN
1003 0 : CPABORT("Poisson solver did not converge.")
1004 : END IF
1005 220 : IF (reached_max_iter .OR. reached_tol) EXIT
1006 :
1007 : ! update
1008 88 : CALL pw_copy(v_new_xpndd, v_old)
1009 376 : lambda_old(:) = lambda_new
1010 88 : CALL pw_copy(res_new, res_old)
1011 44760012 : Btxlambda_old3D(:, :, :) = Btxlambda_new3D
1012 :
1013 : END DO
1014 132 : CALL ps_implicit_print_convergence_msg(iter, max_iter, outp_unit)
1015 :
1016 : CALL pw_shrink(neumann_directions, dct_env%dests_shrink, dct_env%srcs_shrink, &
1017 132 : dct_env%bounds_local_shftd, v_new_xpndd, v_new)
1018 :
1019 132 : IF ((times_called /= 0) .AND. (.NOT. use_zero_initial_guess)) THEN
1020 116 : CALL pw_copy(v_new_xpndd, ps_implicit_env%initial_guess)
1021 550 : ps_implicit_env%initial_lambda(:) = lambda_new
1022 : END IF
1023 :
1024 57935060 : ps_implicit_env%cstr_charge%array = Btxlambda_new3D
1025 132 : IF (PRESENT(electric_enthalpy)) electric_enthalpy = ps_implicit_env%electric_enthalpy
1026 : ! compute the extra contribution to the Hamiltonian due to the presence of dielectric
1027 132 : CALL ps_implicit_compute_veps(pw_pool_xpndd, dielectric, v_new_xpndd, v_eps_xpndd)
1028 : CALL pw_shrink(neumann_directions, dct_env%dests_shrink, dct_env%srcs_shrink, &
1029 132 : dct_env%bounds_local_shftd, v_eps_xpndd, ps_implicit_env%v_eps)
1030 :
1031 132 : CALL pw_pool_xpndd%give_back_pw(g)
1032 132 : CALL pw_pool_xpndd%give_back_pw(v_old)
1033 132 : CALL pw_pool_xpndd%give_back_pw(res_old)
1034 132 : CALL pw_pool_xpndd%give_back_pw(res_new)
1035 132 : CALL pw_pool_xpndd%give_back_pw(QAinvxres)
1036 132 : CALL pw_pool_xpndd%give_back_pw(PxQAinvxres)
1037 132 : CALL pw_pool_xpndd%give_back_pw(Axvbar)
1038 132 : CALL pw_pool_xpndd%give_back_pw(density_xpndd)
1039 132 : CALL pw_pool_xpndd%give_back_pw(v_new_xpndd)
1040 132 : CALL pw_pool_xpndd%give_back_pw(v_eps_xpndd)
1041 132 : CALL pw_pool_release(pw_pool_xpndd)
1042 :
1043 132 : CALL timestop(handle)
1044 :
1045 264 : END SUBROUTINE implicit_poisson_solver_mixed
1046 :
1047 : ! **************************************************************************************************
1048 : !> \brief allocates and zeroises initial guess for implicit (iterative) Poisson solver
1049 : !> \param ps_implicit_env the implicit env containing the initial guess
1050 : !> \param pw_pool pool of pw grid
1051 : !> \par History
1052 : !> 06.2014 created [Hossein Bani-Hashemian]
1053 : !> \author Mohammad Hossein Bani-Hashemian
1054 : ! **************************************************************************************************
1055 56 : SUBROUTINE ps_implicit_initial_guess_create(ps_implicit_env, pw_pool)
1056 :
1057 : TYPE(ps_implicit_type), INTENT(INOUT), POINTER :: ps_implicit_env
1058 : TYPE(pw_pool_type), INTENT(IN), POINTER :: pw_pool
1059 :
1060 : CHARACTER(LEN=*), PARAMETER :: routineN = 'ps_implicit_initial_guess_create'
1061 :
1062 : INTEGER :: handle, n_tiles_tot
1063 :
1064 56 : CALL timeset(routineN, handle)
1065 :
1066 56 : n_tiles_tot = SIZE(ps_implicit_env%v_D)
1067 56 : NULLIFY (ps_implicit_env%initial_guess)
1068 56 : ALLOCATE (ps_implicit_env%initial_guess)
1069 56 : CALL pw_pool%create_pw(ps_implicit_env%initial_guess)
1070 56 : CALL pw_zero(ps_implicit_env%initial_guess)
1071 168 : ALLOCATE (ps_implicit_env%initial_lambda(n_tiles_tot))
1072 298 : ps_implicit_env%initial_lambda = 0.0_dp
1073 :
1074 56 : CALL timestop(handle)
1075 :
1076 56 : END SUBROUTINE ps_implicit_initial_guess_create
1077 :
1078 : ! **************************************************************************************************
1079 : !> \brief prepare blocks B, Bt, QS, R^-1, v_D
1080 : !> \param pw_pool_orig original pw grid
1081 : !> \param dct_pw_grid DCT (extended) grid
1082 : !> \param green green functions for FFT based inverse Laplacian
1083 : !> \param poisson_params paramaters of the poisson_env
1084 : !> \param ps_implicit_env the implicit_env that stores the blocks
1085 : !> \par History
1086 : !> 10.2014 created [Hossein Bani-Hashemian]
1087 : !> \author Mohammad Hossein Bani-Hashemian
1088 : ! **************************************************************************************************
1089 56 : SUBROUTINE ps_implicit_prepare_blocks(pw_pool_orig, dct_pw_grid, green, &
1090 : poisson_params, ps_implicit_env)
1091 :
1092 : TYPE(pw_pool_type), INTENT(IN), POINTER :: pw_pool_orig
1093 : TYPE(pw_grid_type), INTENT(IN), POINTER :: dct_pw_grid
1094 : TYPE(greens_fn_type), INTENT(IN) :: green
1095 : TYPE(pw_poisson_parameter_type), INTENT(IN) :: poisson_params
1096 : TYPE(ps_implicit_type), INTENT(INOUT), POINTER :: ps_implicit_env
1097 :
1098 : CHARACTER(LEN=*), PARAMETER :: routineN = 'ps_implicit_prepare_blocks'
1099 :
1100 : INTEGER :: data_size, handle, i, indx1, indx2, info, j, k, l, lb1, lb2, lb3, n_contacts, &
1101 : n_tiles, n_tiles_tot, neumann_directions, ngpts_local, ub1, ub2, ub3, unit_nr
1102 : INTEGER(KIND=int_8) :: ngpts
1103 56 : INTEGER, ALLOCATABLE, DIMENSION(:) :: ipiv
1104 : INTEGER, DIMENSION(2, 3) :: bounds, bounds_local
1105 : INTEGER, DIMENSION(3) :: npts, npts_local
1106 : LOGICAL :: done_preparing
1107 : REAL(dp) :: tile_volume, vol_scfac
1108 56 : REAL(dp), ALLOCATABLE, DIMENSION(:) :: Bxunit_vec, test_vec, work_arr
1109 56 : REAL(dp), ALLOCATABLE, DIMENSION(:, :) :: QAinvxBt, R
1110 : TYPE(cp_logger_type), POINTER :: logger
1111 : TYPE(dct_type), POINTER :: dct_env
1112 : TYPE(pw_grid_type), POINTER :: pw_grid_orig
1113 : TYPE(pw_pool_type), POINTER :: pw_pool_xpndd
1114 : TYPE(pw_r3d_rs_type) :: pw_in, pw_out
1115 :
1116 56 : CALL timeset(routineN, handle)
1117 :
1118 56 : pw_grid_orig => pw_pool_orig%pw_grid
1119 :
1120 56 : logger => cp_get_default_logger()
1121 56 : IF (logger%para_env%is_source()) THEN
1122 28 : unit_nr = cp_logger_get_default_unit_nr(logger, local=.TRUE.)
1123 : ELSE
1124 : unit_nr = -1
1125 : END IF
1126 :
1127 72 : SELECT CASE (poisson_params%ps_implicit_params%boundary_condition)
1128 : CASE (MIXED_BC)
1129 :
1130 16 : ngpts_local = dct_pw_grid%ngpts_local
1131 16 : ngpts = dct_pw_grid%ngpts
1132 64 : npts_local = dct_pw_grid%npts_local
1133 64 : npts = dct_pw_grid%npts
1134 160 : bounds_local = dct_pw_grid%bounds_local
1135 160 : bounds = dct_pw_grid%bounds
1136 16 : dct_env => ps_implicit_env%dct_env
1137 :
1138 16 : neumann_directions = poisson_params%ps_implicit_params%neumann_directions
1139 :
1140 14 : SELECT CASE (neumann_directions)
1141 : CASE (neumannXYZ)
1142 14 : vol_scfac = 8.0_dp
1143 : CASE (neumannXY, neumannXZ, neumannYZ)
1144 2 : vol_scfac = 4.0_dp
1145 : CASE (neumannX, neumannY, neumannZ)
1146 16 : vol_scfac = 2.0_dp
1147 : END SELECT
1148 :
1149 : ! evaluate indices for converting 3D arrays into 1D arrays
1150 16 : lb1 = bounds_local(1, 1); ub1 = bounds_local(2, 1)
1151 16 : lb2 = bounds_local(1, 2); ub2 = bounds_local(2, 2)
1152 16 : lb3 = bounds_local(1, 3); ub3 = bounds_local(2, 3)
1153 :
1154 16 : IF (dct_pw_grid%para%blocked) THEN
1155 0 : data_size = PRODUCT(npts_local)
1156 16 : ELSE IF (dct_pw_grid%para%ray_distribution) THEN
1157 16 : data_size = ngpts_local
1158 : ELSE ! parallel run with np = 1
1159 0 : data_size = PRODUCT(npts_local)
1160 : END IF
1161 :
1162 48 : ALLOCATE (ps_implicit_env%idx_1dto3d(data_size))
1163 16 : l = 1
1164 : ! Give up on OpenMP for now (at least the Intel compiler has an issue here)
1165 : ! An automatic OpenMP parallelization of this loop might be tricky
1166 : ! because of the l incrementation
1167 1536 : DO k = lb3, ub3
1168 163376 : DO j = lb2, ub2
1169 7922620 : DO i = lb1, ub1
1170 : ps_implicit_env%idx_1dto3d(l) = (i - lb1 + 1) + &
1171 : (j - lb2)*npts_local(1) + &
1172 7759260 : (k - lb3)*npts_local(1)*npts_local(2)
1173 7921100 : l = l + 1
1174 : END DO
1175 : END DO
1176 : END DO
1177 :
1178 16 : n_contacts = SIZE(ps_implicit_env%contacts)
1179 16 : n_tiles_tot = 0
1180 56 : DO j = 1, n_contacts
1181 56 : n_tiles_tot = n_tiles_tot + ps_implicit_env%contacts(j)%dirichlet_bc%n_tiles
1182 : END DO
1183 :
1184 64 : ALLOCATE (ps_implicit_env%B(n_tiles_tot, data_size))
1185 48 : ALLOCATE (ps_implicit_env%Bt(data_size, n_tiles_tot))
1186 64 : ALLOCATE (ps_implicit_env%QS(n_tiles_tot, n_tiles_tot))
1187 64 : ALLOCATE (ps_implicit_env%Rinv(n_tiles_tot + 1, n_tiles_tot + 1))
1188 48 : ALLOCATE (ps_implicit_env%v_D(n_tiles_tot))
1189 32 : ALLOCATE (ps_implicit_env%osc_frac(n_tiles_tot))
1190 32 : ALLOCATE (ps_implicit_env%frequency(n_tiles_tot))
1191 32 : ALLOCATE (ps_implicit_env%phase(n_tiles_tot))
1192 :
1193 48 : ALLOCATE (QAinvxBt(data_size, n_tiles_tot))
1194 32 : ALLOCATE (Bxunit_vec(n_tiles_tot))
1195 32 : ALLOCATE (test_vec(n_tiles_tot))
1196 48 : ALLOCATE (R(n_tiles_tot + 1, n_tiles_tot + 1))
1197 80 : ALLOCATE (work_arr(n_tiles_tot + 1), ipiv(n_tiles_tot + 1)) ! LAPACK work and ipiv arrays
1198 :
1199 : ! prepare pw_pool for evaluating inverse Laplacian of tile_pw's using DCT
1200 16 : CALL pw_pool_create(pw_pool_xpndd, pw_grid=dct_pw_grid)
1201 :
1202 : ! set up B, B^t, (\Delta^-1)*B^t
1203 16 : indx1 = 1
1204 56 : DO j = 1, n_contacts
1205 40 : n_tiles = ps_implicit_env%contacts(j)%dirichlet_bc%n_tiles
1206 40 : indx2 = indx1 + n_tiles - 1
1207 90 : DO i = 1, n_tiles
1208 :
1209 50 : CALL pw_pool_xpndd%create_pw(pw_in)
1210 : CALL pw_expand(neumann_directions, &
1211 : dct_env%recv_msgs_bnds, dct_env%dests_expand, dct_env%srcs_expand, &
1212 : dct_env%flipg_stat, dct_env%bounds_shftd, &
1213 50 : ps_implicit_env%contacts(j)%dirichlet_bc%tiles(i)%tile%tile_pw, pw_in)
1214 :
1215 50 : tile_volume = ps_implicit_env%contacts(j)%dirichlet_bc%tiles(i)%tile%volume
1216 50 : CALL pw_scale(pw_in, 1.0_dp/(vol_scfac*tile_volume)) ! normalize tile_pw
1217 45985636 : ps_implicit_env%Bt(ps_implicit_env%idx_1dto3d, indx1 + i - 1) = RESHAPE(pw_in%array, [data_size])
1218 :
1219 50 : CALL pw_pool_xpndd%create_pw(pw_out)
1220 50 : CALL apply_inv_laplace_operator_dct(pw_pool_xpndd, green, pw_in, pw_out)
1221 45985636 : QAinvxBt(ps_implicit_env%idx_1dto3d, indx1 + i - 1) = RESHAPE(pw_out%array, [data_size])
1222 : ! the electrostatic potential has opposite sign by internal convention
1223 50 : ps_implicit_env%v_D(indx1 + i - 1) = -1.0_dp*ps_implicit_env%contacts(j)%dirichlet_bc%v_D
1224 50 : ps_implicit_env%osc_frac(indx1 + i - 1) = ps_implicit_env%contacts(j)%dirichlet_bc%osc_frac
1225 50 : ps_implicit_env%frequency(indx1 + i - 1) = ps_implicit_env%contacts(j)%dirichlet_bc%frequency
1226 50 : ps_implicit_env%phase(indx1 + i - 1) = ps_implicit_env%contacts(j)%dirichlet_bc%phase
1227 :
1228 50 : CALL pw_pool_xpndd%give_back_pw(pw_in)
1229 90 : CALL pw_pool_xpndd%give_back_pw(pw_out)
1230 : END DO
1231 56 : indx1 = indx2 + 1
1232 : END DO
1233 61504072 : ps_implicit_env%B(:, :) = TRANSPOSE(ps_implicit_env%Bt)
1234 :
1235 : ! evaluate QS = - B*(\Delta^-1)*B^t
1236 16 : IF (data_size /= 0) THEN
1237 : CALL DGEMM('N', 'N', n_tiles_tot, n_tiles_tot, data_size, &
1238 : -1.0_dp, ps_implicit_env%B, n_tiles_tot, QAinvxBt, &
1239 16 : data_size, 0.0_dp, ps_implicit_env%QS, n_tiles_tot)
1240 : END IF
1241 16 : CALL pw_grid_orig%para%group%sum(ps_implicit_env%QS)
1242 :
1243 : ! evaluate B*1
1244 22992834 : Bxunit_vec(:) = SUM(ps_implicit_env%B, 2)/ngpts
1245 16 : CALL pw_grid_orig%para%group%sum(Bxunit_vec)
1246 : ! set up R = [QS B*1; (B*1)^t 0]
1247 16 : R = 0.0_dp
1248 288 : R(1:n_tiles_tot, 1:n_tiles_tot) = ps_implicit_env%QS
1249 66 : R(1:n_tiles_tot, n_tiles_tot + 1) = Bxunit_vec
1250 66 : R(n_tiles_tot + 1, 1:n_tiles_tot) = Bxunit_vec
1251 : ! evaluate R^(-1)
1252 420 : ps_implicit_env%Rinv(:, :) = R
1253 16 : CALL DGETRF(n_tiles_tot + 1, n_tiles_tot + 1, ps_implicit_env%Rinv, n_tiles_tot + 1, ipiv, info)
1254 16 : IF (info /= 0) THEN
1255 : CALL cp_abort(__LOCATION__, &
1256 : "R is (nearly) singular! Either two Dirichlet constraints are identical or "// &
1257 0 : "you need to reduce the number of tiles.")
1258 : END IF
1259 16 : CALL DGETRI(n_tiles_tot + 1, ps_implicit_env%Rinv, n_tiles_tot + 1, ipiv, work_arr, n_tiles_tot + 1, info)
1260 16 : IF (info /= 0) THEN
1261 0 : CPABORT("Inversion of R failed!")
1262 : END IF
1263 :
1264 16 : DEALLOCATE (QAinvxBt, Bxunit_vec, R, work_arr, ipiv)
1265 16 : CALL pw_pool_release(pw_pool_xpndd)
1266 :
1267 16 : done_preparing = .TRUE.
1268 16 : CALL pw_grid_orig%para%group%sum(done_preparing)
1269 16 : IF ((unit_nr > 0) .AND. done_preparing) THEN
1270 8 : WRITE (unit_nr, "(T3,A,/,T3,A,/,A)") "POISSON| ... Done. ", REPEAT('-', 78)
1271 : END IF
1272 :
1273 : CASE (MIXED_PERIODIC_BC)
1274 :
1275 22 : ngpts_local = pw_grid_orig%ngpts_local
1276 22 : ngpts = pw_grid_orig%ngpts
1277 88 : npts_local = pw_grid_orig%npts_local
1278 88 : npts = pw_grid_orig%npts
1279 220 : bounds_local = pw_grid_orig%bounds_local
1280 220 : bounds = pw_grid_orig%bounds
1281 22 : dct_env => ps_implicit_env%dct_env
1282 :
1283 : ! evaluate indices for converting 3D arrays into 1D arrays
1284 22 : lb1 = bounds_local(1, 1); ub1 = bounds_local(2, 1)
1285 22 : lb2 = bounds_local(1, 2); ub2 = bounds_local(2, 2)
1286 22 : lb3 = bounds_local(1, 3); ub3 = bounds_local(2, 3)
1287 :
1288 22 : IF (pw_grid_orig%para%blocked) THEN
1289 0 : data_size = PRODUCT(npts_local)
1290 22 : ELSE IF (pw_grid_orig%para%ray_distribution) THEN
1291 22 : data_size = ngpts_local
1292 : ELSE ! parallel run with np = 1
1293 0 : data_size = PRODUCT(npts_local)
1294 : END IF
1295 :
1296 66 : ALLOCATE (ps_implicit_env%idx_1dto3d(data_size))
1297 22 : l = 1
1298 : ! Give up on OpenMP for now (at least the Intel compiler has an issue here)
1299 : ! An automatic OpenMP parallelization of this loop might be tricky
1300 : ! because of the l incrementation
1301 1444 : DO k = lb3, ub3
1302 95134 : DO j = lb2, ub2
1303 3231189 : DO i = lb1, ub1
1304 : ps_implicit_env%idx_1dto3d(l) = (i - lb1 + 1) + &
1305 : (j - lb2)*npts_local(1) + &
1306 3136077 : (k - lb3)*npts_local(1)*npts_local(2)
1307 3229767 : l = l + 1
1308 : END DO
1309 : END DO
1310 : END DO
1311 :
1312 22 : n_contacts = SIZE(ps_implicit_env%contacts)
1313 22 : n_tiles_tot = 0
1314 110 : DO j = 1, n_contacts
1315 110 : n_tiles_tot = n_tiles_tot + ps_implicit_env%contacts(j)%dirichlet_bc%n_tiles
1316 : END DO
1317 :
1318 88 : ALLOCATE (ps_implicit_env%B(n_tiles_tot, data_size))
1319 66 : ALLOCATE (ps_implicit_env%Bt(data_size, n_tiles_tot))
1320 88 : ALLOCATE (ps_implicit_env%QS(n_tiles_tot, n_tiles_tot))
1321 88 : ALLOCATE (ps_implicit_env%Rinv(n_tiles_tot + 1, n_tiles_tot + 1))
1322 66 : ALLOCATE (ps_implicit_env%v_D(n_tiles_tot))
1323 44 : ALLOCATE (ps_implicit_env%osc_frac(n_tiles_tot))
1324 44 : ALLOCATE (ps_implicit_env%frequency(n_tiles_tot))
1325 44 : ALLOCATE (ps_implicit_env%phase(n_tiles_tot))
1326 :
1327 66 : ALLOCATE (QAinvxBt(data_size, n_tiles_tot))
1328 44 : ALLOCATE (Bxunit_vec(n_tiles_tot))
1329 44 : ALLOCATE (test_vec(n_tiles_tot))
1330 66 : ALLOCATE (R(n_tiles_tot + 1, n_tiles_tot + 1))
1331 110 : ALLOCATE (work_arr(n_tiles_tot + 1), ipiv(n_tiles_tot + 1))
1332 :
1333 : ! set up B, B^t, (\Delta^-1)*B^t
1334 110 : indx1 = 1
1335 110 : DO j = 1, n_contacts
1336 88 : n_tiles = ps_implicit_env%contacts(j)%dirichlet_bc%n_tiles
1337 88 : indx2 = indx1 + n_tiles - 1
1338 262 : DO i = 1, n_tiles
1339 174 : CALL pw_pool_orig%create_pw(pw_in)
1340 174 : CALL pw_copy(ps_implicit_env%contacts(j)%dirichlet_bc%tiles(i)%tile%tile_pw, pw_in)
1341 :
1342 174 : tile_volume = ps_implicit_env%contacts(j)%dirichlet_bc%tiles(i)%tile%volume
1343 174 : CALL pw_scale(pw_in, 1.0_dp/tile_volume) ! normalize tile_pw
1344 40325064 : ps_implicit_env%Bt(ps_implicit_env%idx_1dto3d, indx1 + i - 1) = RESHAPE(pw_in%array, [data_size])
1345 :
1346 174 : CALL pw_pool_orig%create_pw(pw_out)
1347 174 : CALL apply_inv_laplace_operator_fft(pw_pool_orig, green, pw_in, pw_out)
1348 40325064 : QAinvxBt(ps_implicit_env%idx_1dto3d, indx1 + i - 1) = RESHAPE(pw_out%array, [data_size])
1349 : ! the electrostatic potential has opposite sign by internal convention
1350 174 : ps_implicit_env%v_D(indx1 + i - 1) = -1.0_dp*ps_implicit_env%contacts(j)%dirichlet_bc%v_D
1351 174 : ps_implicit_env%osc_frac(indx1 + i - 1) = ps_implicit_env%contacts(j)%dirichlet_bc%osc_frac
1352 174 : ps_implicit_env%frequency(indx1 + i - 1) = ps_implicit_env%contacts(j)%dirichlet_bc%frequency
1353 174 : ps_implicit_env%phase(indx1 + i - 1) = ps_implicit_env%contacts(j)%dirichlet_bc%phase
1354 :
1355 174 : CALL pw_pool_orig%give_back_pw(pw_in)
1356 262 : CALL pw_pool_orig%give_back_pw(pw_out)
1357 : END DO
1358 110 : indx1 = indx2 + 1
1359 : END DO
1360 46596892 : ps_implicit_env%B(:, :) = TRANSPOSE(ps_implicit_env%Bt)
1361 :
1362 : ! evaluate QS = - B*(\Delta^-1)*B^t
1363 22 : IF (data_size /= 0) THEN
1364 : CALL DGEMM('N', 'N', n_tiles_tot, n_tiles_tot, data_size, &
1365 : -1.0_dp, ps_implicit_env%B, n_tiles_tot, QAinvxBt, &
1366 22 : data_size, 0.0_dp, ps_implicit_env%QS, n_tiles_tot)
1367 : END IF
1368 22 : CALL pw_grid_orig%para%group%sum(ps_implicit_env%QS)
1369 :
1370 : ! evaluate B*1
1371 20162554 : Bxunit_vec(:) = SUM(ps_implicit_env%B, 2)/ngpts
1372 22 : CALL pw_grid_orig%para%group%sum(Bxunit_vec)
1373 : ! set up R = [QS B*1; (B*1)^t 0]
1374 22 : R = 0.0_dp
1375 3074 : R(1:n_tiles_tot, 1:n_tiles_tot) = ps_implicit_env%QS
1376 196 : R(1:n_tiles_tot, n_tiles_tot + 1) = Bxunit_vec
1377 196 : R(n_tiles_tot + 1, 1:n_tiles_tot) = Bxunit_vec
1378 : ! evaluate R^(-1)
1379 3466 : ps_implicit_env%Rinv(:, :) = R
1380 22 : CALL DGETRF(n_tiles_tot + 1, n_tiles_tot + 1, ps_implicit_env%Rinv, n_tiles_tot + 1, ipiv, info)
1381 22 : IF (info /= 0) THEN
1382 : CALL cp_abort(__LOCATION__, &
1383 : "R is (nearly) singular! Either two Dirichlet constraints are identical or "// &
1384 0 : "you need to reduce the number of tiles.")
1385 : END IF
1386 22 : CALL DGETRI(n_tiles_tot + 1, ps_implicit_env%Rinv, n_tiles_tot + 1, ipiv, work_arr, n_tiles_tot + 1, info)
1387 22 : IF (info /= 0) THEN
1388 0 : CPABORT("Inversion of R failed!")
1389 : END IF
1390 :
1391 22 : DEALLOCATE (QAinvxBt, Bxunit_vec, R, work_arr, ipiv)
1392 :
1393 22 : done_preparing = .TRUE.
1394 22 : CALL pw_grid_orig%para%group%sum(done_preparing)
1395 22 : IF ((unit_nr > 0) .AND. done_preparing) THEN
1396 11 : WRITE (unit_nr, "(T3,A,/,T3,A,/,A)") "POISSON| ... Done. ", REPEAT('-', 78)
1397 : END IF
1398 :
1399 : CASE (PERIODIC_BC, NEUMANN_BC)
1400 :
1401 18 : ALLOCATE (ps_implicit_env%idx_1dto3d(1))
1402 18 : ALLOCATE (ps_implicit_env%B(1, 1))
1403 18 : ALLOCATE (ps_implicit_env%Bt(1, 1))
1404 18 : ALLOCATE (ps_implicit_env%QS(1, 1))
1405 18 : ALLOCATE (ps_implicit_env%Rinv(1, 1))
1406 18 : ALLOCATE (ps_implicit_env%v_D(1))
1407 18 : ALLOCATE (ps_implicit_env%osc_frac(1))
1408 18 : ALLOCATE (ps_implicit_env%frequency(1))
1409 18 : ALLOCATE (ps_implicit_env%phase(1))
1410 :
1411 36 : ps_implicit_env%idx_1dto3d = 1
1412 54 : ps_implicit_env%B = 0.0_dp
1413 54 : ps_implicit_env%Bt = 0.0_dp
1414 54 : ps_implicit_env%QS = 0.0_dp
1415 54 : ps_implicit_env%Rinv = 0.0_dp
1416 36 : ps_implicit_env%v_D = 0.0_dp
1417 :
1418 : CASE DEFAULT
1419 : CALL cp_abort(__LOCATION__, &
1420 : "Please specify the type of boundary conditions using the "// &
1421 56 : "input file keyword BOUNDARY_CONDITIONS.")
1422 : END SELECT
1423 :
1424 56 : CALL timestop(handle)
1425 :
1426 112 : END SUBROUTINE ps_implicit_prepare_blocks
1427 :
1428 : ! **************************************************************************************************
1429 : !> \brief Evaluates the action of the operator P on a given matrix v, defined
1430 : !> as: P(v) := - \nabla_r(\ln(\eps)) \cdot \nabla_r(v)
1431 : !> \param pw_pool pool of pw grid
1432 : !> \param dielectric dielectric_type containing eps
1433 : !> \param v input matrix
1434 : !> \param Pxv action of the operator P on v
1435 : !> \par History
1436 : !> 07.2014 created [Hossein Bani-Hashemian]
1437 : !> \author Mohammad Hossein Bani-Hashemian
1438 : ! **************************************************************************************************
1439 4758 : SUBROUTINE apply_P_operator(pw_pool, dielectric, v, Pxv)
1440 :
1441 : TYPE(pw_pool_type), POINTER :: pw_pool
1442 : TYPE(dielectric_type), INTENT(IN), POINTER :: dielectric
1443 : TYPE(pw_r3d_rs_type), INTENT(IN) :: v
1444 : TYPE(pw_r3d_rs_type), INTENT(INOUT) :: Pxv
1445 :
1446 : CHARACTER(LEN=*), PARAMETER :: routineN = 'apply_P_operator'
1447 :
1448 : INTEGER :: handle, i
1449 19032 : TYPE(pw_r3d_rs_type), DIMENSION(3) :: dv
1450 :
1451 4758 : CALL timeset(routineN, handle)
1452 :
1453 19032 : DO i = 1, 3
1454 19032 : CALL pw_pool%create_pw(dv(i))
1455 : END DO
1456 :
1457 4758 : CALL derive_fft(v, dv, pw_pool)
1458 : ASSOCIATE (dln_eps => dielectric%dln_eps)
1459 : Pxv%array = -(dv(1)%array*dln_eps(1)%array + &
1460 : dv(2)%array*dln_eps(2)%array + &
1461 850866506 : dv(3)%array*dln_eps(3)%array)
1462 : END ASSOCIATE
1463 :
1464 19032 : DO i = 1, 3
1465 19032 : CALL pw_pool%give_back_pw(dv(i))
1466 : END DO
1467 :
1468 4758 : CALL timestop(handle)
1469 :
1470 4758 : END SUBROUTINE apply_P_operator
1471 :
1472 : ! **************************************************************************************************
1473 : !> \brief Evaluates the action of the inverse of the Laplace operator on a given 3d matrix
1474 : !> \param pw_pool pool of pw grid
1475 : !> \param green green functions for FFT based inverse Laplacian
1476 : !> \param pw_in pw_in (density)
1477 : !> \param pw_out pw_out (potential)
1478 : !> \par History
1479 : !> 07.2014 created [Hossein Bani-Hashemian]
1480 : !> \author Mohammad Hossein Bani-Hashemian
1481 : ! **************************************************************************************************
1482 2770 : SUBROUTINE apply_inv_laplace_operator_fft(pw_pool, green, pw_in, pw_out)
1483 :
1484 : TYPE(pw_pool_type), INTENT(IN), POINTER :: pw_pool
1485 : TYPE(greens_fn_type), INTENT(IN) :: green
1486 : TYPE(pw_r3d_rs_type), INTENT(IN) :: pw_in
1487 : TYPE(pw_r3d_rs_type), INTENT(INOUT) :: pw_out
1488 :
1489 : CHARACTER(LEN=*), PARAMETER :: routineN = 'apply_inv_laplace_operator_fft'
1490 :
1491 : INTEGER :: handle, ig, ng
1492 : REAL(dp) :: prefactor
1493 : TYPE(pw_c1d_gs_type) :: pw_in_gs
1494 : TYPE(pw_grid_type), POINTER :: pw_grid
1495 :
1496 2770 : CALL timeset(routineN, handle)
1497 :
1498 : ! here I divide by fourpi to cancel out the prefactor fourpi in influence_fn
1499 2770 : prefactor = 1.0_dp/fourpi
1500 :
1501 2770 : pw_grid => pw_pool%pw_grid
1502 2770 : ng = SIZE(pw_grid%gsq)
1503 :
1504 2770 : CALL pw_pool%create_pw(pw_in_gs)
1505 :
1506 2770 : CALL pw_transfer(pw_in, pw_in_gs)
1507 385725972 : DO ig = 1, ng
1508 385725972 : pw_in_gs%array(ig) = prefactor*pw_in_gs%array(ig)*green%influence_fn%array(ig)
1509 : END DO
1510 2770 : CALL pw_transfer(pw_in_gs, pw_out)
1511 :
1512 2770 : CALL pw_pool%give_back_pw(pw_in_gs)
1513 :
1514 2770 : CALL timestop(handle)
1515 :
1516 2770 : END SUBROUTINE apply_inv_laplace_operator_fft
1517 :
1518 : ! **************************************************************************************************
1519 : !> \brief Evaluates the action of the inverse of the Laplace operator on a given
1520 : !> 3d matrix using DCT-I
1521 : !> \param pw_pool pool of pw grid
1522 : !> \param green the greens_fn_type data holding a valid dct_influence_fn
1523 : !> \param pw_in pw_in (density)
1524 : !> \param pw_out pw_out (potential)
1525 : !> \par History
1526 : !> 07.2014 created [Hossein Bani-Hashemian]
1527 : !> 11.2015 revised [Hossein Bani-Hashemian]
1528 : !> \author Mohammad Hossein Bani-Hashemian
1529 : ! **************************************************************************************************
1530 474 : SUBROUTINE apply_inv_laplace_operator_dct(pw_pool, green, pw_in, pw_out)
1531 :
1532 : TYPE(pw_pool_type), INTENT(IN), POINTER :: pw_pool
1533 : TYPE(greens_fn_type), INTENT(IN) :: green
1534 : TYPE(pw_r3d_rs_type), INTENT(IN) :: pw_in
1535 : TYPE(pw_r3d_rs_type), INTENT(INOUT) :: pw_out
1536 :
1537 : CHARACTER(LEN=*), PARAMETER :: routineN = 'apply_inv_laplace_operator_dct'
1538 :
1539 : INTEGER :: handle, ig, ng
1540 : REAL(dp) :: prefactor
1541 : TYPE(pw_c1d_gs_type) :: pw_in_gs
1542 : TYPE(pw_grid_type), POINTER :: pw_grid
1543 :
1544 474 : CALL timeset(routineN, handle)
1545 :
1546 : ! here I divide by fourpi to cancel out the prefactor fourpi in influence_fn
1547 474 : prefactor = 1.0_dp/fourpi
1548 :
1549 474 : pw_grid => pw_pool%pw_grid
1550 474 : ng = SIZE(pw_grid%gsq)
1551 :
1552 474 : CALL pw_pool%create_pw(pw_in_gs)
1553 :
1554 474 : CALL pw_transfer(pw_in, pw_in_gs)
1555 205618218 : DO ig = 1, ng
1556 205618218 : pw_in_gs%array(ig) = prefactor*pw_in_gs%array(ig)*green%dct_influence_fn%array(ig)
1557 : END DO
1558 474 : CALL pw_transfer(pw_in_gs, pw_out)
1559 :
1560 474 : CALL pw_pool%give_back_pw(pw_in_gs)
1561 :
1562 474 : CALL timestop(handle)
1563 :
1564 474 : END SUBROUTINE apply_inv_laplace_operator_dct
1565 :
1566 : ! **************************************************************************************************
1567 : !> \brief Evaluates the action of the Laplace operator on a given 3d matrix
1568 : !> \param pw_pool pool of pw grid
1569 : !> \param green green functions for FFT based inverse Laplacian
1570 : !> \param pw_in pw_in (potential)
1571 : !> \param pw_out pw_out (density)
1572 : !> \par History
1573 : !> 07.2014 created [Hossein Bani-Hashemian]
1574 : !> \author Mohammad Hossein Bani-Hashemian
1575 : ! **************************************************************************************************
1576 342 : SUBROUTINE apply_laplace_operator_fft(pw_pool, green, pw_in, pw_out)
1577 :
1578 : TYPE(pw_pool_type), INTENT(IN), POINTER :: pw_pool
1579 : TYPE(greens_fn_type), INTENT(IN) :: green
1580 : TYPE(pw_r3d_rs_type), INTENT(IN) :: pw_in
1581 : TYPE(pw_r3d_rs_type), INTENT(INOUT) :: pw_out
1582 :
1583 : CHARACTER(LEN=*), PARAMETER :: routineN = 'apply_laplace_operator_fft'
1584 :
1585 : INTEGER :: g0_index, handle, ig, ng
1586 : LOGICAL :: have_g0
1587 : REAL(dp) :: prefactor
1588 : TYPE(pw_c1d_gs_type) :: pw_in_gs
1589 : TYPE(pw_grid_type), POINTER :: pw_grid
1590 :
1591 342 : CALL timeset(routineN, handle)
1592 :
1593 : ! here I multiply by fourpi to cancel out the prefactor fourpi in influence_fn
1594 342 : prefactor = fourpi
1595 :
1596 342 : pw_grid => pw_pool%pw_grid
1597 342 : ng = SIZE(pw_in%pw_grid%gsq)
1598 342 : have_g0 = green%influence_fn%pw_grid%have_g0
1599 :
1600 342 : CALL pw_pool%create_pw(pw_in_gs)
1601 :
1602 342 : CALL pw_transfer(pw_in, pw_in_gs)
1603 :
1604 342 : IF (have_g0) THEN
1605 171 : g0_index = green%influence_fn%pw_grid%first_gne0 - 1
1606 171 : pw_in_gs%array(g0_index) = 0.0_dp
1607 : END IF
1608 48044571 : DO ig = green%influence_fn%pw_grid%first_gne0, ng
1609 48044571 : pw_in_gs%array(ig) = prefactor*(pw_in_gs%array(ig)/green%influence_fn%array(ig))
1610 : END DO
1611 :
1612 342 : CALL pw_transfer(pw_in_gs, pw_out)
1613 :
1614 342 : CALL pw_pool%give_back_pw(pw_in_gs)
1615 :
1616 342 : CALL timestop(handle)
1617 :
1618 342 : END SUBROUTINE apply_laplace_operator_fft
1619 :
1620 : ! **************************************************************************************************
1621 : !> \brief Evaluates the action of the Laplace operator on a given 3d matrix using DCT-I
1622 : !> \param pw_pool pool of pw grid
1623 : !> \param green the greens_fn_type data holding a valid dct_influence_fn
1624 : !> \param pw_in pw_in (potential)
1625 : !> \param pw_out pw_out (density)
1626 : !> \par History
1627 : !> 07.2014 created [Hossein Bani-Hashemian]
1628 : !> 11.2015 revised [Hossein Bani-Hashemian]
1629 : !> \author Mohammad Hossein Bani-Hashemian
1630 : ! **************************************************************************************************
1631 156 : SUBROUTINE apply_laplace_operator_dct(pw_pool, green, pw_in, pw_out)
1632 :
1633 : TYPE(pw_pool_type), INTENT(IN), POINTER :: pw_pool
1634 : TYPE(greens_fn_type), INTENT(IN) :: green
1635 : TYPE(pw_r3d_rs_type), INTENT(IN) :: pw_in
1636 : TYPE(pw_r3d_rs_type), INTENT(INOUT) :: pw_out
1637 :
1638 : CHARACTER(LEN=*), PARAMETER :: routineN = 'apply_laplace_operator_dct'
1639 :
1640 : INTEGER :: g0_index, handle, ig, ng
1641 : LOGICAL :: have_g0
1642 : REAL(dp) :: prefactor
1643 : TYPE(pw_c1d_gs_type) :: pw_in_gs
1644 : TYPE(pw_grid_type), POINTER :: pw_grid
1645 :
1646 156 : CALL timeset(routineN, handle)
1647 :
1648 : ! here I multiply by fourpi to cancel out the prefactor fourpi in influence_fn
1649 156 : prefactor = fourpi
1650 :
1651 156 : pw_grid => pw_pool%pw_grid
1652 156 : ng = SIZE(pw_in%pw_grid%gsq)
1653 156 : have_g0 = green%dct_influence_fn%pw_grid%have_g0
1654 :
1655 156 : CALL pw_pool%create_pw(pw_in_gs)
1656 :
1657 156 : CALL pw_transfer(pw_in, pw_in_gs)
1658 :
1659 156 : IF (have_g0) THEN
1660 78 : g0_index = green%dct_influence_fn%pw_grid%first_gne0 - 1
1661 78 : pw_in_gs%array(g0_index) = 0.0_dp
1662 : END IF
1663 65185854 : DO ig = green%dct_influence_fn%pw_grid%first_gne0, ng
1664 65185854 : pw_in_gs%array(ig) = prefactor*(pw_in_gs%array(ig)/green%dct_influence_fn%array(ig))
1665 : END DO
1666 :
1667 156 : CALL pw_transfer(pw_in_gs, pw_out)
1668 :
1669 156 : CALL pw_pool%give_back_pw(pw_in_gs)
1670 :
1671 156 : CALL timestop(handle)
1672 :
1673 156 : END SUBROUTINE apply_laplace_operator_dct
1674 :
1675 : ! **************************************************************************************************
1676 : !> \brief Evaluates the action of the generalized Poisson operator on a given 3d matrix.
1677 : !> \param pw_pool pool of pw grid
1678 : !> \param green green functions for FFT based inverse Laplacian
1679 : !> \param dielectric dielectric environment
1680 : !> \param v potential
1681 : !> \param density density
1682 : !> \par History
1683 : !> 07.2014 created [Hossein Bani-Hashemian]
1684 : !> \author Mohammad Hossein Bani-Hashemian
1685 : ! **************************************************************************************************
1686 342 : SUBROUTINE apply_poisson_operator_fft(pw_pool, green, dielectric, v, density)
1687 :
1688 : TYPE(pw_pool_type), INTENT(IN), POINTER :: pw_pool
1689 : TYPE(greens_fn_type), INTENT(IN) :: green
1690 : TYPE(dielectric_type), INTENT(IN), POINTER :: dielectric
1691 : TYPE(pw_r3d_rs_type), INTENT(IN) :: v
1692 : TYPE(pw_r3d_rs_type), INTENT(INOUT) :: density
1693 :
1694 : CHARACTER(LEN=*), PARAMETER :: routineN = 'apply_poisson_operator_fft'
1695 :
1696 : INTEGER :: handle
1697 : TYPE(pw_r3d_rs_type) :: Pxv
1698 :
1699 342 : CALL timeset(routineN, handle)
1700 :
1701 342 : CALL pw_pool%create_pw(Pxv)
1702 :
1703 342 : CALL apply_P_operator(pw_pool, dielectric, v, Pxv)
1704 342 : CALL apply_laplace_operator_fft(pw_pool, green, v, density)
1705 342 : CALL pw_axpy(Pxv, density)
1706 :
1707 342 : CALL pw_pool%give_back_pw(Pxv)
1708 :
1709 342 : CALL timestop(handle)
1710 :
1711 342 : END SUBROUTINE apply_poisson_operator_fft
1712 :
1713 : ! **************************************************************************************************
1714 : !> \brief Evaluates the action of the generalized Poisson operator on a given
1715 : !> 3d matrix using DCT-I.
1716 : !> \param pw_pool pool of pw grid
1717 : !> \param green the greens_fn_type data holding a valid dct_influence_fn
1718 : !> \param dielectric dielectric environment
1719 : !> \param v potential
1720 : !> \param density density
1721 : !> \par History
1722 : !> 07.2014 created [Hossein Bani-Hashemian]
1723 : !> 11.2015 revised [Hossein Bani-Hashemian]
1724 : !> \author Mohammad Hossein Bani-Hashemian
1725 : ! **************************************************************************************************
1726 156 : SUBROUTINE apply_poisson_operator_dct(pw_pool, green, dielectric, v, density)
1727 :
1728 : TYPE(pw_pool_type), INTENT(IN), POINTER :: pw_pool
1729 : TYPE(greens_fn_type), INTENT(IN) :: green
1730 : TYPE(dielectric_type), INTENT(IN), POINTER :: dielectric
1731 : TYPE(pw_r3d_rs_type), INTENT(IN) :: v
1732 : TYPE(pw_r3d_rs_type), INTENT(INOUT) :: density
1733 :
1734 : CHARACTER(LEN=*), PARAMETER :: routineN = 'apply_poisson_operator_dct'
1735 :
1736 : INTEGER :: handle
1737 : TYPE(pw_r3d_rs_type) :: Pxv
1738 :
1739 156 : CALL timeset(routineN, handle)
1740 :
1741 156 : CALL pw_pool%create_pw(Pxv)
1742 :
1743 156 : CALL apply_P_operator(pw_pool, dielectric, v, Pxv)
1744 156 : CALL apply_laplace_operator_dct(pw_pool, green, v, density)
1745 156 : CALL pw_axpy(Pxv, density)
1746 :
1747 156 : CALL pw_pool%give_back_pw(Pxv)
1748 :
1749 156 : CALL timestop(handle)
1750 :
1751 156 : END SUBROUTINE apply_poisson_operator_dct
1752 :
1753 : ! **************************************************************************************************
1754 : !> \brief Computes the extra contribution (v_eps)
1755 : !> v_eps = - \frac{1}{8*\pi} * |\nabla_r(v)|^2 * \frac{d \eps}{d \rho}
1756 : !> to the functional derivative of the Hartree energy wrt the density, being
1757 : !> attributed to the dependency of the dielectric constant to the charge density.
1758 : !> [see V. M. Sanchez, M. Sued, and D. A. Scherlis, J. Chem. Phys. 131, 174108 (2009)]
1759 : !> \param pw_pool pool of the original plane-wave grid
1760 : !> \param dielectric dielectric environment
1761 : !> \param v Hartree potential
1762 : !> \param v_eps v_eps
1763 : !> \par History
1764 : !> 08.2014 created [Hossein Bani-Hashemian]
1765 : !> \author Mohammad Hossein Bani-Hashemian
1766 : ! **************************************************************************************************
1767 498 : SUBROUTINE ps_implicit_compute_veps(pw_pool, dielectric, v, v_eps)
1768 :
1769 : TYPE(pw_pool_type), INTENT(IN), POINTER :: pw_pool
1770 : TYPE(dielectric_type), INTENT(IN), POINTER :: dielectric
1771 : TYPE(pw_r3d_rs_type), INTENT(IN) :: v
1772 : TYPE(pw_r3d_rs_type), INTENT(INOUT) :: v_eps
1773 :
1774 : CHARACTER(LEN=*), PARAMETER :: routineN = 'ps_implicit_compute_veps'
1775 :
1776 : INTEGER :: handle, i
1777 : REAL(dp) :: eightpi
1778 : TYPE(pw_r3d_rs_type) :: dv2
1779 1992 : TYPE(pw_r3d_rs_type), DIMENSION(3) :: dv
1780 :
1781 498 : CALL timeset(routineN, handle)
1782 :
1783 498 : eightpi = 2*fourpi
1784 :
1785 498 : CALL pw_pool%create_pw(dv2)
1786 1992 : DO i = 1, 3
1787 1992 : CALL pw_pool%create_pw(dv(i))
1788 : END DO
1789 :
1790 498 : CALL derive_fft(v, dv, pw_pool)
1791 :
1792 : ! evaluate |\nabla_r(v)|^2
1793 115979154 : dv2%array = dv(1)%array**2 + dv(2)%array**2 + dv(3)%array**2
1794 :
1795 115979154 : v_eps%array = -(1.0_dp/eightpi)*(dv2%array*dielectric%deps_drho%array)
1796 :
1797 498 : CALL pw_pool%give_back_pw(dv2)
1798 1992 : DO i = 1, 3
1799 1992 : CALL pw_pool%give_back_pw(dv(i))
1800 : END DO
1801 :
1802 498 : CALL timestop(handle)
1803 :
1804 498 : END SUBROUTINE ps_implicit_compute_veps
1805 :
1806 : ! **************************************************************************************************
1807 : !> \brief Computes the Hartree energy
1808 : !> \param density electronic density
1809 : !> \param v Hartree potential
1810 : !> \param ehartree Hartree energy
1811 : !> \par History
1812 : !> 06.2015 created [Hossein Bani-Hashemian]
1813 : !> \author Mohammad Hossein Bani-Hashemian
1814 : ! **************************************************************************************************
1815 784 : SUBROUTINE compute_ehartree_periodic_bc(density, v, ehartree)
1816 :
1817 : TYPE(pw_r3d_rs_type), INTENT(IN) :: density, v
1818 : REAL(dp), INTENT(OUT) :: ehartree
1819 :
1820 : CHARACTER(LEN=*), PARAMETER :: routineN = 'compute_ehartree_periodic_bc'
1821 :
1822 : INTEGER :: handle
1823 :
1824 784 : CALL timeset(routineN, handle)
1825 :
1826 : ! E_H = \frac{1}{2} * \int \rho * v dr
1827 784 : ehartree = 0.5_dp*pw_integral_ab(density, v)
1828 :
1829 784 : CALL timestop(handle)
1830 :
1831 784 : END SUBROUTINE compute_ehartree_periodic_bc
1832 :
1833 : ! **************************************************************************************************
1834 : !> \brief Computes the Hartree energy
1835 : !> \param dielectric dielectric environment
1836 : !> \param density electronic density
1837 : !> \param Btxlambda B^t * \lambda (\lambda is the vector of Lagrange multipliers
1838 : !> and B^t is the transpose of the boundary operator
1839 : !> \param v Hartree potential
1840 : !> \param ehartree Hartree energy
1841 : !> \param electric_enthalpy electric enthalpy
1842 : !> \par History
1843 : !> 06.2015 created [Hossein Bani-Hashemian]
1844 : !> \author Mohammad Hossein Bani-Hashemian
1845 : ! **************************************************************************************************
1846 1738 : SUBROUTINE compute_ehartree_mixed_bc(dielectric, density, Btxlambda, v, ehartree, electric_enthalpy)
1847 :
1848 : TYPE(dielectric_type), INTENT(IN), POINTER :: dielectric
1849 : TYPE(pw_r3d_rs_type), INTENT(IN) :: density
1850 : REAL(dp), ALLOCATABLE, DIMENSION(:, :, :), &
1851 : INTENT(IN) :: Btxlambda
1852 : TYPE(pw_r3d_rs_type), INTENT(IN) :: v
1853 : REAL(dp), INTENT(OUT) :: ehartree, electric_enthalpy
1854 :
1855 : CHARACTER(LEN=*), PARAMETER :: routineN = 'compute_ehartree_mixed_bc'
1856 :
1857 : INTEGER :: handle
1858 : REAL(dp) :: dvol, ehartree_rho, ehartree_rho_cstr
1859 : TYPE(pw_grid_type), POINTER :: pw_grid
1860 :
1861 1738 : CALL timeset(routineN, handle)
1862 :
1863 1738 : pw_grid => v%pw_grid
1864 :
1865 1738 : dvol = pw_grid%dvol
1866 :
1867 : ! E_H = \frac{1}{2} * \int \rho * v dr + \frac{1}{8 \pi} * \int Btxlambda * v dr
1868 : ! the sign of the second term depends on the sign chosen for the Lagrange multiplier
1869 : ! term in the variational form
1870 287894894 : ehartree_rho = accurate_sum(density%array*v%array)
1871 287894894 : ehartree_rho_cstr = accurate_sum(dielectric%eps%array*Btxlambda*v%array/fourpi)
1872 1738 : ehartree_rho = 0.5_dp*ehartree_rho*dvol
1873 1738 : ehartree_rho_cstr = 0.5_dp*ehartree_rho_cstr*dvol
1874 1738 : CALL pw_grid%para%group%sum(ehartree_rho)
1875 1738 : CALL pw_grid%para%group%sum(ehartree_rho_cstr)
1876 1738 : electric_enthalpy = ehartree_rho + ehartree_rho_cstr
1877 1738 : ehartree = ehartree_rho - ehartree_rho_cstr
1878 :
1879 1738 : CALL timestop(handle)
1880 :
1881 1738 : END SUBROUTINE compute_ehartree_mixed_bc
1882 :
1883 : ! **************************************************************************************************
1884 : !> \brief Computes the (normalized) preconditioned residual norm error and the
1885 : !> normalized absolute error
1886 : !> \param pw_pool pool of the original plane-wave grid
1887 : !> \param green greens functions for FFT based inverse Laplacian
1888 : !> \param res_new residual
1889 : !> \param v_old old v
1890 : !> \param v_new new v
1891 : !> \param QAinvxres_new Delta^-1(res_new)
1892 : !> \param pres_error preconditioned residual norm error
1893 : !> \param nabs_error normalized absolute error
1894 : !> \par History
1895 : !> 07.2014 created [Hossein Bani-Hashemian]
1896 : !> \author Mohammad Hossein Bani-Hashemian
1897 : ! **************************************************************************************************
1898 2254 : SUBROUTINE ps_implicit_compute_error_fft(pw_pool, green, res_new, v_old, v_new, &
1899 : QAinvxres_new, pres_error, nabs_error)
1900 :
1901 : TYPE(pw_pool_type), INTENT(IN), POINTER :: pw_pool
1902 : TYPE(greens_fn_type), INTENT(IN) :: green
1903 : TYPE(pw_r3d_rs_type), INTENT(IN) :: res_new, v_old, v_new
1904 : TYPE(pw_r3d_rs_type), INTENT(INOUT) :: QAinvxres_new
1905 : REAL(dp), INTENT(OUT) :: pres_error, nabs_error
1906 :
1907 : CHARACTER(LEN=*), PARAMETER :: routineN = 'ps_implicit_compute_error_fft'
1908 :
1909 : INTEGER :: handle
1910 : REAL(dp) :: vol
1911 :
1912 2254 : CALL timeset(routineN, handle)
1913 :
1914 2254 : vol = pw_pool%pw_grid%vol
1915 :
1916 : ! evaluate \Delta^-1(res) = \Delta^-1 (g - \Delta(v_new) - P(v_new) + Bt \lambda)
1917 2254 : CALL apply_inv_laplace_operator_fft(pw_pool, green, res_new, QAinvxres_new)
1918 : ! (normalized) preconditioned residual norm error :
1919 327225790 : pres_error = accurate_sum(QAinvxres_new%array(:, :, :)**2)
1920 2254 : CALL pw_pool%pw_grid%para%group%sum(pres_error)
1921 2254 : pres_error = SQRT(pres_error)/vol
1922 :
1923 : ! normalized absolute error :
1924 : ! nabs_error := \frac{\| v_old - v_new \|}{volume}
1925 327225790 : nabs_error = accurate_sum(ABS(v_old%array - v_new%array)**2)
1926 2254 : CALL pw_pool%pw_grid%para%group%sum(nabs_error)
1927 2254 : nabs_error = SQRT(nabs_error)/vol
1928 :
1929 2254 : CALL timestop(handle)
1930 :
1931 2254 : END SUBROUTINE ps_implicit_compute_error_fft
1932 :
1933 : ! **************************************************************************************************
1934 : !> \brief Computes the (normalized) preconditioned residual norm error and the
1935 : !> normalized absolute error
1936 : !> \param pw_pool pool of the original plane-wave grid
1937 : !> \param green the greens_fn_type data holding a valid dct_influence_fn
1938 : !> \param res_new residual
1939 : !> \param v_old old v
1940 : !> \param v_new new v
1941 : !> \param QAinvxres_new Delta^-1(res_new)
1942 : !> \param pres_error preconditioned residual norm error
1943 : !> \param nabs_error normalized absolute error
1944 : !> \par History
1945 : !> 07.2014 created [Hossein Bani-Hashemian]
1946 : !> 11.2015 revised [Hossein Bani-Hashemian]
1947 : !> \author Mohammad Hossein Bani-Hashemian
1948 : ! **************************************************************************************************
1949 268 : SUBROUTINE ps_implicit_compute_error_dct(pw_pool, green, res_new, v_old, v_new, &
1950 : QAinvxres_new, pres_error, nabs_error)
1951 :
1952 : TYPE(pw_pool_type), INTENT(IN), POINTER :: pw_pool
1953 : TYPE(greens_fn_type), INTENT(IN) :: green
1954 : TYPE(pw_r3d_rs_type), INTENT(IN) :: res_new, v_old, v_new
1955 : TYPE(pw_r3d_rs_type), INTENT(INOUT) :: QAinvxres_new
1956 : REAL(dp), INTENT(OUT) :: pres_error, nabs_error
1957 :
1958 : CHARACTER(LEN=*), PARAMETER :: routineN = 'ps_implicit_compute_error_dct'
1959 :
1960 : INTEGER :: handle
1961 : REAL(dp) :: vol
1962 :
1963 268 : CALL timeset(routineN, handle)
1964 :
1965 268 : vol = pw_pool%pw_grid%vol
1966 :
1967 : ! evaluate \Delta^-1(res) = \Delta^-1 (g - \Delta(v_new) - P(v_new) + Bt \lambda)
1968 268 : CALL apply_inv_laplace_operator_dct(pw_pool, green, res_new, QAinvxres_new)
1969 : ! (normalized) preconditioned residual norm error :
1970 119766668 : pres_error = accurate_sum(QAinvxres_new%array(:, :, :)**2)
1971 268 : CALL pw_pool%pw_grid%para%group%sum(pres_error)
1972 268 : pres_error = SQRT(pres_error)/vol
1973 :
1974 : ! normalized absolute error :
1975 : ! nabs_error := \frac{\| v_old - v_new \|}{volume}
1976 119766668 : nabs_error = accurate_sum(ABS(v_old%array - v_new%array)**2)
1977 268 : CALL pw_pool%pw_grid%para%group%sum(nabs_error)
1978 268 : nabs_error = SQRT(nabs_error)/vol
1979 :
1980 268 : CALL timestop(handle)
1981 :
1982 268 : END SUBROUTINE ps_implicit_compute_error_dct
1983 :
1984 : ! **************************************************************************************************
1985 : !> \brief output of the implicit (iterative) Poisson solver
1986 : !> \param iter current iteration
1987 : !> \param pres_error preconditioned residual norm error
1988 : !> \param nabs_error normalized absolute error
1989 : !> \param outp_unit output unit
1990 : !> \par History
1991 : !> 07.2014 created [Hossein Bani-Hashemian]
1992 : !> \author Mohammad Hossein Bani-Hashemian
1993 : ! **************************************************************************************************
1994 2522 : SUBROUTINE ps_implicit_output(iter, pres_error, nabs_error, outp_unit)
1995 :
1996 : INTEGER, INTENT(IN) :: iter
1997 : REAL(dp), INTENT(IN) :: pres_error, nabs_error
1998 : INTEGER, INTENT(OUT) :: outp_unit
1999 :
2000 : CHARACTER(LEN=*), PARAMETER :: routineN = 'ps_implicit_output'
2001 : INTEGER, PARAMETER :: low_print_level = 1
2002 :
2003 : INTEGER :: handle
2004 : TYPE(cp_logger_type), POINTER :: logger
2005 :
2006 2522 : CALL timeset(routineN, handle)
2007 :
2008 2522 : logger => cp_get_default_logger()
2009 2522 : IF (logger%para_env%is_source()) THEN
2010 1261 : outp_unit = cp_logger_get_default_unit_nr(logger, local=.TRUE.)
2011 : ELSE
2012 1261 : outp_unit = -1
2013 : END IF
2014 :
2015 2522 : IF (logger%iter_info%print_level > low_print_level) THEN
2016 2476 : IF ((outp_unit > 0) .AND. (iter == 1)) THEN
2017 : WRITE (outp_unit, '(T3,A)') &
2018 226 : "POISSON| iter pres error nabs error E_hartree delta E"
2019 : END IF
2020 :
2021 2476 : IF (outp_unit > 0) THEN
2022 : WRITE (outp_unit, '(T3,A,I6,5X,E13.4,3X,E13.4)', ADVANCE='NO') &
2023 1238 : "POISSON| ", iter, pres_error, nabs_error
2024 : END IF
2025 : END IF
2026 :
2027 2522 : CALL timestop(handle)
2028 :
2029 2522 : END SUBROUTINE ps_implicit_output
2030 :
2031 : ! **************************************************************************************************
2032 : !> \brief reports the Hartree energy in every iteration
2033 : !> \param ps_implicit_env the implicit poisson solver environment
2034 : !> \param outp_unit output unit
2035 : !> \param ehartree Hartree energy
2036 : !> \par History
2037 : !> 07.2014 created [Hossein Bani-Hashemian]
2038 : !> \author Mohammad Hossein Bani-Hashemian
2039 : ! **************************************************************************************************
2040 5044 : SUBROUTINE ps_implicit_report_ehartree(ps_implicit_env, outp_unit, ehartree)
2041 :
2042 : TYPE(ps_implicit_type) :: ps_implicit_env
2043 : INTEGER, INTENT(IN) :: outp_unit
2044 : REAL(dp), INTENT(IN) :: ehartree
2045 :
2046 : CHARACTER(LEN=*), PARAMETER :: routineN = 'ps_implicit_report_ehartree'
2047 : INTEGER, PARAMETER :: low_print_level = 1
2048 :
2049 : INTEGER :: handle
2050 : TYPE(cp_logger_type), POINTER :: logger
2051 :
2052 2522 : logger => cp_get_default_logger()
2053 2522 : CALL timeset(routineN, handle)
2054 2522 : IF (logger%iter_info%print_level > low_print_level) THEN
2055 2476 : IF (outp_unit > 0) WRITE (outp_unit, '(F19.10,E10.2)') &
2056 1238 : ehartree, ehartree - ps_implicit_env%ehartree
2057 : END IF
2058 2522 : CALL timestop(handle)
2059 :
2060 2522 : END SUBROUTINE ps_implicit_report_ehartree
2061 :
2062 : ! **************************************************************************************************
2063 : !> \brief reports the final number of iteration
2064 : !> \param iter the iteration number after exiting the main loop
2065 : !> \param max_iter maximum number of iterations
2066 : !> \param outp_unit output unit
2067 : !> \par History
2068 : !> 02.2016 created [Hossein Bani-Hashemian]
2069 : !> \author Mohammad Hossein Bani-Hashemian
2070 : ! **************************************************************************************************
2071 498 : SUBROUTINE ps_implicit_print_convergence_msg(iter, max_iter, outp_unit)
2072 :
2073 : INTEGER, INTENT(IN) :: iter, max_iter, outp_unit
2074 :
2075 : CHARACTER(LEN=*), PARAMETER :: routineN = 'ps_implicit_print_convergence_msg'
2076 :
2077 : CHARACTER(LEN=12) :: msg
2078 : INTEGER :: handle, last_iter
2079 :
2080 498 : CALL timeset(routineN, handle)
2081 :
2082 498 : last_iter = iter - 1
2083 :
2084 498 : IF (outp_unit > 0) THEN
2085 249 : IF (last_iter == max_iter) THEN
2086 : WRITE (outp_unit, '(T3,A)') &
2087 0 : "POISSON| No convergence achieved within the maximum number of iterations."
2088 : END IF
2089 249 : IF (last_iter < max_iter) THEN
2090 249 : IF (last_iter == 1) THEN
2091 117 : msg = " iteration."
2092 : ELSE
2093 132 : msg = " iterations."
2094 : END IF
2095 : WRITE (outp_unit, '(T3,A,I0,A)') &
2096 249 : "POISSON| Poisson solver converged in ", last_iter, msg
2097 : END IF
2098 : END IF
2099 498 : CALL timestop(handle)
2100 :
2101 498 : END SUBROUTINE ps_implicit_print_convergence_msg
2102 :
2103 : ! **************************************************************************************************
2104 : !> \brief converts a 1D array to a 3D array (contiguous layout)
2105 : !> \param idx_1dto3d mapping of indices
2106 : !> \param arr1d input 1D array
2107 : !> \param arr3d input 3D array
2108 : ! **************************************************************************************************
2109 2062 : SUBROUTINE convert_1dto3d(idx_1dto3d, arr1d, arr3d)
2110 :
2111 : INTEGER, DIMENSION(:), INTENT(IN) :: idx_1dto3d
2112 : REAL(dp), DIMENSION(:), INTENT(IN) :: arr1d
2113 : REAL(dp), ALLOCATABLE, DIMENSION(:, :, :), &
2114 : INTENT(INOUT) :: arr3d
2115 :
2116 : CHARACTER(LEN=*), PARAMETER :: routineN = 'convert_1dto3d'
2117 :
2118 : INTEGER :: handle, i, j, k, l, lb1, lb2, lb3, &
2119 : npts1, npts2, npts3, ub1, ub2, ub3
2120 :
2121 2062 : CALL timeset(routineN, handle)
2122 :
2123 2062 : lb1 = LBOUND(arr3d, 1); ub1 = UBOUND(arr3d, 1)
2124 2062 : lb2 = LBOUND(arr3d, 2); ub2 = UBOUND(arr3d, 2)
2125 2062 : lb3 = LBOUND(arr3d, 3); ub3 = UBOUND(arr3d, 3)
2126 :
2127 2062 : npts1 = ub1 - lb1 + 1
2128 2062 : npts2 = ub2 - lb2 + 1
2129 2062 : npts3 = ub3 - lb3 + 1
2130 :
2131 363318274 : DO l = 1, SIZE(idx_1dto3d)
2132 363316212 : k = ((idx_1dto3d(l) - 1)/(npts1*npts2)) + lb3
2133 363316212 : j = ((idx_1dto3d(l) - 1) - (k - lb3)*npts1*npts2)/npts1 + lb2
2134 363316212 : i = idx_1dto3d(l) - ((j - lb2)*npts1 + (k - lb3)*npts1*npts2) + lb1 - 1
2135 363318274 : arr3d(i, j, k) = arr1d(l)
2136 : END DO
2137 :
2138 2062 : CALL timestop(handle)
2139 :
2140 2062 : END SUBROUTINE convert_1dto3d
2141 :
2142 : ! **************************************************************************************************
2143 : !> \brief Returns the voltage of a tile. In case an alternating field is used, the oltage is a function of time
2144 : !> \param time ...
2145 : !> \param v_D ...
2146 : !> \param osc_frac ...
2147 : !> \param frequency ...
2148 : !> \param phase ...
2149 : !> \param v_D_new ...
2150 : ! **************************************************************************************************
2151 324 : SUBROUTINE get_voltage(time, v_D, osc_frac, frequency, phase, v_D_new)
2152 :
2153 : REAL(dp), INTENT(IN) :: time
2154 : REAL(dp), DIMENSION(:), INTENT(IN) :: v_D, osc_frac, frequency, phase
2155 : REAL(dp), ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: v_D_new
2156 :
2157 : CHARACTER(LEN=*), PARAMETER :: routineN = 'get_voltage'
2158 :
2159 : INTEGER :: handle, i
2160 :
2161 324 : CALL timeset(routineN, handle)
2162 :
2163 972 : ALLOCATE (v_D_new(SIZE(v_D)))
2164 :
2165 2256 : DO i = 1, SIZE(v_D)
2166 : v_D_new(i) = v_D(i)*(1 - osc_frac(i)) + &
2167 2256 : v_D(i)*osc_frac(i)*COS(2*pi*time*frequency(i) + phase(i))
2168 : END DO
2169 :
2170 324 : CALL timestop(handle)
2171 :
2172 324 : END SUBROUTINE get_voltage
2173 :
2174 : END MODULE ps_implicit_methods
|