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