MPI-SCATCI  2.0
An MPI version of SCATCI
Hamiltonian_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 
34 
35  use integer_packing, only: unpack8ints
36  use precisn, only: longint, wp
38  use csf_module, only: csfobject, csforbital
39  use options_module, only: options
41  use parallelization_module, only: grid => process_grid
43 
44  implicit none
45 
46  public basehamiltonian
47 
57  type, abstract :: basehamiltonian
58  class(orbitaltable), pointer :: orbitals
59  class(options), pointer :: options
60  class(baseintegral), pointer :: integral
61  class(csfobject), pointer :: csfs(:)
62 
63  integer :: nflg = 0
64  integer :: diagonal_flag
65  integer :: positron_flag
66  integer :: phase_flag
67  logical :: constructed = .false.
68  logical :: initialized = .false.
69  integer :: job_id = 0
70  integer :: number_of_integrals = 0
71  real(wp) :: element_one = 0.0
72 
73  type(symbolicelementvector) :: reference_symbol
74  contains
75  !Constructor
76  procedure, public :: construct => construct_base_hamiltonian
77  procedure(generic_build), deferred :: build_hamiltonian
78 
79  !-----------------------Private procedures-----------------!
80  procedure, public :: slater_rules
81  !procedure, public :: obey_slater_rules
82  procedure, public :: evaluate_integrals
83  procedure, public :: evaluate_integrals_singular
84  !procedure, public :: store_first_element
85 
86  !-------Slater rule functions------------------!
87  procedure, private :: handle_two_pair
88  procedure, private :: handle_one_pair
89  procedure, private :: handle_same
90  procedure, public :: my_job
91  end type basehamiltonian
92 
93  abstract interface
94 
101  subroutine generic_build(this,matrix_elements)
102  use basematrix_module, only: basematrix
103  import :: basehamiltonian
104  class(basehamiltonian) :: this
105  class(basematrix), intent(inout) :: matrix_elements
106  end subroutine generic_build
107  end interface
108 
109 contains
110 
121  subroutine construct_base_hamiltonian (this, option, csfs, orbitals, integral)
122  class(basehamiltonian), intent(inout) :: this
123  class(options), target, intent(in) :: option
124  class(csfobject), target, intent(in) :: csfs(:)
125  class(orbitaltable), target, intent(in) :: orbitals
126  class(baseintegral), target, intent(in) :: integral
127 
128  !Assign our member pointers for each class
129  this % options => option
130  this % csfs => csfs
131  this % orbitals => orbitals
132  this % integral => integral
133  this % phase_flag = this % options % phase_correction_flag
134  this % positron_flag = this % options % positron_flag
135 
136  this % job_id = 0
137  this % diagonal_flag = min(this % options % matrix_eval, 2)
138  this % constructed = .true.
139 
140  end subroutine construct_base_hamiltonian
141 
142 
159  subroutine slater_rules (this, csf_a, csf_b, sym_elements, flag, job_)
160  class(basehamiltonian), intent(in) :: this
161  class(csfobject), intent(in) :: csf_a, csf_b
162  class(symbolicelementvector), intent(inout) :: sym_elements
163  integer, intent(in) :: flag
164  logical, optional, intent(in) :: job_
165  logical :: job
166  integer :: diag, num_differences, dtr_diff(4), dtr_diff_fast(4), dtr_idx_a, dtr_idx_b
167  real(wp) :: coeff
168 
169  !Check for job parameter
170  if (present(job_)) then
171  job = job_
172  else
173  job = .true.
174  end if
175 
176  !Clear the symbolic elements ahead of time
177  call sym_elements % clear()
178 
179  !Check if we should be doing this
180  if (job) then
181  !If not then return
182  if (.not. this % my_job()) return
183  end if
184 
185  !Check if we are not the same
186  if (csf_a % id /= csf_b % id) then
187  !If we are not then quickly check if we need to do a slater calculation
188  if (csf_a % check_slater(csf_b) > 4) return
189  !if(this%obey_slater_rules(csf_a,csf_b) > 4) return
190  end if
191 
192  !Loop through the first set of determinants
193  do dtr_idx_a = 1, csf_a % num_orbitals
194  !Loop through the second set of determinants
195  do dtr_idx_b = 1, csf_b % num_orbitals
196 
197  !Use the fast slater rules method to check the number of differences and get the differing determinants
198  call csf_a % orbital(dtr_idx_a) % compare_excitations_fast(csf_b % orbital(dtr_idx_b), &
199  this % options % num_electrons, &
200  coeff, &
201  num_differences, &
202  dtr_diff)
203 
204  !Depending on the number of differences, we handle it appropriately
205  select case (num_differences)
206  case(3:)
207  cycle
208  case(2)
209  call this % handle_two_pair(dtr_diff, coeff, sym_elements, flag)
210  case(1)
211  call this % handle_one_pair(dtr_diff, coeff, sym_elements, csf_a % orbital(dtr_idx_a), flag)
212  case(0)
213  call this % handle_same(dtr_diff, coeff, sym_elements, csf_a % orbital(dtr_idx_a), flag)
214  end select
215 
216  end do
217  end do
218 
219  end subroutine slater_rules
220 
221 
232  subroutine handle_two_pair (this, dtrs, coeff, sym_element, flag)
233  class(basehamiltonian) :: this
234  class(symbolicelementvector) :: sym_element
235  integer, intent(in) :: dtrs(4)
236  real(wp), intent(in) :: coeff
237  integer, intent(in) :: flag
238 
239  if (this % diagonal_flag> 1 .and. this % orbitals % get_minimum_mcon(dtrs) > 0) return
240 
241  !Evaluate for symbolic elements
242  call this % orbitals % evaluate_IJKL_and_coeffs(dtrs, coeff, 0, sym_element, flag)
243 
244  end subroutine handle_two_pair
245 
246 
258  subroutine handle_one_pair (this, dtrs, coeff, sym_element, csf, flag)
259  class(basehamiltonian) :: this
260  class(symbolicelementvector) :: sym_element
261  class(csforbital), intent(in) :: csf
262  integer, intent(inout) :: dtrs(4)
263  real(wp), intent(in) :: coeff
264  integer, intent(in) :: flag
265 
266  type(spinorbital) :: so1, so2
267 
268  integer :: ref_dtrs(this % options % num_electrons)
269  integer :: num_electrons, N, M, ido, ia1, ia2, lnadd
270  integer :: nchk !Whether its continuum maybe?
271 
272  !Reset the positron flag
273  lnadd = 0
274 
275  !Get number of electrons
276  num_electrons = this % options % num_electrons
277 
278  !Copt the reference determinant to a seperate array
279  !ref_dtrs(:) = this%options%reference_dtrs(:)
280 
281  !Loop through the reference and replace the spin orbitals with ones from the CSF
282  !do ido=1,csf%num_replaced_dtrs
283  ! N = csf%replacing(ido)
284  ! M = this%orbitals%get_electron_number(N)
285  ! ref_dtrs(M) = csf%replaced(ido)
286  !enddo
287 
288  call csf % get_determinants(ref_dtrs, num_electrons)
289 
290  !Not sure what this does exactly
291  if (this % diagonal_flag > 1 .and. this % orbitals % get_two_minimum_mcon(dtrs(3), dtrs(4)) > 0) then
292  nchk = 1
293  else
294  nchk = 0
295  end if
296 
297  !Loop through all the spin orbitals
298  do ido = 1, num_electrons
299  !If we have the same spin orbitals then skip
300  if (ref_dtrs(ido) == dtrs(3)) cycle
301  !Again not quite sure what this does
302  if (nchk == 1 .and. this % orbitals % get_mcon(ref_dtrs(ido)) > 0) cycle
303  !Place the reference into our determinant array
304  dtrs(1) = ref_dtrs(ido)
305  dtrs(2) = ref_dtrs(ido)
306  !Now evaluate for symbols
307  call this % orbitals % evaluate_IJKL_and_coeffs(dtrs, coeff, 0, sym_element, flag)
308  end do
309 
310  !If this run contains positrons, then add them in if necessary
311  if (this % positron_flag /= 0) lnadd = this % orbitals % add_positron(dtrs, 3, 4)
312  if (nchk == 1) return
313 
314  !Get our original slater replacements
315  so1 = this % orbitals % spin_orbitals(dtrs(3))
316  so2 = this % orbitals % spin_orbitals(dtrs(4))
317 
318  !Check if they posses the same spin and m quanta
319  if (so1 % m_quanta /= so2 % m_quanta .or. so1 % spin /= so2 % spin) return
320  if (so1 % orbital_number < so2 % orbital_number) then
321  ia1 = so2 % orbital_number
322  ia2 = so1 % orbital_number
323  else
324  ia1 = so1 % orbital_number
325  ia2 = so2 % orbital_number
326  end if
327 
328  !if they do then add a 0I0J + P symbol
329  call sym_element % insert_ijklm_symbol(0, ia1, 0, ia2, lnadd, coeff)
330 
331  end subroutine handle_one_pair
332 
333 
345  subroutine handle_same (this, dtrs, coeff, sym_element, csf, flag)
346  class(basehamiltonian) :: this
347  class(symbolicelementvector) :: sym_element
348  class(csforbital), intent(in) :: csf
349  integer, intent(inout) :: dtrs(4)
350  real(wp), intent(in) :: coeff
351  integer, intent(in) :: flag
352 
353  type(spinorbital) :: so1, so2
354 
355  integer :: ref_dtrs(this % options % num_electrons)
356  integer :: num_electrons, N, M, ido, jdo, ia1, ia2, lnadd
357  integer :: nchk !Whether its continuum maybe?
358 
359  !Reset the positron flag
360  lnadd = 0
361 
362  !Get number of electrons
363  num_electrons = this % options % num_electrons
364 
365  !Copt the reference determinant to a seperate array
366  !ref_dtrs(:) = this%options%reference_dtrs(:)
367 
368  !Loop through the reference and replace the spin orbitals with ones from the CSF
369  !do ido=1,csf%num_replaced_dtrs
370  ! N = csf%replacing(ido)
371  ! M = this%orbitals%get_electron_number(N)
372  ! ref_dtrs(M) = csf%replaced(ido)
373  !enddo
374 
375  call csf % get_determinants(ref_dtrs, num_electrons)
376 
377  do ido = 2, num_electrons
378  dtrs(1) = ref_dtrs(ido)
379  dtrs(2) = dtrs(1)
380  do jdo = 1, ido - 1
381  dtrs(3) = ref_dtrs(jdo)
382  dtrs(4) = dtrs(3)
383  if (this % diagonal_flag > 1 .and. this % orbitals % get_two_minimum_mcon(dtrs(1), dtrs(3)) > 0) cycle
384  !Now evaluate for symbols
385  call this % orbitals % evaluate_IJKL_and_coeffs(dtrs, coeff, 0, sym_element, flag)
386  end do
387  end do
388 
389  if (this % NFLG /= 0) this % NFLG = 2
390  do ido = 1, num_electrons
391  n = ref_dtrs(ido)
392  if (this % diagonal_flag > 1 .and. this % orbitals%get_mcon(n) /= 0) cycle
393 
394  m = this % orbitals % get_orbital_number(n)
395  if (this % positron_flag /= 0) lnadd = this % orbitals % add_positron(dtrs, 1, 2)
396  call sym_element % insert_ijklm_symbol(0, m, 0, m, lnadd, coeff)
397  end do
398 
399  end subroutine handle_same
400 
401 
413  real(wp) function evaluate_integrals_singular (this, label, coeff, dummy_orb)
414  class(basehamiltonian), intent(inout) :: this
415  integer, intent(in) :: dummy_orb
416  real(wp), intent(in) :: coeff
417  integer(longint), intent(in) :: label(2)
418 
419  integer :: ido, jdo, num_elements, lwd(8)
420 
422 
423  if (coeff == 0.0_wp) return
424 
425  !Search for the dummy orbital
426  if (this % phase_flag > 0) then
427  call unpack8ints(label, lwd)
428  do jdo = 1, 4
429  if (lwd(jdo) == this % phase_flag) return
430  end do
431  else if (this % phase_flag == 0) then
432  call unpack8ints(label, lwd)
433  do jdo = 1, 4
434  if (lwd(jdo) <= 0) cycle
435  if (this % orbitals % mcorb(lwd(jdo)) == 0) return
436  end do
437  end if
438 
439  evaluate_integrals_singular = coeff * this % integral % get_integralf(label)
440 
441  !$OMP ATOMIC
442  this % number_of_integrals = this % number_of_integrals + 1
443 
444  end function evaluate_integrals_singular
445 
446 
457  real(wp) function evaluate_integrals (this, symbolic_elements, dummy_orb)
458  class(basehamiltonian), intent(inout) :: this
459  class(symbolicelementvector), intent(in) :: symbolic_elements
460  integer, intent(in) :: dummy_orb
461 
462  integer :: ido, num_elements
463  integer(longint) :: label(2)
464  real(wp) :: coeff
465 
466  num_elements = symbolic_elements % get_size()
467 
468  evaluate_integrals = 0.0_wp
469  if (num_elements == 0) return
470  do ido = 1, num_elements
471  call symbolic_elements % get_coeff_and_integral(ido, coeff, label)
472  evaluate_integrals = evaluate_integrals + this % evaluate_integrals_singular(label, coeff, dummy_orb)
473  end do
474 
475  end function evaluate_integrals
476 
477 
479  logical function my_job (this)
480  class(basehamiltonian) :: this
481 
482  my_job = .true.
483 
484  if (grid % gprocs == 1 .or. grid % grank == -1) return
485 
486  if (this % job_id /= grid % grank) then
487  my_job = .false.
488  end if
489 
490  this % job_id = this % job_id + 1
491  if (this % job_id == grid % gprocs) then
492  this % job_id = 0
493  end if
494 
495  end function my_job
496 
497 
504  integer function fast_slat (rep1, rep2, fin1, fin2, num_rep1, num_rep2, spin_orbs, tot_num_spin_orbs)
505  integer, intent(in) :: num_rep1, rep1(num_rep1), fin1(num_rep1)
506  integer, intent(in) :: num_rep2, rep2(num_rep2), fin2(num_rep2), tot_num_spin_orbs
507  integer, intent(inout) :: spin_orbs(tot_num_spin_orbs)
508 
509  integer :: i_orbital
510  integer :: i_dtrs
511  integer :: i_spin_orbital
512  integer :: orb_add
513  integer :: orb_remove
514 
515  do i_dtrs = 1, num_rep1
516  orb_remove = (rep1(i_dtrs) + 1) / 2
517  orb_add = (fin1(i_dtrs) + 1) / 2
518  spin_orbs(orb_remove) = spin_orbs(orb_remove) - 1
519  spin_orbs(orb_add) = spin_orbs(orb_add) + 1
520  end do
521 
522  do i_dtrs = 1, num_rep2
523  orb_remove = (rep2(i_dtrs) + 1) / 2
524  orb_add = (fin2(i_dtrs) + 1) / 2
525  spin_orbs(orb_remove) = spin_orbs(orb_remove) + 1
526  spin_orbs(orb_add) = spin_orbs(orb_add) - 1
527  end do
528 
529  fast_slat = 0
530 
531  do i_dtrs = 1, num_rep1
532  orb_remove = (rep1(i_dtrs) + 1) / 2
533  orb_add = (fin1(i_dtrs) + 1) / 2
534  fast_slat = fast_slat + spin_orbs(orb_remove)
535  spin_orbs(orb_remove) = 0
536  fast_slat = fast_slat + spin_orbs(orb_add)
537  spin_orbs(orb_add) = 0
538  end do
539 
540  do i_dtrs = 1, num_rep2
541  orb_remove = (rep2(i_dtrs) + 1) / 2
542  orb_add = (fin2(i_dtrs) + 1) / 2
543  fast_slat = fast_slat + spin_orbs(orb_remove)
544  spin_orbs(orb_remove) = 0
545  fast_slat = fast_slat + spin_orbs(orb_add)
546  spin_orbs(orb_add) = 0
547  end do
548 
549  end function fast_slat
550 
551 end module hamiltonian_module
csf_module
CSF module.
Definition: CSF_module.F90:34
hamiltonian_module::basehamiltonian
This is an abstract class that contains the majority of functionality required to construct hamiltoni...
Definition: Hamiltonian_module.f90:57
hamiltonian_module::fast_slat
integer function fast_slat(rep1, rep2, fin1, fin2, num_rep1, num_rep2, spin_orbs, tot_num_spin_orbs)
?
Definition: Hamiltonian_module.f90:505
hamiltonian_module::generic_build
Main build routine of the hamiltonian.
Definition: Hamiltonian_module.f90:101
hamiltonian_module::evaluate_integrals
real(wp) function evaluate_integrals(this, symbolic_elements, dummy_orb)
Evaluates all integrals from a list of symbolic elements.
Definition: Hamiltonian_module.f90:458
options_module::options
Calculation setup for a single Hamiltonian diagonalization.
Definition: Options_module.f90:74
hamiltonian_module::slater_rules
subroutine slater_rules(this, csf_a, csf_b, sym_elements, flag, job_)
Performs Slater rules and returns symbolic elements.
Definition: Hamiltonian_module.f90:160
hamiltonian_module::handle_same
subroutine handle_same(this, dtrs, coeff, sym_element, csf, flag)
Handles if there are no differences between spin orbitals.
Definition: Hamiltonian_module.f90:346
hamiltonian_module::handle_two_pair
subroutine handle_two_pair(this, dtrs, coeff, sym_element, flag)
Handles if there are two pairs of spin orbitals that differ.
Definition: Hamiltonian_module.f90:233
csf_module::csfobject
Single configuration state function.
Definition: CSF_module.F90:135
hamiltonian_module::my_job
logical function my_job(this)
Used by the easy MPI parallelization of slater loops.
Definition: Hamiltonian_module.f90:480
symbolic_module
Symbolic module.
Definition: Symbolic_Module.f90:33
hamiltonian_module::handle_one_pair
subroutine handle_one_pair(this, dtrs, coeff, sym_element, csf, flag)
Handles if there are one pair of spin orbitals that differ.
Definition: Hamiltonian_module.f90:259
parallelization_module::process_grid
type(processgrid) process_grid
Definition: Parallelization_module.F90:80
basematrix_module
Base matrix module.
Definition: BaseMatrix_module.f90:28
symbolic_module::symbolicelementvector
This class handles the storage symbolic elements.
Definition: Symbolic_Module.f90:57
orbital_module
Target CI Hamiltonian module.
Definition: Orbital_module.f90:31
basematrix_module::basematrix
Base matrix type.
Definition: BaseMatrix_module.f90:47
baseintegral_module
Base integral module.
Definition: BaseIntegral_module.f90:28
parallelization_module
Distribution of processes into a grid.
Definition: Parallelization_module.F90:29
csf_module::csforbital
Single determinant.
Definition: CSF_module.F90:118
baseintegral_module::baseintegral
Definition: BaseIntegral_module.f90:41
orbital_module::spinorbital
This type holds a single spin orbital.
Definition: Orbital_module.f90:52
hamiltonian_module::evaluate_integrals_singular
real(wp) function evaluate_integrals_singular(this, label, coeff, dummy_orb)
Evaluates a single integral from labels and also checks for dummy orbitals.
Definition: Hamiltonian_module.f90:414
options_module
Options module.
Definition: Options_module.f90:41
orbital_module::orbitaltable
This class generates the molecular and spin orbitals, stores them and generates symblic elements from...
Definition: Orbital_module.f90:69
hamiltonian_module
Base Hamiltonian module.
Definition: Hamiltonian_module.f90:33
hamiltonian_module::construct_base_hamiltonian
subroutine construct_base_hamiltonian(this, option, csfs, orbitals, integral)
Constructs the hamiltonain object and provides all the various componenets required for building.
Definition: Hamiltonian_module.f90:122