MPI-SCATCI  2.0
An MPI version of SCATCI
Postprocessing_module.F90
Go to the documentation of this file.
1 ! Copyright 2019
2 !
3 ! For a comprehensive list of the developers that contributed to these codes
4 ! see the UK-AMOR website.
5 !
6 ! This file is part of UKRmol-in (UKRmol+ suite).
7 !
8 ! UKRmol-in is free software: you can redistribute it and/or modify
9 ! it under the terms of the GNU General Public License as published by
10 ! the Free Software Foundation, either version 3 of the License, or
11 ! (at your option) any later version.
12 !
13 ! UKRmol-in is distributed in the hope that it will be useful,
14 ! but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ! GNU General Public License for more details.
17 !
18 ! You should have received a copy of the GNU General Public License
19 ! along with UKRmol-in (in source/COPYING). Alternatively, you can also visit
20 ! <https://www.gnu.org/licenses/>.
21 
33 
34  use blas_lapack_gbl, only: blasint
35  use cdenprop_defs, only: civect
36  use precisn, only: shortint, wp
37 
38  implicit none
39 
40  private
41 
42  public postprocess
43 
44  ! data types used by RMT and expected in the 'molecular_data' file
45  integer, parameter :: rmt_int = shortint
46  integer, parameter :: rmt_real = wp
47 
56  integer :: maxchan = 10000
57  integer :: maxnmo
58  integer :: ntarg
59  integer :: nchan
60  integer :: ismax
61 
62  integer :: nfdm
63  real(wp) :: rmatr
64 
65  real(wp), allocatable :: r_points(:)
66  type(civect), allocatable :: wamp(:)
67 
68  integer, allocatable :: idtarg(:)
69  integer, allocatable :: irrchl(:)
70  integer, allocatable :: ichl(:)
71  integer, allocatable :: lchl(:)
72  integer, allocatable :: mchl(:)
73  integer, allocatable :: qchl(:)
74  real(wp), allocatable :: echl(:)
75  real(wp), allocatable :: a(:,:)
76  contains
77  procedure, public :: init => init_outer_interface
78  procedure, public :: deinit => deinit_outer_interface
79  procedure, public :: setup_amplitudes
80 
81  procedure, public :: extract_data
82  procedure, private :: get_channel_info
83  procedure, private :: get_channel_couplings
84  procedure, private :: get_boundary_data
85 
86  procedure, public :: write_data
87  procedure, private :: write_channel_info
88  procedure, private :: write_boundary_data
89  end type outerinterface
90 
91 contains
92 
103  subroutine postprocess (SCATCI_input, solutions)
104 
105  use class_molecular_properties_data, only: molecular_properties_data
106  use options_module, only: optionsset
108 
109  type(optionsset), intent(in) :: scatci_input
110  type(solutionhandler), allocatable, intent(inout) :: solutions(:)
111 
112  type(molecular_properties_data) :: properties_all
113 
114  call redistribute_solutions(scatci_input, solutions)
115  call cdenprop_properties(scatci_input, solutions, properties_all)
116  call outer_interface(scatci_input, solutions, properties_all)
117 
118  end subroutine postprocess
119 
120 
132  subroutine redistribute_solutions (SCATCI_input, solutions)
133 
135  use mpi_gbl, only: mpiint, myrank, mpi_mod_bcast
136  use options_module, only: optionsset
138  use parallelization_module, only: grid => process_grid
139 
140  type(optionsset), intent(in) :: SCATCI_input
141  type(solutionhandler), allocatable, intent(inout) :: solutions(:)
142 
143  type(solutionhandler) :: oldsolution
144  integer(mpiint) :: srcrank
145  integer :: i, nnuc, nstat
146 
147  ! nothing to do when the solutions are already uniformly distributed everywhere
148  if (grid % sequential) return
149 
150  ! set up the uniformly distributed solutions
151  do i = 1, size(scatci_input % opts)
152  if (iand(scatci_input % opts(i) % vector_storage_method, pass_to_cdenprop) /= 0 .and. &
153  scatci_input % opts(i) % diagonalization_flag /= no_diagonalization) then
154 
155  ! 1. Share metadata for the solutions from the master of group that found that solution to everyone.
156 
157  srcrank = grid % group_master_world_rank(grid % which_group_is_work(i))
158 
159  call mpi_mod_bcast(solutions(i) % core_energy, srcrank)
160  call mpi_mod_bcast(solutions(i) % vec_dimen, srcrank)
161 
162  call mpi_mod_bcast(solutions(i) % ci_vec % nstat, srcrank)
163  call mpi_mod_bcast(solutions(i) % ci_vec % mgvn, srcrank)
164  call mpi_mod_bcast(solutions(i) % ci_vec % s, srcrank)
165  call mpi_mod_bcast(solutions(i) % ci_vec % sz, srcrank)
166  call mpi_mod_bcast(solutions(i) % ci_vec % nnuc, srcrank)
167  call mpi_mod_bcast(solutions(i) % ci_vec % e0, srcrank)
168  call mpi_mod_bcast(solutions(i) % ci_vec % CV_is_scalapack, srcrank)
169  call mpi_mod_bcast(solutions(i) % ci_vec % mat_dimen_r, srcrank)
170  call mpi_mod_bcast(solutions(i) % ci_vec % mat_dimen_c, srcrank)
171 
172  nnuc = solutions(i) % ci_vec % nnuc
173  nstat = solutions(i) % ci_vec % nstat
174 
175  if (.not. allocated(solutions(i) % ci_vec % ei)) then
176  allocate (solutions(i) % ci_vec % ei(nstat))
177  end if
178 
179  call mpi_mod_bcast(solutions(i) % ci_vec % cname(1:nnuc), srcrank)
180  call mpi_mod_bcast(solutions(i) % ci_vec % charge(1:nnuc), srcrank)
181  call mpi_mod_bcast(solutions(i) % ci_vec % xnuc(1:nnuc), srcrank)
182  call mpi_mod_bcast(solutions(i) % ci_vec % ynuc(1:nnuc), srcrank)
183  call mpi_mod_bcast(solutions(i) % ci_vec % znuc(1:nnuc), srcrank)
184  call mpi_mod_bcast(solutions(i) % ci_vec % ei(1:nstat), srcrank)
185 
186  ! 2. Backup the original solutions, which are distributed over the MPI group.
187 
188  oldsolution = solutions(i)
189 
190  if (.not. grid % is_my_group_work(i)) then
191  oldsolution % ci_vec % blacs_context = -1
192  oldsolution % ci_vec % descr_CV_mat = -1
193  end if
194 
195  ! 3. Redistribute the eigenvectors over whole MPI world.
196 
197  solutions(i) % ci_vec % blacs_context = grid % wcntxt
198  solutions(i) % ci_vec % nprow = grid % wprows
199  solutions(i) % ci_vec % npcol = grid % wpcols
200 
201  call solutions(i) % ci_vec % init_CV(int(oldsolution % ci_vec % mat_dimen_r), &
202  int(oldsolution % ci_vec % mat_dimen_c))
203  call solutions(i) % ci_vec % redistribute(oldsolution % ci_vec)
204 
205  end if
206  end do
207 
208  end subroutine redistribute_solutions
209 
210 
225  subroutine cdenprop_properties (SCATCI_input, solutions, properties_all)
226 
227  use class_molecular_properties_data, only: molecular_properties_data
228  use class_namelists, only: cdenprop_namelists
230  use cdenprop_procs, only: cdenprop_drv
231  use cdenprop_defs, only: ir_max
232  use mpi_gbl, only: myrank, master, mpi_reduceall_sum_cfp
233  use options_module, only: optionsset
235 
236  type(optionsset), intent(in) :: SCATCI_input
237  type(solutionhandler), allocatable, intent(inout) :: solutions(:)
238  type(molecular_properties_data), intent(inout) :: properties_all
239 
240  type(cdenprop_namelists) :: namelist_ij
241  type(civect) :: oldblock
242 
243  integer :: i, j, ntgsym, ninputs, maxpole, nblock, mxstat
244 
245  ninputs = size(scatci_input % opts)
246  maxpole = 2
247 
248  ! for now, skip evaluation of properties if RMT data not required
249  if (.not. scatci_input % write_dip .and. .not. scatci_input % write_rmt) return
250 
251  ! prepare free slots for the distributed dense property matrices
252  call properties_all % clean
253  call properties_all % preallocate_property_blocks(ninputs**2 * (maxpole + 1)**2)
254 
255  ! calculate properties for all pairs of symmetries
256  sym_i_loop: do i = 1, ninputs
257  if (iand(scatci_input % opts(i) % vector_storage_method, pass_to_cdenprop) /= 0 .and. &
258  scatci_input % opts(i) % diagonalization_flag /= no_diagonalization) then
259 
260  sym_j_loop: do j = i, ninputs
261  if (iand(scatci_input % opts(j) % vector_storage_method, pass_to_cdenprop) /= 0 .and. &
262  scatci_input % opts(j) % diagonalization_flag /= no_diagonalization) then
263 
264  ! only calculate self-overlaps when required
265  if (i == j .and. .not. scatci_input % all_props) cycle
266 
267  ! virtual CDENPROP namelist
268  call namelist_ij % init(2)
269 
270  ! assign values obtained from SCATCI namelists
271  namelist_ij % lucsf(1:2) = (/ scatci_input % opts(i) % megul, &
272  scatci_input % opts(j) % megul /)
273  namelist_ij % nciset(1:2) = (/ scatci_input % opts(i) % output_ci_set_number, &
274  scatci_input % opts(j) % output_ci_set_number /)
275  namelist_ij % nstat = 0 ! use all states
276  namelist_ij % lupropw = 0 ! no output needed now, will be done later
277  namelist_ij % lucivec = 0 ! no CI units needed, will be given as arguments
278  namelist_ij % ukrmolp_ints = scatci_input % opts(i) % use_UKRMOL_integrals
279  namelist_ij % luprop = merge(scatci_input % opts(i) % integral_unit, &
280  scatci_input % luprop, &
281  scatci_input % luprop < 0)
282 
283  ntgsym = scatci_input % opts(i) % num_target_sym
284 
285  ! following depend on whether this is target or scattering calculation
286  if (scatci_input % opts(i) % ci_target_flag == no_ci_target) then
287  namelist_ij % numtgt(1:ir_max) = 0
288  namelist_ij % max_multipole = 2 ! dipole + quadrupole
289  else
290  namelist_ij % max_multipole = 1 ! dipole only
291  namelist_ij % numtgt(1:ir_max) = 0
292  namelist_ij % numtgt(1:ntgsym) = scatci_input % opts(i) % num_target_state_sym(1:ntgsym)
293  end if
294 
295  ! use BLACS context of the first vector for communication (they are all of the same shape anyway)
296  solutions(j) % ci_vec % blacs_context = solutions(i) % ci_vec % blacs_context
297  solutions(j) % ci_vec % descr_CV_mat(2) = solutions(i) % ci_vec % descr_CV_mat(2)
298 
299  ! get the properties in CDENPROP
300  call cdenprop_drv(solutions(i) % ci_vec, solutions(j) % ci_vec, namelist_ij, properties_all)
301 
302  ! cleanup
303  call namelist_ij % dealloc
304 
305  end if
306  end do sym_j_loop
307 
308  end if
309  end do sym_i_loop
310 
311  ! prefer SWINTERF-compatible format of the property file (so that it can be, e.g., read back to CDENPROP_ALL)
312  properties_all % swintf_format = .true.
313 
314  ! write calculated properties
315  if (scatci_input % write_dip .and. properties_all % no_symmetries > 0) then
316  if (scatci_input % all_props) call properties_all % sort_by_energy
317  call properties_all % write_properties(624, scatci_input % opts(1) % use_UKRMOL_integrals)
318  end if
319 
320  ! inflate all property matrices to uniform size for easy output for RMT, which wants it that way
321  if (scatci_input % write_rmt) then
322  nblock = properties_all % no_blocks
323  mxstat = max(maxval(properties_all % dense_blocks(1:nblock) % mat_dimen_r), &
324  maxval(properties_all % dense_blocks(1:nblock) % mat_dimen_c))
325  do i = 1, nblock
326  associate(d => properties_all % dense_blocks(i))
327  if (d % mat_dimen_r /= mxstat .or. d % mat_dimen_c /= mxstat) then
328  oldblock = d
329  call d % init_CV(mxstat, mxstat)
330  d % CV = 0
331  call d % redistribute(oldblock)
332  call oldblock % final_CV
333  end if
334  end associate
335  end do
336  end if
337 
338  end subroutine cdenprop_properties
339 
340 
357  subroutine outer_interface (SCATCI_input, solutions, inner_properties)
358 
359  use class_molecular_properties_data, only: molecular_properties_data
361  use options_module, only: optionsset
363 
364  type(optionsset), intent(in) :: SCATCI_input
365  type(solutionhandler), allocatable, intent(in) :: solutions(:)
366  type(molecular_properties_data), intent(in) :: inner_properties
367 
368  type(molecular_properties_data) :: target_properties
369  type(outerinterface), allocatable :: intf(:)
370 
371  integer :: i, nsym, nset
372 
373  nset = 0 ! number of sets in the output files fort.10 and fort.21
374  nsym = size(scatci_input % opts) ! number of inputs (symmetries) that we dealt with
375 
376  if (.not. scatci_input % write_amp .and. .not. scatci_input % write_rmt) return
377 
378  allocate (intf(nsym))
379 
380  call target_properties % read_properties(24, 1)
381 
382  ! run SWITERF for all symmetries
383  do i = 1, nsym
384  call intf(i) % init(scatci_input)
385  if (iand(scatci_input % opts(i) % vector_storage_method, pass_to_cdenprop) /= 0 .and. &
386  scatci_input % opts(i) % diagonalization_flag /= no_diagonalization) then
387  nset = nset + 1
388  call intf(i) % setup_amplitudes(scatci_input % opts(i))
389  call intf(i) % extract_data(scatci_input % opts(i), target_properties, solutions(i))
390  if (scatci_input % write_amp) then
391  call intf(i) % write_data(i, scatci_input, target_properties, solutions(i))
392  end if
393  end if
394  end do
395 
396  if (scatci_input % write_rmt) then
397  call write_rmt_data(scatci_input, inner_properties, target_properties, solutions, intf)
398  end if
399 
400  end subroutine outer_interface
401 
402 
410  subroutine init_outer_interface (this, input)
411 
412  use mpi_gbl, only: mpi_xermsg
413  use options_module, only: optionsset
414 
415  class(outerinterface), intent(inout) :: this
416  type(optionsset), intent(in) :: input
417 
418  integer :: i, ierr
419 
420  this % ntarg = 0
421  this % nchan = 0
422  this % ismax = 0
423  this % maxnmo = 0
424 
425  this % nfdm = input % nfdm
426  this % rmatr = input % rmatr
427 
428  allocate (this % r_points(this % nfdm + 1))
429 
430  do i = 1, this % nfdm + 1
431  this % r_points(i) = this % rmatr - (this % nfdm + 1 - i) * input % delta_r
432  end do
433 
434  allocate (this % irrchl(this % maxchan), &
435  this % ichl(this % maxchan), &
436  this % lchl(this % maxchan), &
437  this % mchl(this % maxchan), &
438  this % qchl(this % maxchan), &
439  this % echl(this % maxchan), stat = ierr)
440 
441  if (ierr /= 0) then
442  call mpi_xermsg('OuterInterface', 'init_outer_interface', 'Failed to allocate memory for channel data.', 1, 1)
443  end if
444 
445  if (allocated(input % idtarg)) then
446  this % ntarg = size(input % idtarg)
447  this % idtarg = input % idtarg
448  end if
449 
450  end subroutine init_outer_interface
451 
452 
459  subroutine deinit_outer_interface (this)
460 
461  class(outerinterface), intent(inout) :: this
462 
463  integer :: i
464 
465  ! release the no-longer-needed BLACS context
466  if (allocated(this % wamp)) then
467  do i = 1, size(this % wamp)
468  call this % wamp(i) % final_CV
469 #if defined(usempi) && defined(scalapack)
470  if (i == 1 .and. this % wamp(this % nfdm + 1) % blacs_context >= 0) then
471  call blacs_gridexit(this % wamp(this % nfdm + 1) % blacs_context)
472  end if
473 #endif
474  end do
475  deallocate (this % wamp)
476  end if
477 
478  if (allocated(this % irrchl)) deallocate (this % irrchl)
479  if (allocated(this % ichl)) deallocate (this % ichl)
480  if (allocated(this % lchl)) deallocate (this % lchl)
481  if (allocated(this % mchl)) deallocate (this % mchl)
482  if (allocated(this % qchl)) deallocate (this % qchl)
483  if (allocated(this % echl)) deallocate (this % echl)
484  if (allocated(this % a)) deallocate (this % a)
485  if (allocated(this % r_points)) deallocate (this % r_points)
486 
487  this % nchan = 0
488  this % ismax = 0
489  this % maxnmo = 0
490  this % nfdm = 0
491 
492  end subroutine deinit_outer_interface
493 
494 
502  subroutine setup_amplitudes (this, opts)
503 
504  use options_module, only: options
505  use ukrmol_interface_gbl, only: read_ukrmolp_basis, eval_amplitudes
506 
507  class(outerinterface), intent(inout) :: this
508  type(options), intent(in) :: opts
509 
510  if (opts % use_UKRMOL_integrals) then
511  call read_ukrmolp_basis(opts % integral_unit)
512  call eval_amplitudes(this % rmatr, .true.)
513  end if
514 
515  end subroutine setup_amplitudes
516 
517 
534  subroutine extract_data (this, opts, prop, solution)
535 
536  use class_molecular_properties_data, only: molecular_properties_data
537  use options_module, only: options
539 
540  class(outerinterface), intent(inout) :: this
541  type(molecular_properties_data), intent(in) :: prop
542  type(options), intent(in) :: opts
543  type(solutionhandler), intent(in) :: solution
544 
545  integer, parameter :: ismax = 2
546  real(wp), parameter :: alpha0 = 0
547  real(wp), parameter :: alpha2 = 0
548 
549  logical :: use_pol
550  integer :: lamax
551 
552  lamax = ismax
553  use_pol = .false. ! if set to .true., then channel_couplings will take into account the polarizability
554 
555  if (alpha0 /= 0 .or. alpha2 /= 0) then
556  use_pol = .true.
557  lamax = max(ismax, 3) ! polarizability (1/r^4) corresponds to lambda = 3
558  end if
559 
560  this % ismax = lamax
561 
562  call this % get_channel_info(opts, prop)
563  call this % get_channel_couplings(lamax, prop % no_states, prop, alpha0, alpha2, use_pol)
564  call this % get_boundary_data(opts, prop, solution)
565 
566  ! redefine magnetic quantum number by multiplying in the projectile charge
567  this % mchl(1:this % nchan) = this % mchl(1:this % nchan) * this % qchl(1:this % nchan)
568 
569  end subroutine extract_data
570 
571 
584  subroutine write_data (this, i, opts, prop, solution)
585 
586  use class_molecular_properties_data, only: molecular_properties_data
587  use mpi_gbl, only: myrank, master
588  use options_module, only: optionsset
590 
591  class(outerinterface), intent(in) :: this
592  type(molecular_properties_data), intent(in) :: prop
593  type(optionsset), intent(in) :: opts
594  type(solutionhandler), intent(in) :: solution
595  integer, intent(in) :: i
596 
597  if (myrank == master) then
598  call this % write_channel_info(i, opts % opts(i), opts % cform, prop)
599  call this % write_boundary_data(i, opts % opts(i), opts % rform, prop, solution)
600  end if
601 
602  end subroutine write_data
603 
604 
616  subroutine get_channel_info (this, opts, prop)
617 
618  use algorithms, only: findloc
619  use class_molecular_properties_data, only: molecular_properties_data
620  use const_gbl, only: stdout
621  use global_utils, only: mprod
622  use mpi_gbl, only: mpi_xermsg
623  use options_module, only: options
624  use precisn, only: wp
625  use ukrmol_interface_gbl, only: ukp_preamp
626 
627  class(outerinterface), intent(inout) :: this
628  type(options), intent(in) :: opts
629  type(molecular_properties_data), intent(in) :: prop
630 
631  integer :: i, j, k, mgvn, spin, tgt_stat, tgt_symm, tgt_mgvn, tgt_spin, nch, prj_mgvn, congen_tgt_id, denprop_tgt_id, isym
632  real(wp) :: tgt_enrg, ebase
633 
634  ebase = prop % energies(1)
635  mgvn = opts % lambda
636  spin = nint(2 * opts % spin + 1)
637 
638  this % maxnmo = sum(opts % num_orbitals_sym)
639  this % nchan = 0
640 
641  ! construct default IDTARG, i.e. denprop (energy-order) index for each congen (symmetry-order) target state
642  if (.not. allocated(this % idtarg)) then
643  this % ntarg = opts % total_num_target_states
644  allocate (this % idtarg(this % ntarg))
645  congen_tgt_id = 0
646  congen_symm_loop: do j = 1, size(opts % num_target_state_sym)
647  congen_targ_loop: do k = 1, opts % num_target_state_sym(j)
648  congen_tgt_id = congen_tgt_id + 1
649  denprop_tgt_id = 0
650  tgt_mgvn = opts % target_spatial(congen_tgt_id)
651  tgt_spin = opts % target_multiplicity(congen_tgt_id)
652  denprop_targ_loop: do i = 1, this % ntarg
653  isym = prop % energies_index(2, i)
654  if (prop % symmetries_index(1, isym) == tgt_mgvn .and. &
655  prop % symmetries_index(2, isym) == tgt_spin) then
656  denprop_tgt_id = denprop_tgt_id + 1
657  end if
658  if (denprop_tgt_id == k) then
659  this % idtarg(congen_tgt_id) = i
660  exit denprop_targ_loop
661  end if
662  end do denprop_targ_loop
663  end do congen_targ_loop
664  end do congen_symm_loop
665  end if
666 
667  write (stdout, '(/,1x,"Target state indices sorted by energy (IDTARG): ")')
668  write (stdout, '( 1x,20I4)') this % idtarg
669 
670  do i = 1, prop % no_states
671 
672  ! skip states not referenced in IDTARG
673  if (findloc(this % idtarg, i, 1) == 0) cycle
674 
675  ! retrieve target data from the properties structure
676  tgt_stat = prop % energies_index(1, i)
677  tgt_symm = prop % energies_index(2, i)
678  tgt_mgvn = prop % symmetries_index(1, tgt_symm)
679  tgt_spin = prop % symmetries_index(2, tgt_symm)
680  tgt_enrg = prop % energies(i)
681 
682  ! angular momentum composition rule -- must allow for projectile spin
683  if (abs(spin - tgt_spin) /= 1) cycle
684 
685  ! get projectile total symmetry IRR from the multiplication table
686  prj_mgvn = mprod(tgt_mgvn + 1, mgvn + 1, 0, stdout)
687 
688  ! retrieve channel descriptions from the integral library
689  if (opts % use_UKRMOL_integrals) then
690  call ukp_preamp(prj_mgvn, this % nchan + 1, this % lchl, this % mchl, this % qchl, nch, this % maxnmo)
691  else
692  call mpi_xermsg('Postprocessing_module', 'get_channel_info', &
693  'Interface for SWINTERF integrals not implemented.', 1, 1)
694  end if
695 
696  ! fill in the remaining channel identifiers
697  do j = 1, nch
698  this % ichl(this % nchan + j) = tgt_stat
699  this % echl(this % nchan + j) = 2 * (tgt_enrg - ebase)
700  this % irrchl(this % nchan + j) = prj_mgvn
701  end do
702 
703  ! update total number of channels
704  this % nchan = this % nchan + nch
705 
706  end do
707 
708  write (stdout, '(/," Channel Target l m q Irr Energy Energy(eV)")')
709 
710  do i = 1, this % nchan
711  write (stdout, '(I5,I8,I5,2I3,2x,A4,I3,2F10.6)') i, this % ichl(i), this % lchl(i), this % mchl(i), this % qchl(i), &
712  'N/A', this % irrchl(i), this % echl(i), this % echl(i) / 0.073500d0
713  end do
714 
715  end subroutine get_channel_info
716 
717 
734  subroutine get_boundary_data (this, opts, prop, solution)
735 
736  use class_molecular_properties_data, only: molecular_properties_data
737  use const_gbl, only: stdout
738  use global_utils, only: mprod
739  use mpi_gbl, only: master, myrank
740  use options_module, only: options
742  use ukrmol_interface_gbl, only: eval_amplitudes, ukp_readamp
743 
744  class(outerinterface), intent(inout) :: this
745  type(options), intent(in) :: opts
746  type(molecular_properties_data), intent(in) :: prop
747  type(solutionhandler), intent(in) :: solution
748 
749  type(civect) :: orb_amps, dist_orb_amps, sol_amps
750 
751  integer, allocatable :: mcont(:), idchl(:)
752  real(wp), allocatable :: ampls(:)
753 
754  integer :: i, j, k, ir, ncontmo(8), iprnt = 0, nchan, nstat, ierr, nnoncontmo, norbs, morbs, ncnt, offs, nvo(8) = 0
755 
756  nchan = this % nchan
757  nstat = solution % ci_vec % nstat
758  morbs = maxval(opts % num_orbitals_sym) ! safe upper bound on maximal number of continuum orbitals per channel...
759  norbs = dot_product(opts % num_target_state_sym, opts % num_continuum_orbitals_target) ! ... and total
760 
761  allocate(ampls(morbs), idchl(nchan))
762 
763  ! set up energy order channel map
764  k = 0
765  do j = 1, this % ntarg
766  do i = 1, nchan
767  if (this % ichl(i) == this % idtarg(j)) then
768  k = k + 1
769  idchl(k) = i
770  end if
771  end do
772  end do
773 
774  write (stdout, '(/,1x,"Channel target state energy map (IDCHL): ")')
775  write (stdout, '( 1x,20I4)') idchl
776 
777  ! set up storage for the raw boundary amplitudes (master-only ScaLAPACK matrix)
778  orb_amps % nprow = 1
779  orb_amps % npcol = 1
780  orb_amps % blacs_context = -1
781  orb_amps % CV_is_scalapack = solution % ci_vec % CV_is_scalapack
782 #if defined(usempi) && defined(scalapack)
783  if (orb_amps % CV_is_scalapack) then
784  call blacs_get(-1_blasint, 0_blasint, orb_amps % blacs_context)
785  call blacs_gridinit(orb_amps % blacs_context, 'R', 1_blasint, 1_blasint)
786  end if
787 #endif
788  call orb_amps % init_CV(nchan, norbs)
789 
790  ! set up storage for the evaluated boundary amplitudes
791  if (.not. allocated(this % wamp)) then
792  allocate (this % wamp(this % nfdm + 1))
793  end if
794 
795  ! most of them are distributed...
796  this % wamp(:) % nprow = solution % ci_vec % nprow
797  this % wamp(:) % npcol = solution % ci_vec % npcol
798  this % wamp(:) % blacs_context = solution % ci_vec % blacs_context
799  this % wamp(:) % CV_is_scalapack = solution % ci_vec % CV_is_scalapack
800 
801  ! ...but the last is master-only (needed for write_boundary_data)
802  this % wamp(this % nfdm + 1) % nprow = 1
803  this % wamp(this % nfdm + 1) % npcol = 1
804  this % wamp(this % nfdm + 1) % blacs_context = orb_amps % blacs_context
805 
806  ! allocate the matrices
807  do ir = 1, this % nfdm + 1
808  call this % wamp(ir) % init_CV(nchan, nstat)
809  end do
810 
811  ! for all evaluation radii
812  do ir = 1, this % nfdm + 1
813 
814  ! get raw boundary amplitudes of the continuum orbitals
815  if (myrank == master) then
816  ! retrieve orbital amplitudes from the integral library
817  call eval_amplitudes(this % r_points(ir), .false.)
818  call ukp_readamp(orb_amps % CV, this % nchan, this % irrchl, this % lchl, this % mchl, this % qchl, ncontmo, &
819  opts % lambda_continuum_orbitals_target, iprnt)
820 
821  ! re-align the orbital amplitude data to their absolute positions in the list of all virt+cont orbitals:
822  !
823  ! 1st target used in channels...........2nd target used in channels...........etc....
824  ! orb_amps: [virtual orbitals][continuum orbitals][virtual orbitals][continuum orbitals]etc...
825  offs = 0
826  do i = 1, nchan
827  j = idchl(i)
828  if (i > 1) then
829  if (this % ichl(j) /= this % ichl(idchl(i - 1))) then
830  offs = offs + ncnt + nvo(this % irrchl(j))
831  end if
832  end if
833  ncnt = ncontmo(this % irrchl(j))
834  ampls(1:ncnt) = orb_amps % CV(j, 1:ncnt)
835  orb_amps % CV(j, 1:norbs) = 0
836  orb_amps % CV(j, offs + 1:offs + ncnt) = ampls(1:ncnt)
837  end do
838  end if
839 
840  ! multiply the raw boundary amplitudes with the eigenvectors
841  if (.not. solution % ci_vec % CV_is_scalapack) then
842  call this % wamp(ir) % A_B_matmul(orb_amps, solution % ci_vec, 'N', 'N')
843  else
844  ! distribute raw boundary amplitudes over solution context
845  dist_orb_amps % nprow = solution % ci_vec % nprow
846  dist_orb_amps % npcol = solution % ci_vec % npcol
847  dist_orb_amps % blacs_context = solution % ci_vec % blacs_context
848  dist_orb_amps % CV_is_scalapack = solution % ci_vec % CV_is_scalapack
849  call dist_orb_amps % init_CV(nchan, norbs)
850  call dist_orb_amps % redistribute(orb_amps)
851 
852  ! multiply distributed matrices
853  if (ir < this % nfdm + 1) then
854  call this % wamp(ir) % A_B_matmul(dist_orb_amps, solution % ci_vec, 'N', 'N')
855  else
856  ! this is needed by write_boundary_data, which cannot use distributed array
857  sol_amps % nprow = solution % ci_vec % nprow
858  sol_amps % npcol = solution % ci_vec % npcol
859  sol_amps % blacs_context = solution % ci_vec % blacs_context
860  sol_amps % CV_is_scalapack = solution % ci_vec % CV_is_scalapack
861  call sol_amps % init_CV(nchan, nstat)
862  call sol_amps % A_B_matmul(dist_orb_amps, solution % ci_vec, 'N', 'N')
863  call this % wamp(ir) % redistribute(sol_amps, solution % ci_vec % blacs_context)
864  end if
865  end if
866 
867  ! change normalization convention to the RMT one
868  this % wamp(ir) % CV = sqrt(2.0_wp) * this % wamp(ir) % CV
869 
870  end do
871 
872  end subroutine get_boundary_data
873 
874 
888  subroutine write_channel_info (this, nchset, opts, cform, prop)
889 
890  use algorithms, only: findloc
891  use class_molecular_properties_data, only: molecular_properties_data
892  use consts, only: amu
893  use consts_mpi_ci, only: symtype_d2h
894  use options_module, only: options
895 
896  class(outerinterface), intent(in) :: this
897  integer, intent(in) :: nchset
898  type(options), intent(in) :: opts
899  character(len=1), intent(in) :: cform
900 
901  type(molecular_properties_data), intent(in) :: prop
902 
903  integer, parameter :: luchan = 10, keych = 10
904 
905  integer :: i, k, nvd, nrec, ninfo, ndata, nvib, ndis, stot, gutot, ntarg, ivtarg(1), iv(1), ion, mtarg, starg, gutarg, nnuc
906  integer :: isymm
907  real(wp) :: r, rmass, xmass, etarg
908 
909  nnuc = prop % no_nuclei
910  nvib = 0
911  ndis = 0
912  gutot = symtype_d2h ! to indicate polyatomic code
913  ion = nint(sum(prop % charge(1:nnuc))) - (opts % num_electrons - 1)
914  r = 0
915  ntarg = this % ntarg
916  stot = nint(2 * opts % spin + 1)
917  rmass = sum(1 / prop % mass(1:nnuc), prop % mass(1:nnuc) > 0)
918 
919  if (rmass > 0) then
920  rmass = amu / rmass
921  end if
922 
923  nvd = nvib + ndis
924  nrec = 3 + ntarg + this % nchan + nvd
925  ninfo = 1
926  ndata = nrec - ninfo
927 
928  if (cform == 'F') then
929  write (luchan, '(16I5)') keych, nchset, nrec, ninfo, ndata
930  write (luchan, '(A80)') opts % diag_name
931  write (luchan, '(16I5)') ntarg, nvib, ndis, this % nchan
932  write (luchan, '(4I5,2D20.12)') opts % lambda, stot, gutot, ion, r, rmass
933 
934  k = 0
935  do i = 1, prop % no_states
936  if (findloc(this % idtarg, i, 1) /= 0) then
937  k = k + 1
938  isymm = prop % energies_index(2, i)
939  mtarg = prop % symmetries_index(1, isymm)
940  starg = prop % symmetries_index(2, isymm)
941  gutarg = 0
942  etarg = prop % energies(i)
943  write (luchan, '(4I5,2D20.12)') k, mtarg, starg, gutarg, etarg
944  end if
945  end do
946  do i = 1, this % nchan
947  write (luchan, '(4I5,2D20.12)') i, this % ichl(i), this % lchl(i), this % mchl(i), this % echl(i)
948  end do
949  do i = 1, nvd
950  write (luchan, '(16I5)') i, ivtarg(i), iv(i)
951  end do
952  else
953  write (luchan) keych, nchset, nrec, ninfo, ndata
954  write (luchan) opts % diag_name
955  write (luchan) ntarg, nvib, ndis, this % nchan
956  write (luchan) opts % lambda, stot, gutot, ion, r, rmass
957 
958  k = 0
959  do i = 1, prop % no_states
960  if (findloc(this % idtarg, i, 1) > 0) then
961  k = k + 1
962  isymm = prop % energies_index(2, i)
963  mtarg = prop % symmetries_index(1, isymm)
964  starg = prop % symmetries_index(2, isymm)
965  gutarg = 0
966  etarg = prop % energies(i)
967  write (luchan) k, mtarg, starg, gutarg, etarg
968  end if
969  end do
970  do i = 1, this % nchan
971  write (luchan) i, this % ichl(i), this % lchl(i), this % mchl(i), this % echl(i)
972  end do
973  do i = 1, nvd
974  write (luchan) i, ivtarg(i), iv(i)
975  end do
976  end if
977 
978  end subroutine write_channel_info
979 
980 
1007  subroutine write_boundary_data (this, nrmset, opts, rform, prop, solution)
1008 
1009  use cdenprop_defs, only: amass
1010  use class_molecular_properties_data, only: molecular_properties_data
1011  use consts, only: amu
1012  use consts_mpi_ci, only: symtype_d2h
1013  use options_module, only: options
1015 
1016  class(outerinterface), intent(in) :: this
1017  integer, intent(in) :: nrmset
1018  type(options), intent(in) :: opts
1019  character(len=1), intent(in) :: rform
1020  type(molecular_properties_data), intent(in) :: prop
1021  type(solutionhandler), intent(in) :: solution
1022 
1023  integer, parameter :: keyrm = 11
1024  integer, parameter :: lurmt = 21
1025 
1026  integer :: i, j, k, nchsq, nrec, ninfo, ndata, ntarg, nvib, ndis, nchan, stot, gutot, ion, ibut, iex, nocsf, npole, nnuc
1027  integer :: ismax, nstat
1028  real(wp) :: r, rmass, rmatr
1029 
1030  nnuc = prop % no_nuclei
1031  iex = 0
1032  ibut = 0
1033  nocsf = 0
1034  npole = 0
1035  ismax = 2
1036  rmatr = this % rmatr
1037  ntarg = this % ntarg
1038  nstat = solution % ci_vec % nstat
1039  nvib = 0
1040  ndis = 0
1041  nchan = this % nchan
1042  stot = nint(2 * opts % spin + 1)
1043  gutot = symtype_d2h ! to indicate polyatomic code
1044  ion = nint(sum(prop % charge(1:nnuc))) - (opts % num_electrons - 1)
1045  r = 0
1046  nchsq = nchan * (nchan + 1) / 2
1047  nrec = 4
1048  rmass = sum(1 / prop % mass(1:nnuc), prop % mass(1:nnuc) > 0)
1049 
1050  if (rmass > 0) then
1051  rmass = amu / rmass
1052  end if
1053 
1054  if (rform == 'F') then
1055  if (ismax > 0) nrec = nrec + (nchsq * ismax + 3) / 4
1056  nrec = nrec + (nstat + 3) / 4
1057  nrec = nrec + (nchan * nstat + 3) / 4
1058  if (npole > 0) nrec = nrec + (nocsf * npole + 3) / 4
1059  if (ibut > 0) nrec = nrec + (3 * nchan + 3) / 4
1060  if (abs(ibut) > 1) nrec = nrec + 1 + (nchan + 3) / 4 + (iex + 3) / 4 + (nchan * iex + 3) / 4
1061  else
1062  nrec = nrec + 2
1063  if (ismax > 0) nrec = nrec + 1
1064  if (npole > 0) nrec = nrec + 1
1065  if (ibut > 0) nrec = nrec + 1
1066  if (abs(ibut) > 1) nrec = nrec + 4
1067  end if
1068 
1069  ninfo = 1
1070  ndata = nrec - ninfo
1071 
1072  if (rform == 'F') then
1073  write (lurmt, '(10I7)') keyrm, nrmset, nrec, ninfo, ndata
1074  write (lurmt, '(A80)') opts % diag_name
1075  write (lurmt, '(16I5)') ntarg, nvib, ndis, nchan
1076  write (lurmt, '(4I5,2D20.13)') opts % lambda, stot, gutot, ion, r, rmass
1077  write (lurmt, '(4I5,2D20.13)') ismax, nstat, npole, ibut, rmatr
1078  !if (abs(ibut) > 1) write(lurmt, '(i10,d20.13,i5)') nocsf, ezero, iex
1079  if (ismax > 0) write(lurmt, '(4D20.13)') ((this % a(i,k),i=1,nchsq),k=1,ismax)
1080  write (lurmt, '(4D20.13)') (solution % ci_vec % ei(i) + solution % core_energy,i=1,nstat)
1081  write (lurmt, '(4D20.13)') ((this % wamp(this % nfdm + 1) % cv(i,j)/sqrt(2.0_wp),i=1,nchan),j=1,nstat)
1082  if (npole > 0) write (lurmt, '(4D20.13)') ((solution % ci_vec % cv(i,j),i=1,nocsf),j=1,npole) ! TODO: all ranks need to write their part
1083  !if (ibut > 0) write (lurmt, '(4D20.13)') ((bcoef(i,j),i=1,3),j=1,nchan)
1084  !if (abs(ibut) > 1) write (lurmt, '(4D20.13)') (sfac(j),j=1,nchan)
1085  !if (abs(ibut) > 1) write (lurmt, '(4D20.13)') (ecex(j),j=1,iex)
1086  !if (abs(ibut) > 1) write (lurmt, '(4D20.13)') ((rcex(i,j),i=1,nchan),j=1,iex)
1087  else
1088  write(lurmt) keyrm, nrmset, nrec, ninfo, ndata
1089  write(lurmt) opts % diag_name
1090  write(lurmt) ntarg, nvib, ndis, nchan
1091  write(lurmt) opts % lambda, stot, gutot, ion, r, rmass
1092  write(lurmt) ismax, nstat, npole, ibut, rmatr
1093  !if (abs(ibut) > 1) write(lurmt) nocsf, ezero, iex
1094  if (ismax > 0) write(lurmt) ((this % a(i,k),i=1,nchsq),k=1,ismax)
1095  write (lurmt) (solution % ci_vec % ei(i) + solution % core_energy,i=1,nstat)
1096  write (lurmt) ((this % wamp(this % nfdm + 1) % cv(i,j)/sqrt(2.0_wp),i=1,nchan),j=1,nstat)
1097  if (npole > 0) write (lurmt) ((solution % ci_vec % cv(i,j),i=1,nocsf),j=1,npole) ! TODO: all ranks need to write their part
1098  !if (ibut > 0) write (lurmt) ((bcoef(i,j),i=1,3),j=1,nchan)
1099  !if (abs(ibut) > 1) write (lurmt) (sfac(j),j=1,nchan)
1100  !if (abs(ibut) > 1) write (lurmt) (ecex(j),j=1,iex)
1101  !if (abs(ibut) > 1) write (lurmt) ((rcex(i,j),i=1,nchan),j=1,iex)
1102  end if
1103 
1104  end subroutine write_boundary_data
1105 
1106 
1140  subroutine get_channel_couplings (this, ismax, ntarg, target_properties, alpha0, alpha2, use_pol)
1141 
1142  use coupling_obj_gbl, only: couplings_type
1143  use class_molecular_properties_data, only: molecular_properties_data
1144  use mpi_gbl, only: mpi_xermsg
1145  use precisn, only: wp
1146 
1147  class(outerinterface), intent(inout) :: this
1148  type(molecular_properties_data), intent(in) :: target_properties
1149 
1150  integer, intent(in) :: ismax, ntarg
1151  real(wp), intent(in) :: alpha0, alpha2
1152  logical, intent(in) :: use_pol
1153 
1154  real(wp), parameter :: fourpi = 12.5663706143591729538505735331180115_wp
1155  real(wp), parameter :: roneh = 0.70710678118654752440084436210484903_wp !sqrt(0.5)
1156  real(wp), parameter :: rothree = 0.57735026918962576450914878050195745_wp !1/sqrt(3)
1157 
1158  ! The Gaunt coefficients for the real spherical harmonics defined below are needed to express the xx, yy, zz angular
1159  ! behaviour in terms of the real spherical harmonics.
1160 
1161  ! x^2 = x*x ~ X_{11}*X_{11} = x2_X00*X_{00} + x2_X20*X_{20} + x2_X22*X_{22}
1162  real(wp), parameter :: x2_X00 = 0.282094791773878e+00_wp
1163  real(wp), parameter :: x2_X20 = -0.126156626101008e+00_wp
1164  real(wp), parameter :: x2_X22 = 0.218509686118416e+00_wp
1165  ! y^2 = y*y ~ X_{1-1}*X_{1-1} = y2_X00*X_{00} + y2_X20*X_{20} + y2_X22*X_{22}
1166  real(wp), parameter :: y2_X00 = 0.282094791773878e+00_wp
1167  real(wp), parameter :: y2_X20 = -0.126156626101008e+00_wp
1168  real(wp), parameter :: y2_X22 = -0.218509686118416e+00_wp
1169  ! z^2 = z*z ~ X_{10}*X_{10} = z2_X00*X_{00} + z2_X20*X_{20}
1170  real(wp), parameter :: z2_X00 = 0.282094791773878e+00_wp
1171  real(wp), parameter :: z2_X20 = 0.252313252202016e+00_wp
1172 
1173  type(couplings_type) :: couplings
1174 
1175  real(wp), allocatable :: prop(:,:,:)
1176 
1177  real(wp) :: cpl, sph_cpl, fac
1178  integer :: l1, m1, q1, l2, m2, q2, no_cpl, lqt, isq, it1, it2
1179  integer :: ch_1, ch_2, lambda, mlambda, lmin, lmax
1180  logical :: use_alpha2
1181 
1182  if (use_pol .and. ntarg > 1) then
1183  write (*, '("WARNING: adding polarization potential while more than one target state is present in the outer region.")')
1184  end if
1185 
1186  ! get a convenient dense matrix set with the properties
1187  call target_properties % decompress(1, ismax, prop)
1188 
1189  ! find size parameters based on polarizability setup
1190  use_alpha2 = (use_pol .and. alpha2 /= 0.0_wp)
1191  lmax = merge(max(ismax, 3), ismax, use_pol) ! maximal angular momentum transfer (polarizability corresponds to lambda=3)
1192  no_cpl = this % nchan * (this % nchan + 1) / 2 ! total number of unique combinations of the scattering channels
1193 
1194  ! allocate memory for the coupling coefficients
1195  if (allocated(this % a)) deallocate (this % a)
1196  allocate (this % a(no_cpl, lmax)) ! indices (:, 0) are never needed, though
1197  this % a(:,:) = 0.0_wp
1198 
1199  do ch_1 = 1, this % nchan
1200 
1201  l1 = this % lchl(ch_1) ! the l, |m| values correspond to the l,|m| values of the real spherical harmonics
1202  m1 = this % mchl(ch_1)
1203  q1 = this % qchl(ch_1) ! q is sign(m) or 0 if m=0.
1204  m1 = m1 * q1 ! determine m
1205  it1 = this % ichl(ch_1) ! sequence number of the target state corresponding to this channel
1206 
1207  do ch_2 = 1, ch_1
1208 
1209  l2 = this % lchl(ch_2) ! the l, |m| values correspond to the l,|m| values of the real spherical harmonics
1210  m2 = this % mchl(ch_2)
1211  q2 = this % qchl(ch_2) ! q is sign(m) or 0 if m=0.
1212  m2 = m2 * q2 ! determine m
1213  it2 = this % ichl(ch_2) ! sequence number of the target state corresponding to this channel
1214 
1215  ! use the selection rules for the real spherical harmonics to determine the range of lambda values
1216  ! which may give non-zero real Gaunt coefficients
1217  call couplings % bounds_rg(l1, l2, m1, m2, lmin, lmax)
1218 
1219  !don't include potentials with lambda > ismax
1220  if (lmax > ismax) lmax = ismax
1221 
1222  ! lambda = 0 would correspond to the monopole contribution, i.e. taking into account the total (perhaps nonzero)
1223  ! molecular charge. This is taken into account separately in RSOLVE.
1224  if (lmin == 0) lmin = 2
1225 
1226  ! linear index corresponding to the current combination of the channels (ch_1,ch_2).
1227  lqt = ch_1 * (ch_1 - 1) / 2 + ch_2
1228 
1229  ! Loop over the multipole moments which may be non-zero: we loop in steps of two since the selection rules
1230  ! for the r.s.h. imply that only if the sum of all L values is even the coupling may be non-zero.
1231  do lambda = lmin, lmax, 2
1232 
1233  ! see the formula for V_{ij}: effectively this converts the inner region property from solid harmonic
1234  ! normalization to spherical harmonic normalization as needed by the Leg. expansion.
1235  fac = sqrt(fourpi / (2 * lambda + 1.0_wp))
1236 
1237  do mlambda = -lambda, lambda
1238 
1239  ! rgaunt: the coupling coefficient for the real spherical harmonics (l1,m1,l2,m2,lambda,mlambda);
1240  ! The factor 2.0_wp converts the units of the inner region
1241  ! properties from Hartree to Rydberg since that's the energy
1242  ! unit used in the outer region.
1243  cpl = 2.0_wp * fac * couplings % rgaunt(l1, lambda, l2, m1, mlambda, m2)
1244 
1245  ! linear index corresponding to the current (lambda,mlambda) values. lambda*lambda is the number
1246  ! of all (lambda1,mlambda1) combinations for lambda1 = lambda-1.
1247  isq = lambda * lambda + lambda + mlambda
1248 
1249  ! increment the value of the coefficient for the multipole coupling potential of order lambda between
1250  ! the target states it1 and it2
1251  this % a(lqt, lambda) = this % a(lqt, lambda) + cpl * prop(it1, it2, isq)
1252 
1253  end do !mlambda
1254 
1255  end do !lambda
1256 
1257  ! add polarizabilities for the ground state channels, but only if use_pol == .true.
1258  if (use_pol .and. it1 == 1 .and. it2 == 1) then
1259 
1260  ! the radial dependence of the polarization potential is r^{-4} which corresponds to lambda = 3
1261  lambda = 3
1262 
1263  ! obviously, the spherical polarizability (l=0) couples only the channels with identical angular behaviour
1264  if (l1 == l2 .and. m1 == m2) then
1265  sph_cpl = 1.0_wp
1266  write(*, '("Spherical part of the polarization potential will be added to the &
1267  &channel (target state,l,m), coefficient value: (",3i5,") ",e25.15)') &
1268  it1, l1, m1, -sph_cpl * alpha0
1269  this % a(lqt, lambda) = this % a(lqt, lambda) - sph_cpl * alpha0 ! add the spherical part of the polarizability
1270  else
1271  sph_cpl = 0.0_wp
1272  end if
1273 
1274  ! Add the non-spherical part of the polarizability; the polarization potential here corresponds to:
1275  ! alpha_{2}*C_{i}*r_{-4}, where the angular behaviour of C_{i} = xy, xz, yz, xx, yy, zz
1276  ! Note that we assume below that the polarizability tensor has the same values (alpha2) for all components.
1277  ! Also note that as long as l1,m1 and l2,m2 belong to the same IR, only the totally symmetric components
1278  ! of the pol. tensor (xx,yy,zz) may contribute.
1279  if (use_alpha2) then
1280 
1281  write(*, '("Non-spherical part of the polarization potential &
1282  &(target state,l1,m2,l2,m2): ",5i5)') it1, l1, m1, l2, m2
1283 
1284  ! x^2 component
1285  cpl = x2_x00 * couplings % rgaunt(l1, 0, l2, m1, 0, m2) + &
1286  x2_x20 * couplings % rgaunt(l1, 2, l2, m1, 0, m2) + &
1287  x2_x22 * couplings % rgaunt(l1, 2, l2, m1, 2, m2)
1288  write(*, '("x^2 coefficient: ",e25.15)') -cpl * alpha2
1289  this % a(lqt, lambda) = this % a(lqt, lambda) - cpl * alpha2
1290 
1291  ! y^2 component
1292  cpl = y2_x00 * couplings % rgaunt(l1, 0, l2, m1, 0, m2) + &
1293  y2_x20 * couplings % rgaunt(l1, 2, l2, m1, 0, m2) + &
1294  y2_x22 * couplings % rgaunt(l1, 2, l2, m1, 2, m2)
1295  write(*, '("y^2 coefficient: ",e25.15)') -cpl * alpha2
1296  this % a(lqt, lambda) = this % a(lqt, lambda) - cpl * alpha2
1297 
1298  ! z^2 component
1299  cpl = z2_x00 * couplings % rgaunt(l1, 0, l2, m1, 0, m2) + &
1300  z2_x20 * couplings % rgaunt(l1, 2, l2, m1, 0, m2)
1301  write(*, '("z^2 coefficient: ",e25.15)') -cpl * alpha2
1302  this % a(lqt, lambda) = this % a(lqt, lambda) - cpl * alpha2
1303 
1304  ! xz component
1305  cpl = couplings % rgaunt(l1, 2, l2, m1, 1, m2) * rothree
1306  write(*, '("xz coefficient: ",e25.15)') -cpl * alpha2
1307  this % a(lqt, lambda) = this % a(lqt, lambda) - cpl * alpha2
1308 
1309  ! yz component
1310  cpl = couplings % rgaunt(l1, 2, l2, m1, -1, m2) * rothree
1311  write(*, '("yz coefficient: ",e25.15)') -cpl * alpha2
1312  this % a(lqt, lambda) = this % a(lqt, lambda) - cpl * alpha2
1313 
1314  ! xy component
1315  cpl = couplings % rgaunt(l1, 2, l2, m1, -2, m2) * rothree
1316  write(*, '("xy coefficient: ",e25.15)') -cpl * alpha2
1317  this % a(lqt, lambda) = this % a(lqt, lambda) - cpl * alpha2
1318 
1319  end if ! use_alpha2
1320 
1321  end if ! use_pol
1322 
1323  end do ! ch_2
1324 
1325  end do ! ch_1
1326 
1327  end subroutine get_channel_couplings
1328 
1329 
1374  subroutine write_rmt_data (input, inner_properties, target_properties, solutions, intf)
1375 
1376  use algorithms, only: findloc, insertion_sort
1377  use class_molecular_properties_data, only: molecular_properties_data
1378  use const_gbl, only: stdout
1380  use mpi_gbl, only: myrank, master, mpiint, mpi_xermsg, mpi_mod_file_open_write, &
1381  mpi_mod_file_close, mpi_mod_file_set_size, mpi_mod_file_write, mpi_mod_barrier
1382  use options_module, only: optionsset
1384 
1385  type(optionsset), intent(in) :: input
1386  type(molecular_properties_data), intent(in) :: inner_properties
1387  type(molecular_properties_data), intent(in) :: target_properties
1388  type(solutionhandler), allocatable, intent(in) :: solutions(:)
1389  type(outerinterface), allocatable, intent(in) :: intf(:)
1390 
1391  real(rmt_real), allocatable :: cf(:,:,:), wmat(:,:), crlv(:,:), etarg(:), rg(:), eig(:), r_points(:)
1392  integer(rmt_int), allocatable :: iidip(:), ifdip(:), lm_rg(:,:), ltarg(:), starg(:), nconat(:), l2p(:), m2p(:), ichl(:), &
1393  propblocks(:)
1394  integer, allocatable :: ions(:)
1395 
1396  real(rmt_real) :: bbloch = 0, prop, rmatr, delta_r
1397  integer :: i, j, k, l, nsym, m, irr_map(8,8), iblock, u, v, nsymm, ind, ip, irr1, irr2, it1, it2, lu, neig, nnuc, &
1398  ierr, nblock, sym1, sym2, ref
1399  integer(mpiint) :: fh
1400  integer(rmt_int) :: s, s1, s2, ntarg, n_rg, nelc, nz, lran2, lamax, inast, nchmx, nstmx, lmaxp1, nchan, lrgl, nspn, npty, &
1401  mnp1, mxstat, lrang2, lmax, nfdm
1402 
1403  ! number of N + 1 irrs for which we have inner solution and dipoles
1404  inast = count(iand(input % opts(:) % vector_storage_method, pass_to_cdenprop) /= 0 .and. &
1405  input % opts(:) % diagonalization_flag /= no_diagonalization)
1406 
1407  ref = maxloc(intf(:) % ntarg, 1) ! irr with the first non-zero number of outer region states
1408  nnuc = target_properties % no_nuclei ! number of nuclei in the molecule
1409  nfdm = input % nfdm ! number of boundary amplitudes evaluation radii *inside* R-matrix sphere
1410  delta_r = input % delta_r ! spacing between the evaluation distances
1411  rmatr = input % rmatr ! radius of the inner region
1412  nsymm = size(input % opts(:)) ! number of N + 1 irrs for which we have inputs (= size(intf(:)))
1413  ntarg = intf(ref) % ntarg ! number of target ("ionic") states
1414  nelc = input % opts(1) % num_electrons - 1 ! number of electrons of the target (ion)
1415  nz = sum(target_properties % charge(1:nnuc)) ! number of protons in the molecule
1416 
1417  mxstat = maxval(inner_properties % dense_blocks(1:inner_properties % no_blocks) % mat_dimen) ! largest dipole block size
1418 
1419  ! avoid the routine altogether when there are no data for any reason
1420  if (nsymm == 0 .or. inast == 0 .or. ntarg == 0 .or. mxstat <= 0) then
1421  write (stdout, *) 'Nothing to do in RMT interface'
1422  return
1423  end if
1424 
1425  lrang2 = 0 ! angular momentum limit
1426  lamax = 0 ! angular momentum limit
1427  nchmx = 0 ! maximal number of channels across irreducible representations
1428  nstmx = 0 ! maximal number of inner region eigenstates across representations
1429 
1430  ! get energy-sorted list of ionic states for use in outer region (only a subset of all if IDTARG is incomplete)
1431  ions = intf(ref) % idtarg
1432  call insertion_sort(ions)
1433 
1434  ! open the output file and reset its contents
1435  call mpi_mod_file_open_write('molecular_data', fh, ierr)
1436  call mpi_mod_file_set_size(fh, 0)
1437 
1438  allocate (propblocks(inner_properties % no_blocks))
1439 
1440  write (stdout, '(/,"Writing RMT data file")')
1441  write (stdout, '( "=====================")')
1442  write (stdout, '(/," Inner region dipole block max size: ",I0)') mxstat
1443  write (stdout, '(/," Ionic states used in outer region: ")')
1444  write (stdout, '(/," ",20I5)') ions
1445 
1446  ! store all (N + 1)-electron dipole blocks
1447  do m = -1, 1
1448 
1449  ! find out which IRR pairs yield non-zero matrix elements of Y[1,m]
1450  irr_map = 0
1451  nblock = 0
1452  do ip = 1, inner_properties % no_blocks
1453  if (inner_properties % dense_index(3, ip) == 1 .and. &
1454  inner_properties % dense_index(4, ip) == m) then
1455  sym1 = inner_properties % dense_index(1, ip)
1456  sym2 = inner_properties % dense_index(2, ip)
1457  irr1 = inner_properties % symmetries_index(1, sym1) + 1
1458  irr2 = inner_properties % symmetries_index(1, sym2) + 1
1459  nblock = nblock + 1
1460  propblocks(nblock) = ip
1461  irr_map(irr1, irr2) = nblock
1462  end if
1463  end do
1464 
1465  ! get some free memory
1466  if (allocated(iidip)) deallocate (iidip)
1467  if (allocated(ifdip)) deallocate (ifdip)
1468  allocate (iidip(nblock))
1469  allocate (ifdip(nblock))
1470  iidip(:) = -1
1471  ifdip(:) = -1
1472 
1473  ! compose sequence of bra and ket symmetry indices
1474  do ip = 1, nblock
1475  sym1 = inner_properties % dense_index(1, propblocks(ip))
1476  sym2 = inner_properties % dense_index(2, propblocks(ip))
1477  irr1 = inner_properties % symmetries_index(1, sym1) + 1
1478  irr2 = inner_properties % symmetries_index(1, sym2) + 1
1479  iidip(ip) = irr1
1480  ifdip(ip) = irr2
1481  end do
1482 
1483  ! write dipole metadata
1484  call mpi_mod_file_write(fh, int(nblock, rmt_int))
1485  call mpi_mod_file_write(fh, mxstat)
1486  call mpi_mod_file_write(fh, mxstat)
1487  call mpi_mod_file_write(fh, iidip, nblock)
1488  call mpi_mod_file_write(fh, ifdip, nblock)
1489 
1490  ! collective write of the ScaLAPACK matrices to the stream file
1491  do ip = 1, nblock
1492  associate(d => inner_properties % dense_blocks(propblocks(ip)))
1493  if (d % CV_is_scalapack) then
1494  call mpi_mod_file_write(fh, int(d % mat_dimen_r), int(d % mat_dimen_c), int(d % nprow), int(d % npcol), &
1495  int(d % scal_block_size), int(d % scal_block_size), d % CV, &
1496  int(d % local_row_dimen), int(d % local_col_dimen))
1497  call mpi_mod_barrier(ierr)
1498  else
1499  call mpi_mod_file_write(fh, d % CV, int(d % mat_dimen_r), int(d % mat_dimen_c))
1500  end if
1501  end associate
1502  end do
1503  end do
1504 
1505  ! set up angular momentum limits
1506  do i = 1, nsymm
1507  if (iand(input % opts(i) % vector_storage_method, pass_to_cdenprop) /= 0 .and. &
1508  input % opts(i) % diagonalization_flag /= no_diagonalization) then
1509  if (intf(i) % nchan > 0) then
1510  lrang2 = max(int(lrang2), maxval(intf(i) % lchl(1:intf(i) % nchan)))
1511  lamax = max(int(lamax), intf(i) % ismax)
1512  nchmx = max(int(nchmx), intf(i) % nchan)
1513  lmaxp1 = lrang2
1514  end if
1515  nstmx = max(int(nstmx), solutions(i) % vec_dimen)
1516  end if
1517  end do
1518 
1519  ! number of target (ion) states
1520  call mpi_mod_file_write(fh, ntarg)
1521 
1522  ! allocate memory for auxiliary arrays
1523  allocate (etarg(ntarg), ltarg(ntarg), starg(ntarg), nconat(ntarg), l2p(nchmx), m2p(nchmx), eig(nstmx), &
1524  wmat(nchmx, nstmx), cf(nchmx, nchmx, lamax), ichl(nchmx), crlv(ntarg, ntarg), r_points(nfdm + 1), stat = ierr)
1525  if (ierr /= 0) then
1526  call mpi_xermsg('Postprocessing_module', 'write_rmt_data', 'Memory allocation failure.', 1, 1)
1527  end if
1528 
1529  ! target data
1530  do i = 1, ntarg
1531  etarg(i) = target_properties % energies(ions(i))
1532  ltarg(i) = target_properties % symmetries_index(1, target_properties % energies_index(2, ions(i))) + 1
1533  starg(i) = target_properties % symmetries_index(2, target_properties % energies_index(2, ions(i)))
1534  end do
1535 
1536  ! finite difference points inside (and at) the R-matrix sphere
1537  do i = 1, nfdm + 1
1538  r_points(i) = rmatr - (nfdm + 1 - i) * delta_r
1539  end do
1540 
1541  ! store all N-electron dipole blocks
1542  do m = -1, 1
1543  crlv(:,:) = 0
1544  do ip = 1, target_properties % non_zero_properties
1545  if (target_properties % properties_index(3, ip) == 1 .and. &
1546  target_properties % properties_index(4, ip) == m) then
1547  ! get state information for this property
1548  it1 = target_properties % properties_index(1, ip)
1549  it2 = target_properties % properties_index(2, ip)
1550  prop = target_properties % properties(ip)
1551  ! check that these two states are requested in the outer region
1552  it1 = findloc(ions, it1, 1)
1553  it2 = findloc(ions, it2, 1)
1554  if (it1 > 0 .and. it2 > 0) then
1555  crlv(it1, it2) = prop
1556  crlv(it2, it1) = prop
1557  end if
1558  end if
1559  end do
1560 
1561  call mpi_mod_file_write(fh, crlv, int(ntarg), int(ntarg))
1562  end do
1563 
1564  ! generate the coupling coefficients needed in RMT to construct the laser-target and laser-electron asymptotic potentials
1565  lmax = 0
1566  do i = 1, nsymm
1567  if (iand(input % opts(i) % vector_storage_method, pass_to_cdenprop) /= 0 .and. &
1568  input % opts(i) % diagonalization_flag /= no_diagonalization) then
1569  if (intf(i) % nchan > 0) then
1570  lmax = max(int(lmax), maxval(intf(i) % lchl(1:intf(i) % nchan)))
1571  end if
1572  end if
1573  end do
1574  call generate_couplings(lmax, n_rg, rg, lm_rg)
1575 
1576  call mpi_mod_file_write(fh, n_rg)
1577  call mpi_mod_file_write(fh, rg, int(n_rg))
1578  call mpi_mod_file_write(fh, lm_rg, 6, int(n_rg))
1579  call mpi_mod_file_write(fh, nelc)
1580  call mpi_mod_file_write(fh, nz)
1581  call mpi_mod_file_write(fh, lrang2)
1582  call mpi_mod_file_write(fh, lamax)
1583  call mpi_mod_file_write(fh, ntarg)
1584  call mpi_mod_file_write(fh, inast)
1585  call mpi_mod_file_write(fh, nchmx)
1586  call mpi_mod_file_write(fh, nstmx)
1587  call mpi_mod_file_write(fh, lmaxp1)
1588  call mpi_mod_file_write(fh, rmatr)
1589  call mpi_mod_file_write(fh, bbloch)
1590  call mpi_mod_file_write(fh, etarg, int(ntarg))
1591  call mpi_mod_file_write(fh, ltarg, int(ntarg))
1592  call mpi_mod_file_write(fh, starg, int(ntarg))
1593  call mpi_mod_file_write(fh, nfdm)
1594  call mpi_mod_file_write(fh, delta_r)
1595  call mpi_mod_file_write(fh, r_points, int(nfdm + 1))
1596 
1597  ! write channel information (per symmetry)
1598  do i = 1, nsymm
1599  if (iand(input % opts(i) % vector_storage_method, pass_to_cdenprop) /= 0 .and. &
1600  input % opts(i) % diagonalization_flag /= no_diagonalization) then
1601 
1602  lrgl = 1 + input % opts(i) % lambda
1603  nspn = int(2 * input % opts(i) % spin + 1, rmt_int)
1604  npty = 0
1605  nchan = intf(i) % nchan
1606  mnp1 = solutions(i) % vec_dimen
1607 
1608  do j = 1, ntarg
1609  nconat(j) = count(intf(i) % ichl(1:nchan) == j)
1610  end do
1611 
1612  l2p(:) = 0
1613  m2p(:) = 0
1614  ichl(:) = 0
1615 
1616  do j = 1, nchan
1617  l2p(j) = intf(i) % lchl(j)
1618  m2p(j) = intf(i) % mchl(j)
1619  ichl(j) = intf(i) % ichl(j)
1620  end do
1621 
1622  call mpi_mod_file_write(fh, lrgl)
1623  call mpi_mod_file_write(fh, nspn)
1624  call mpi_mod_file_write(fh, npty)
1625  call mpi_mod_file_write(fh, nchan)
1626  call mpi_mod_file_write(fh, mnp1)
1627  call mpi_mod_file_write(fh, nconat, int(ntarg))
1628  call mpi_mod_file_write(fh, l2p, int(nchmx))
1629  call mpi_mod_file_write(fh, m2p, int(nchmx))
1630 
1631  eig(:) = 0
1632  eig(1:mnp1) = solutions(i) % ci_vec % ei(1:mnp1) + solutions(i) % core_energy
1633 
1634  wmat(:,:) = 0
1635  if (myrank == master) wmat(1:nchan, 1:mnp1) = intf(i) % wamp(nfdm + 1) % CV(1:nchan, 1:mnp1)
1636 
1637  call mpi_mod_file_write(fh, eig, int(nstmx))
1638  call mpi_mod_file_write(fh, wmat, int(nchmx), int(nstmx))
1639 
1640  cf(:,:,:) = 0
1641  do l = 1, intf(i) % ismax
1642  do k = 1, nchan
1643  do j = 1, nchan
1644  u = max(j, k)
1645  v = min(j, k)
1646  ind = u * (u - 1) / 2 + v
1647  cf(j, k, l) = intf(i) % a(ind, l)
1648  end do
1649  end do
1650  end do
1651 
1652  call mpi_mod_file_write(fh, cf, int(nchmx), int(nchmx), int(lamax))
1653  call mpi_mod_file_write(fh, ichl, int(nchmx))
1654 
1655  s1 = intf(i) % wamp(1) % mat_dimen_r
1656  s2 = intf(i) % wamp(1) % mat_dimen_c
1657 
1658  call mpi_mod_file_write(fh, s1)
1659  call mpi_mod_file_write(fh, s2)
1660 
1661  ! collective write of the ScaLAPACK matrix to the stream file
1662  do j = 1, nfdm
1663  associate(w => intf(i) % wamp(j))
1664  if (w % CV_is_scalapack) then
1665  call mpi_mod_file_write(fh, int(w % mat_dimen_r), int(w % mat_dimen_c), &
1666  int(w % nprow), int(w % npcol), &
1667  int(w % scal_block_size), int(w % scal_block_size), w % CV, &
1668  int(w % local_row_dimen), int(w % local_col_dimen))
1669  else
1670  call mpi_mod_file_write(fh, w % CV, int(w % mat_dimen_r), int(w % mat_dimen_c))
1671  end if
1672  end associate
1673  end do
1674 
1675  end if
1676  end do
1677 
1678  call mpi_mod_file_close(fh, ierr)
1679 
1680  end subroutine write_rmt_data
1681 
1682 
1694  subroutine generate_couplings (maxl, n_rg, rg, lm_rg)
1695 
1696  use coupling_obj_gbl, only: couplings_type
1697 
1698  integer(rmt_int), intent(in) :: maxl
1699  integer(rmt_int), intent(out) :: n_rg
1700  real(rmt_real), allocatable, intent(inout) :: rg(:)
1701  integer(rmt_int), allocatable, intent(inout) :: lm_rg(:,:)
1702 
1703  type(couplings_type) :: cpl
1704 
1705  integer :: l1, l2, l3, m1, m2, m3, err
1706  real(wp) :: tmp
1707 
1708  call cpl % prec_cgaunt(int(maxl))
1709 
1710  l3 = 1
1711 
1712  ! How many non-zero dipole couplings there are
1713  n_rg = 0
1714  do l1 = 0, maxl
1715  do m1 = -l1, l1
1716  do l2 = 0, maxl
1717  do m2 = -l2, l2
1718  do m3 = -l3, l3
1719  tmp = cpl % rgaunt(l1, l2, l3, m1, m2, m3)
1720  if (tmp /= 0) n_rg = n_rg + 1
1721  end do !m3
1722  end do !m2
1723  end do !l2
1724  end do !m1
1725  end do !l1
1726 
1727  if (allocated(rg)) deallocate (rg)
1728  if (allocated(lm_rg)) deallocate (lm_rg)
1729  n_rg = max(n_rg, 1_rmt_int) ! make sure that even if all couplings are zero this routine allocates rg, lm_rg
1730  allocate (rg(n_rg), lm_rg(6, n_rg), stat = err)
1731  rg = 0
1732  lm_rg = -2
1733 
1734  ! Save them
1735  n_rg = 0
1736  do l1 = 0, maxl
1737  do m1 = -l1, l1
1738  do l2 = 0, maxl
1739  do m2 = -l2, l2
1740  do m3 = -l3, l3
1741  tmp = cpl % rgaunt(l1, l2, l3, m1, m2, m3)
1742  if (tmp /= 0) then
1743  n_rg = n_rg + 1
1744  rg(n_rg) = tmp
1745  lm_rg(1:6, n_rg) = (/ l1, m1, l2, m2, l3, m3 /)
1746  end if
1747  end do !m3
1748  end do !m2
1749  end do !l2
1750  end do !m1
1751  end do !l1
1752 
1753  end subroutine generate_couplings
1754 
1755 end module postprocessing_module
postprocessing_module::deinit_outer_interface
subroutine deinit_outer_interface(this)
Release memory held by this object.
Definition: Postprocessing_module.F90:460
postprocessing_module::rmt_int
integer, parameter rmt_int
Definition: Postprocessing_module.F90:45
postprocessing_module
Further processing of the diagonalization results.
Definition: Postprocessing_module.F90:32
postprocessing_module::rmt_real
integer, parameter rmt_real
Definition: Postprocessing_module.F90:46
solutionhandler_module::solutionhandler
Solution writer.
Definition: SolutionHandler_module.f90:56
postprocessing_module::get_channel_info
subroutine get_channel_info(this, opts, prop)
Assemble the list of outer channels.
Definition: Postprocessing_module.F90:617
consts_mpi_ci::symtype_d2h
integer, parameter symtype_d2h
This describes D_2_h symmetries.
Definition: consts_mpi_ci.f90:57
options_module::options
Calculation setup for a single Hamiltonian diagonalization.
Definition: Options_module.f90:74
postprocessing_module::outerinterface
SWINTERF-inspired post-processing object.
Definition: Postprocessing_module.F90:55
consts_mpi_ci::no_ci_target
integer, parameter no_ci_target
No Ci target contraction (target only run)
Definition: consts_mpi_ci.f90:61
postprocessing_module::write_rmt_data
subroutine write_rmt_data(input, inner_properties, target_properties, solutions, intf)
Compose the RMT molecular input data file.
Definition: Postprocessing_module.F90:1375
postprocessing_module::setup_amplitudes
subroutine setup_amplitudes(this, opts)
Initialize raw boundary amplitudes (in integral library)
Definition: Postprocessing_module.F90:503
postprocessing_module::outer_interface
subroutine outer_interface(SCATCI_input, solutions, inner_properties)
Extract data needed by outer-region codes.
Definition: Postprocessing_module.F90:358
parallelization_module::process_grid
type(processgrid) process_grid
Definition: Parallelization_module.F90:80
postprocessing_module::extract_data
subroutine extract_data(this, opts, prop, solution)
Get interface data.
Definition: Postprocessing_module.F90:535
consts_mpi_ci
MPI SCATCI Constants module.
Definition: consts_mpi_ci.f90:31
postprocessing_module::write_channel_info
subroutine write_channel_info(this, nchset, opts, cform, prop)
Write the channel list to disk.
Definition: Postprocessing_module.F90:889
options_module::optionsset
MPI-SCATCI input.
Definition: Options_module.f90:202
parallelization_module
Distribution of processes into a grid.
Definition: Parallelization_module.F90:29
postprocessing_module::cdenprop_properties
subroutine cdenprop_properties(SCATCI_input, solutions, properties_all)
Evaluate multipoles in CDENPROP.
Definition: Postprocessing_module.F90:226
postprocessing_module::get_boundary_data
subroutine get_boundary_data(this, opts, prop, solution)
Evaluate boundary amplitudes for propagation and RMT.
Definition: Postprocessing_module.F90:735
postprocessing_module::write_data
subroutine write_data(this, i, opts, prop, solution)
Write interface data.
Definition: Postprocessing_module.F90:585
postprocessing_module::init_outer_interface
subroutine init_outer_interface(this, input)
Allocate memory for channel information.
Definition: Postprocessing_module.F90:411
postprocessing_module::write_boundary_data
subroutine write_boundary_data(this, nrmset, opts, rform, prop, solution)
Write the R-matrix amplitudes to disk.
Definition: Postprocessing_module.F90:1008
solutionhandler_module
Solution handler module.
Definition: SolutionHandler_module.f90:31
postprocessing_module::redistribute_solutions
subroutine redistribute_solutions(SCATCI_input, solutions)
Redistribute solutions from groups to everyone.
Definition: Postprocessing_module.F90:133
options_module
Options module.
Definition: Options_module.f90:41
postprocessing_module::get_channel_couplings
subroutine get_channel_couplings(this, ismax, ntarg, target_properties, alpha0, alpha2, use_pol)
Evaluate long-range channel coupling coefficients.
Definition: Postprocessing_module.F90:1141
consts_mpi_ci::no_diagonalization
integer, parameter no_diagonalization
No diagonalization.
Definition: consts_mpi_ci.f90:85
postprocessing_module::generate_couplings
subroutine generate_couplings(maxl, n_rg, rg, lm_rg)
Evaluate angular couplings for outer region of RMT.
Definition: Postprocessing_module.F90:1695
consts_mpi_ci::pass_to_cdenprop
integer, parameter pass_to_cdenprop
Eigenpairs will be saved in memory and passed to CDENPROP and outer interface.
Definition: consts_mpi_ci.f90:110
postprocessing_module::postprocess
subroutine, public postprocess(SCATCI_input, solutions)
Full post-processing pass.
Definition: Postprocessing_module.F90:104