#include "macros.h" module pchb_excitgen use constants, only: n_int, dp, maxExcit, stdout use SystemData, only: nel, nBasis use fortran_strings, only: to_upper, join, Token_t use util_mod, only: operator(.div.), EnumBase_t, stop_all, operator(.implies.) use exc_gen_class_wrappers, only: UniformSingles_t, WeightedSingles_t use SystemData, only: tUHF use gasci, only: GASSpec_t, LocalGASSpec_t use excitation_generators, only: ClassicAbInitExcitationGenerator_t, & SingleExcitationGenerator_t use exc_gen_class_wrappers, only: UniformSingles_t, WeightedSingles_t use gasci_singles_pc_weighted, only: & PC_WeightedSinglesOptions_t, PC_WeightedSinglesOptions_vals_t, & PC_Weighted_t, do_allocation, print_options use gasci_pchb_main, only: PCHB_OptionSelection_t, PCHB_OptionSelection_vals_t use gasci_pchb_doubles_main, only: PCHB_DoublesOptions_t, PCHB_DoublesOptions_vals_t, & doubles_allocate_and_init => allocate_and_init use gasci_pchb_doubles_select_particles, only: PCHB_ParticleSelection_t better_implicit_none private public :: PCHB_FCI_excit_generator_t, & FCI_PCHB_options_t, FCI_PCHB_Options_vals_t, FCI_PCHB_options_vals public :: FCI_PCHB_user_input, FCI_PCHB_user_input_vals, decide_on_PCHB_options public :: FCI_PCHB_SinglesOptions_t ! Reexpose public :: PCHB_DoublesOptions_t type, extends(ClassicAbInitExcitationGenerator_t) :: PCHB_FCI_excit_generator_t contains private procedure, public :: init end type type, extends(EnumBase_t) :: FCI_PCHB_singles_algorithm_t end type type :: FCI_PCHB_singles_algorithm_vals_t type(FCI_PCHB_singles_algorithm_t) :: & ON_FLY_HEAT_BATH = FCI_PCHB_singles_algorithm_t(1), & UNIFORM = FCI_PCHB_singles_algorithm_t(2), & PC_WEIGHTED = FCI_PCHB_singles_algorithm_t(3) end type type :: FCI_PCHB_SinglesOptions_vals_t type(FCI_PCHB_singles_algorithm_vals_t) :: & algorithm = FCI_PCHB_singles_algorithm_vals_t() type(PC_WeightedSinglesOptions_vals_t) :: & PC_weighted = PC_WeightedSinglesOptions_vals_t() contains procedure, nopass :: from_str => singles_from_keyword end type type(FCI_PCHB_SinglesOptions_vals_t), parameter :: & FCI_PCHB_singles_options_vals = FCI_PCHB_SinglesOptions_vals_t() type :: FCI_PCHB_SinglesOptions_t type(FCI_PCHB_singles_algorithm_t) :: algorithm type(PC_WeightedSinglesOptions_t) :: PC_weighted = PC_WeightedSinglesOptions_t(& FCI_PCHB_singles_options_vals%PC_weighted%drawing%UNDEFINED) contains procedure :: to_str => to_str_FCI_PCHB_SinglesOptions_t end type type :: FCI_PCHB_Options_t type(FCI_PCHB_SinglesOptions_t) :: singles type(PCHB_DoublesOptions_t) :: doubles contains procedure :: assert_validity procedure :: to_str => to_str_FCI_PCHB_Options_t end type type :: FCI_PCHB_Options_vals_t type(FCI_PCHB_SinglesOptions_vals_t) :: singles = FCI_PCHB_SinglesOptions_vals_t() type(PCHB_DoublesOptions_vals_t) :: doubles = PCHB_DoublesOptions_vals_t() end type type(FCI_PCHB_Options_vals_t), parameter :: FCI_PCHB_options_vals = FCI_PCHB_Options_vals_t() type :: FCI_PCHB_OptionsUserInput_t type(PCHB_OptionSelection_t) :: option_selection type(FCI_PCHB_options_t), allocatable :: options end type type :: FCI_PCHB_OptionsUserInput_vals_t type(PCHB_OptionSelection_vals_t) :: option_selection = PCHB_OptionSelection_vals_t() type(FCI_PCHB_options_vals_t) :: options = FCI_PCHB_options_vals_t() end type type(FCI_PCHB_OptionsUserInput_vals_t), parameter :: FCI_PCHB_user_input_vals = FCI_PCHB_OptionsUserInput_vals_t() type(FCI_PCHB_OptionsUserInput_t), allocatable :: FCI_PCHB_user_input contains pure function singles_from_keyword(w) result(res) !! Parse a given keyword into the possible weighting schemes character(*), intent(in) :: w type(FCI_PCHB_SinglesOptions_t) :: res routine_name("from_keyword") character(:), allocatable :: up_w up_w = to_upper(w) associate(vals => FCI_PCHB_singles_options_vals) select case(up_w) case('UNIFORM', 'UNIF:UNIF') res = FCI_PCHB_SinglesOptions_t(vals%algorithm%UNIFORM) case('ON-THE-FLY-HEAT-BATH') res = FCI_PCHB_SinglesOptions_t(vals%algorithm%ON_FLY_HEAT_BATH) case('UNIF:FAST', 'UNIF:FULL', 'FULL:FULL') res = FCI_PCHB_SinglesOptions_t(& vals%algorithm%PC_WEIGHTED, & PC_WeightedSinglesOptions_t(vals%PC_weighted%drawing%from_str(up_w)) & ) case default call stop_all(this_routine, trim(w)//" not a valid singles generator for GAS PCHB.") end select end associate end function pure function to_str_FCI_PCHB_SinglesOptions_t(options) result(res) class(FCI_PCHB_SinglesOptions_t), intent(in) :: options character(:), allocatable :: res routine_name("to_str_FCI_PCHB_SinglesOptions_t") associate(vals => FCI_PCHB_singles_options_vals) if (options%algorithm == vals%algorithm%UNIFORM) then res = 'UNIF:UNIF' else if (options%algorithm == vals%algorithm%ON_FLY_HEAT_BATH) then res = 'ON-THE-FLY-HEAT-BATH' else if (options%algorithm == vals%algorithm%PC_WEIGHTED) then res = options%PC_weighted%drawing%to_str() else call stop_all(this_routine, "Should not be here.") end if end associate end function subroutine init(this, options) class(PCHB_FCI_excit_generator_t), intent(inout) :: this type(FCI_PCHB_Options_t), intent(in) :: options character(*), parameter :: this_routine = 'pchb_excitgen::init' write(stdout, '(A)') 'FCI PCHB with ' // options%to_str() // ' is used' call singles_allocate_and_init(options%singles, this%singles_generator) ! CAS is implemented as a special case of GAS with only one GAS space. ! Since a GAS specification with one GAS space is trivially disconnected, there ! is no point to use the lookup. call doubles_allocate_and_init(& CAS_spec(n_el=nEl, n_spat_orbs=nBasis .div. 2), options%doubles, & .false., this%doubles_generator) end subroutine subroutine singles_allocate_and_init(options, generator) type(FCI_PCHB_SinglesOptions_t), intent(in) :: options class(SingleExcitationGenerator_t), allocatable, intent(inout) :: generator routine_name("pchb_excitgen::singles_allocate_and_init") if (allocated(generator)) then call generator%finalize() deallocate(generator) end if ! luckily many of the singles generators don't require initialization. if (options%algorithm == FCI_PCHB_singles_options_vals%algorithm%ON_FLY_HEAT_BATH) then allocate(WeightedSingles_t :: generator) else if (options%algorithm == FCI_PCHB_singles_options_vals%algorithm%UNIFORM) then allocate(UniformSingles_t :: generator) else if (options%algorithm == FCI_PCHB_singles_options_vals%algorithm%PC_WEIGHTED) then call print_options(options%PC_weighted, stdout) call do_allocation(generator, options%PC_weighted%drawing) select type(generator) class is(PC_Weighted_t) call generator%init(CAS_spec(n_el=nEl, n_spat_orbs=nBasis .div. 2), & use_lookup=.false., create_lookup=.false.) end select else call stop_all(this_routine, "Invalid PCHB_singles in FCI PCHB init.") end if end subroutine type(LocalGASSpec_t) pure function CAS_spec(n_el, n_spat_orbs) integer, intent(in) :: n_el, n_spat_orbs integer :: i ! It does not matter if we use local or cumulative GAS ! constraints CAS_spec = LocalGASSpec_t(& n_min=[n_el], n_max=[n_el], spat_GAS_orbs=[(1, i = 1, n_spat_orbs)]) end function subroutine assert_validity(this) class(FCI_PCHB_options_t), intent(in) :: this routine_name("assert_validity") if (.not. (this%singles%algorithm == FCI_PCHB_options_vals%singles%algorithm%PC_WEIGHTED & .implies. (this%singles%PC_weighted%drawing /= FCI_PCHB_options_vals%singles%PC_weighted%drawing%UNDEFINED))) then call stop_all(this_routine, "PC-WEIGHTED singles require valid PC_weighted options.") end if if (.not. (tUHF .implies. this%doubles%spin_orb_resolved)) then call stop_all(this_routine, "Spin-resolved excitation generation requires spin-resolved hole generation.") end if end subroutine pure function decide_on_PCHB_options(FCI_PCHB_user_input, loc_nBasis, loc_nEl, loc_tUHF) result(res) type(FCI_PCHB_OptionsUserInput_t), intent(in) :: FCI_PCHB_user_input integer, intent(in) :: loc_nBasis, loc_nEl logical, intent(in) :: loc_tUHF type(FCI_PCHB_options_t) :: res routine_name("decide_on_PCHB_options") associate(hole_selection_2 => merge(FCI_PCHB_options_vals%doubles%hole_selection%FAST_FAST, & FCI_PCHB_options_vals%doubles%hole_selection%FULL_FULL, & loc_nBasis > 4 * loc_nEl)) if (FCI_PCHB_user_input%option_selection == FCI_PCHB_user_input_vals%option_selection%LOCALISED) then associate(single_selection => merge(FCI_PCHB_options_vals%singles%PC_WEIGHTED%drawing%UNIF_FAST, & FCI_PCHB_options_vals%singles%PC_WEIGHTED%drawing%UNIF_FULL, & loc_nBasis > 4 * loc_nEl)) res = FCI_PCHB_options_t(& FCI_PCHB_SinglesOptions_t(& FCI_PCHB_options_vals%singles%algorithm%PC_weighted, & PC_WeightedSinglesOptions_t(single_selection) & ), & PCHB_DoublesOptions_t( & FCI_PCHB_options_vals%doubles%particle_selection%UNIF_FULL, & hole_selection_2 & ) & ) end associate else if (FCI_PCHB_user_input%option_selection == FCI_PCHB_user_input_vals%option_selection%DELOCALISED) then res = FCI_PCHB_options_t(& FCI_PCHB_SinglesOptions_t(& FCI_PCHB_options_vals%singles%algorithm%UNIFORM & ), & PCHB_DoublesOptions_t( & FCI_PCHB_options_vals%doubles%particle_selection%UNIF_UNIF, & hole_selection_2 & ) & ) else if (FCI_PCHB_user_input%option_selection == FCI_PCHB_user_input_vals%option_selection%MANUAL) then res = FCI_PCHB_user_input%options else call stop_all(this_routine, "Should not be here.") end if end associate if (.not. allocated(res%doubles%spin_orb_resolved)) then res%doubles%spin_orb_resolved = & loc_tUHF & .or. (res%doubles%hole_selection == FCI_PCHB_options_vals%doubles%hole_selection%FULL_FULL) end if end function function to_str_FCI_PCHB_Options_t(options) result(res) class(FCI_PCHB_Options_t), intent(in) :: options character(:), allocatable :: res res = options%singles%to_str() // ' ' // options%doubles%to_str() end function end module pchb_excitgen