LCOV - code coverage report
Current view: top level - src - trexio_utils.F (source / functions) Hit Total Coverage
Test: CP2K Regtests (git:b4bd748) Lines: 465 695 66.9 %
Date: 2025-03-09 07:56:22 Functions: 4 5 80.0 %

          Line data    Source code
       1             : !--------------------------------------------------------------------------------------------------!
       2             : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3             : !   Copyright 2000-2025 CP2K developers group <https://cp2k.org>                                   !
       4             : !                                                                                                  !
       5             : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6             : !--------------------------------------------------------------------------------------------------!
       7             : 
       8             : ! **************************************************************************************************
       9             : !> \brief The module to read/write TREX IO files for interfacing CP2K with other programs
      10             : !> \par History
      11             : !>      05.2024 created [SB]
      12             : !> \author Stefano Battaglia
      13             : ! **************************************************************************************************
      14             : MODULE trexio_utils
      15             : 
      16             :    USE atomic_kind_types, ONLY: get_atomic_kind
      17             :    USE basis_set_types, ONLY: gto_basis_set_type, get_gto_basis_set
      18             :    USE cell_types, ONLY: cell_type
      19             :    USE cp2k_info, ONLY: cp2k_version
      20             :    USE cp_blacs_env, ONLY: cp_blacs_env_type
      21             :    USE cp_control_types, ONLY: dft_control_type
      22             :    USE cp_dbcsr_operations, ONLY: copy_dbcsr_to_fm
      23             :    USE cp_files, ONLY: close_file, file_exists, open_file
      24             :    USE cp_fm_types, ONLY: cp_fm_get_info, cp_fm_type, cp_fm_create, cp_fm_set_all, &
      25             :                           cp_fm_get_submatrix, cp_fm_to_fm_submat_general, cp_fm_release, &
      26             :                           cp_fm_set_element
      27             :    USE cp_fm_struct, ONLY: cp_fm_struct_create, &
      28             :                            cp_fm_struct_release, &
      29             :                            cp_fm_struct_type
      30             :    USE cp_log_handling, ONLY: cp_get_default_logger, &
      31             :                               cp_logger_get_default_io_unit, &
      32             :                               cp_logger_type
      33             :    USE cp_dbcsr_api, ONLY: dbcsr_p_type, dbcsr_iterator_type, dbcsr_iterator_start, &
      34             :                            dbcsr_iterator_stop, dbcsr_iterator_blocks_left, &
      35             :                            dbcsr_iterator_next_block, &
      36             :                            dbcsr_copy, dbcsr_set
      37             :    USE cp_dbcsr_contrib, ONLY: dbcsr_reserve_all_blocks
      38             :    USE cp_dbcsr_output, ONLY: cp_dbcsr_write_sparse_matrix
      39             :    USE cp_output_handling, ONLY: medium_print_level
      40             :    USE external_potential_types, ONLY: sgp_potential_type, get_potential
      41             :    USE input_section_types, ONLY: section_vals_type, section_vals_get, &
      42             :                                   section_vals_val_get
      43             :    USE kinds, ONLY: default_path_length, dp
      44             :    USE kpoint_types, ONLY: kpoint_type, kpoint_env_p_type, &
      45             :                            get_kpoint_info, get_kpoint_env
      46             :    USE mathconstants, ONLY: fourpi, pi
      47             :    USE message_passing, ONLY: mp_para_env_type
      48             :    USE orbital_pointers, ONLY: nco, nso
      49             :    USE orbital_transformation_matrices, ONLY: orbtramat
      50             :    USE particle_types, ONLY: particle_type
      51             :    USE qs_energy_types, ONLY: qs_energy_type
      52             :    USE qs_environment_types, ONLY: get_qs_env, &
      53             :                                    qs_environment_type
      54             :    USE qs_kind_types, ONLY: get_qs_kind, get_qs_kind_set, &
      55             :                             qs_kind_type
      56             :    USE qs_mo_types, ONLY: mo_set_type, get_mo_set, init_mo_set, allocate_mo_set
      57             : #ifdef __TREXIO
      58             :    USE trexio, ONLY: trexio_open, trexio_close, &
      59             :                      TREXIO_HDF5, TREXIO_SUCCESS, &
      60             :                      trexio_string_of_error, trexio_t, trexio_exit_code, &
      61             :                      trexio_write_metadata_code, trexio_write_metadata_code_num, &
      62             :                      trexio_write_nucleus_coord, trexio_read_nucleus_coord, &
      63             :                      trexio_write_nucleus_num, trexio_read_nucleus_num, &
      64             :                      trexio_write_nucleus_charge, trexio_read_nucleus_charge, &
      65             :                      trexio_write_nucleus_label, trexio_read_nucleus_label, &
      66             :                      trexio_write_nucleus_repulsion, &
      67             :                      trexio_write_cell_a, trexio_write_cell_b, trexio_write_cell_c, &
      68             :                      trexio_write_cell_g_a, trexio_write_cell_g_b, &
      69             :                      trexio_write_cell_g_c, trexio_write_cell_two_pi, &
      70             :                      trexio_write_pbc_periodic, trexio_write_pbc_k_point_num, &
      71             :                      trexio_write_pbc_k_point, trexio_write_pbc_k_point_weight, &
      72             :                      trexio_write_electron_num, trexio_read_electron_num, &
      73             :                      trexio_write_electron_up_num, trexio_read_electron_up_num, &
      74             :                      trexio_write_electron_dn_num, trexio_read_electron_dn_num, &
      75             :                      trexio_write_state_num, trexio_write_state_id, &
      76             :                      trexio_write_state_energy, &
      77             :                      trexio_write_basis_type, trexio_write_basis_prim_num, &
      78             :                      trexio_write_basis_shell_num, trexio_read_basis_shell_num, &
      79             :                      trexio_write_basis_nucleus_index, &
      80             :                      trexio_write_basis_shell_ang_mom, trexio_read_basis_shell_ang_mom, &
      81             :                      trexio_write_basis_shell_factor, &
      82             :                      trexio_write_basis_r_power, trexio_write_basis_shell_index, &
      83             :                      trexio_write_basis_exponent, trexio_write_basis_coefficient, &
      84             :                      trexio_write_basis_prim_factor, &
      85             :                      trexio_write_ecp_z_core, trexio_write_ecp_max_ang_mom_plus_1, &
      86             :                      trexio_write_ecp_num, trexio_write_ecp_ang_mom, &
      87             :                      trexio_write_ecp_nucleus_index, trexio_write_ecp_exponent, &
      88             :                      trexio_write_ecp_coefficient, trexio_write_ecp_power, &
      89             :                      trexio_write_ao_cartesian, trexio_write_ao_num, &
      90             :                      trexio_read_ao_cartesian, trexio_read_ao_num, &
      91             :                      trexio_write_ao_shell, trexio_write_ao_normalization, &
      92             :                      trexio_read_ao_shell, trexio_read_ao_normalization, &
      93             :                      trexio_write_mo_num, trexio_write_mo_energy, &
      94             :                      trexio_read_mo_num, trexio_read_mo_energy, &
      95             :                      trexio_write_mo_occupation, trexio_write_mo_spin, &
      96             :                      trexio_read_mo_occupation, trexio_read_mo_spin, &
      97             :                      trexio_write_mo_class, trexio_write_mo_coefficient, &
      98             :                      trexio_read_mo_class, trexio_read_mo_coefficient, &
      99             :                      trexio_write_mo_coefficient_im, trexio_write_mo_k_point, &
     100             :                      trexio_write_mo_type
     101             : #endif
     102             : #include "./base/base_uses.f90"
     103             : 
     104             :    IMPLICIT NONE
     105             : 
     106             :    PRIVATE
     107             : 
     108             :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'trexio_utils'
     109             : 
     110             :    PUBLIC :: write_trexio, read_trexio
     111             : 
     112             : CONTAINS
     113             : 
     114             : ! **************************************************************************************************
     115             : !> \brief Write a trexio file
     116             : !> \param qs_env the qs environment with all the info of the computation
     117             : !> \param trexio_section the section with the trexio info
     118             : ! **************************************************************************************************
     119           8 :    SUBROUTINE write_trexio(qs_env, trexio_section)
     120             :       TYPE(qs_environment_type), INTENT(IN), POINTER     :: qs_env
     121             :       TYPE(section_vals_type), INTENT(IN), POINTER       :: trexio_section
     122             : 
     123             :       CHARACTER(LEN=*), PARAMETER :: routineN = 'write_trexio'
     124             : 
     125             :       INTEGER                                            :: handle
     126             : 
     127             : #ifdef __TREXIO
     128             :       INTEGER                                            :: output_unit, unit_trexio
     129             :       CHARACTER(len=default_path_length)                 :: filename
     130             :       INTEGER(trexio_t)                                  :: f        ! The TREXIO file handle
     131             :       INTEGER(trexio_exit_code)                          :: rc       ! TREXIO return code
     132             :       LOGICAL                                            :: explicit, do_kpoints, ecp_semi_local, &
     133             :                                                             ecp_local, sgp_potential_present, ionode, &
     134             :                                                             use_real_wfn, save_cartesian
     135             :       REAL(KIND=dp)                                      :: e_nn, zeff, expzet, prefac, zeta, gcca
     136             :       TYPE(cell_type), POINTER                           :: cell => Null()
     137             :       TYPE(cp_logger_type), POINTER                      :: logger => Null()
     138             :       TYPE(dft_control_type), POINTER                    :: dft_control => Null()
     139             :       TYPE(gto_basis_set_type), POINTER                  :: basis_set
     140             :       TYPE(kpoint_type), POINTER                         :: kpoints => Null()
     141             :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set => Null()
     142             :       TYPE(qs_energy_type), POINTER                      :: energy => Null()
     143             :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: kind_set => Null()
     144             :       TYPE(sgp_potential_type), POINTER                  :: sgp_potential => Null()
     145             :       TYPE(mo_set_type), DIMENSION(:), POINTER           :: mos => Null()
     146             :       TYPE(mo_set_type), DIMENSION(:, :), POINTER        :: mos_kp => Null()
     147             :       TYPE(kpoint_env_p_type), DIMENSION(:), POINTER     :: kp_env => Null()
     148             :       TYPE(mp_para_env_type), POINTER                    :: para_env => Null(), para_env_inter_kp => Null()
     149             :       TYPE(cp_blacs_env_type), POINTER                   :: blacs_env => Null()
     150             :       TYPE(cp_fm_struct_type), POINTER                   :: fm_struct => Null()
     151             :       TYPE(cp_fm_type)                                   :: fm_mo_coeff, fm_dummy, fm_mo_coeff_im
     152             : 
     153             :       CHARACTER(LEN=2)                                   :: element_symbol
     154           8 :       CHARACTER(LEN=2), DIMENSION(:), ALLOCATABLE        :: label
     155             :       INTEGER                                            :: iatom, natoms, periodic, nkp, nel_tot, &
     156             :                                                             nspins, ikind, ishell_loc, ishell, &
     157             :                                                             shell_num, prim_num, nset, iset, ipgf, z, &
     158             :                                                             sl_lmax, ecp_num, nloc, nsemiloc, sl_l, iecp, &
     159             :                                                             igf, icgf, ncgf, ngf_shell, lshell, ao_num, nmo, &
     160             :                                                             mo_num, ispin, ikp, imo, ikp_loc, nsgf, &
     161             :                                                             i, k, l, m
     162             :       INTEGER, DIMENSION(2)                              :: nel_spin, kp_range, nmo_spin
     163             :       INTEGER, DIMENSION(3)                              :: nkp_grid
     164             :       INTEGER, DIMENSION(0:10)                           :: npot
     165           8 :       INTEGER, DIMENSION(:), ALLOCATABLE                 :: nucleus_index, shell_ang_mom, r_power, &
     166           8 :                                                             shell_index, z_core, max_ang_mom_plus_1, &
     167           8 :                                                             ang_mom, powers, ao_shell, mo_spin, mo_kpoint
     168             :       INTEGER, DIMENSION(:), POINTER                     :: nshell => Null(), npgf => Null()
     169             :       INTEGER, DIMENSION(:, :), POINTER                  :: l_shell_set => Null()
     170           8 :       REAL(KIND=dp), DIMENSION(:), ALLOCATABLE           :: charge, shell_factor, exponents, coefficients, &
     171           8 :                                                             prim_factor, ao_normalization, mo_energy, &
     172           8 :                                                             mo_occupation
     173             :       REAL(KIND=dp), DIMENSION(:), POINTER               :: wkp => Null(), norm_cgf => Null()
     174           8 :       REAL(KIND=dp), DIMENSION(:, :), ALLOCATABLE        :: coord, mo_coefficient, mo_coefficient_im, &
     175           8 :                                                             mos_sgf, diag_nsgf, diag_ncgf, temp
     176             :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: zetas => Null()
     177             :       REAL(KIND=dp), DIMENSION(:, :, :), POINTER         :: gcc => Null()
     178             : #endif
     179             : 
     180           8 :       CALL timeset(routineN, handle)
     181             : 
     182             : #ifdef __TREXIO
     183           8 :       logger => cp_get_default_logger()
     184           8 :       output_unit = cp_logger_get_default_io_unit(logger)
     185             : 
     186           8 :       CPASSERT(ASSOCIATED(qs_env))
     187             : 
     188             :       ! get filename
     189           8 :       CALL section_vals_val_get(trexio_section, "FILENAME", c_val=filename, explicit=explicit)
     190           8 :       IF (.NOT. explicit) THEN
     191           6 :          filename = TRIM(logger%iter_info%project_name)//'-TREXIO.h5'
     192             :       ELSE
     193           2 :          filename = TRIM(filename)//'.h5'
     194             :       END IF
     195             : 
     196           8 :       CALL get_qs_env(qs_env, para_env=para_env)
     197           8 :       ionode = para_env%is_source()
     198             : 
     199             :       ! inquire whether a file with the same name already exists, if yes, delete it
     200           8 :       IF (ionode) THEN
     201           4 :          IF (file_exists(filename)) THEN
     202           0 :             CALL open_file(filename, unit_number=unit_trexio)
     203           0 :             CALL close_file(unit_number=unit_trexio, file_status="DELETE")
     204             :          END IF
     205             : 
     206             :          !========================================================================================!
     207             :          ! Open the TREXIO file
     208             :          !========================================================================================!
     209           4 :          f = trexio_open(filename, 'w', TREXIO_HDF5, rc)
     210           4 :          CALL trexio_error(rc)
     211             : 
     212             :          !========================================================================================!
     213             :          ! Metadata group
     214             :          !========================================================================================!
     215           4 :          rc = trexio_write_metadata_code_num(f, 1)
     216           4 :          CALL trexio_error(rc)
     217             : 
     218           4 :          rc = trexio_write_metadata_code(f, cp2k_version, LEN_TRIM(cp2k_version) + 1)
     219           4 :          CALL trexio_error(rc)
     220             : 
     221             :          !========================================================================================!
     222             :          ! Nucleus group
     223             :          !========================================================================================!
     224           4 :          CALL get_qs_env(qs_env, cell=cell, particle_set=particle_set, qs_kind_set=kind_set, natom=natoms)
     225             : 
     226           4 :          rc = trexio_write_nucleus_num(f, natoms)
     227           4 :          CALL trexio_error(rc)
     228             : 
     229          12 :          ALLOCATE (coord(3, natoms))
     230           8 :          ALLOCATE (label(natoms))
     231          12 :          ALLOCATE (charge(natoms))
     232          17 :          DO iatom = 1, natoms
     233             :             ! store the coordinates
     234          52 :             coord(:, iatom) = particle_set(iatom)%r(1:3)
     235             :             ! figure out the element symbol and to which kind_set entry this atomic_kind corresponds to
     236          13 :             CALL get_atomic_kind(particle_set(iatom)%atomic_kind, element_symbol=element_symbol, kind_number=ikind)
     237             :             ! store the element symbol
     238          13 :             label(iatom) = element_symbol
     239             :             ! get and store the effective nuclear charge of this kind_type (ikind)
     240          13 :             CALL get_qs_kind(kind_set(ikind), zeff=zeff)
     241          17 :             charge(iatom) = zeff
     242             :          END DO
     243             : 
     244           4 :          rc = trexio_write_nucleus_coord(f, coord)
     245           4 :          CALL trexio_error(rc)
     246           4 :          DEALLOCATE (coord)
     247             : 
     248           4 :          rc = trexio_write_nucleus_charge(f, charge)
     249           4 :          CALL trexio_error(rc)
     250           4 :          DEALLOCATE (charge)
     251             : 
     252           4 :          rc = trexio_write_nucleus_label(f, label, 3)
     253           4 :          CALL trexio_error(rc)
     254           4 :          DEALLOCATE (label)
     255             : 
     256             :          ! nuclear repulsion energy well-defined for molecules only
     257          16 :          IF (SUM(cell%perd) == 0) THEN
     258           2 :             CALL nuclear_repulsion_energy(particle_set, kind_set, e_nn)
     259           2 :             rc = trexio_write_nucleus_repulsion(f, e_nn)
     260           2 :             CALL trexio_error(rc)
     261             :          END IF
     262             : 
     263             :          !========================================================================================!
     264             :          ! Cell group
     265             :          !========================================================================================!
     266           4 :          rc = trexio_write_cell_a(f, cell%hmat(:, 1))
     267           4 :          CALL trexio_error(rc)
     268             : 
     269           4 :          rc = trexio_write_cell_b(f, cell%hmat(:, 2))
     270           4 :          CALL trexio_error(rc)
     271             : 
     272           4 :          rc = trexio_write_cell_c(f, cell%hmat(:, 3))
     273           4 :          CALL trexio_error(rc)
     274             : 
     275           4 :          rc = trexio_write_cell_g_a(f, cell%h_inv(:, 1))
     276           4 :          CALL trexio_error(rc)
     277             : 
     278           4 :          rc = trexio_write_cell_g_b(f, cell%h_inv(:, 2))
     279           4 :          CALL trexio_error(rc)
     280             : 
     281           4 :          rc = trexio_write_cell_g_c(f, cell%h_inv(:, 3))
     282           4 :          CALL trexio_error(rc)
     283             : 
     284           4 :          rc = trexio_write_cell_two_pi(f, 0)
     285           4 :          CALL trexio_error(rc)
     286             : 
     287             :          !========================================================================================!
     288             :          ! PBC group
     289             :          !========================================================================================!
     290           4 :          CALL get_qs_env(qs_env, do_kpoints=do_kpoints, kpoints=kpoints)
     291             : 
     292           4 :          periodic = 0
     293          16 :          IF (SUM(cell%perd) /= 0) periodic = 1
     294           4 :          rc = trexio_write_pbc_periodic(f, periodic)
     295           4 :          CALL trexio_error(rc)
     296             : 
     297           4 :          IF (do_kpoints) THEN
     298           1 :             CALL get_kpoint_info(kpoints, nkp=nkp, nkp_grid=nkp_grid, wkp=wkp)
     299             : 
     300           1 :             rc = trexio_write_pbc_k_point_num(f, nkp)
     301           1 :             CALL trexio_error(rc)
     302             : 
     303           4 :             rc = trexio_write_pbc_k_point(f, REAL(nkp_grid, KIND=dp))
     304           1 :             CALL trexio_error(rc)
     305             : 
     306           1 :             rc = trexio_write_pbc_k_point_weight(f, wkp)
     307           1 :             CALL trexio_error(rc)
     308             :          END IF
     309             : 
     310             :          !========================================================================================!
     311             :          ! Electron group
     312             :          !========================================================================================!
     313           4 :          CALL get_qs_env(qs_env, dft_control=dft_control, nelectron_total=nel_tot)
     314             : 
     315           4 :          rc = trexio_write_electron_num(f, nel_tot)
     316           4 :          CALL trexio_error(rc)
     317             : 
     318           4 :          nspins = dft_control%nspins
     319           4 :          IF (nspins == 1) THEN
     320             :             ! it is a spin-restricted calculation and we need to split the electrons manually,
     321             :             ! because in CP2K they are all otherwise weirdly stored in nelectron_spin(1)
     322           3 :             nel_spin(1) = nel_tot/2
     323           3 :             nel_spin(2) = nel_tot/2
     324             :          ELSE
     325             :             ! for UKS/ROKS, the two spin channels are populated correctly and according to
     326             :             ! the multiplicity
     327           1 :             CALL get_qs_env(qs_env, nelectron_spin=nel_spin)
     328             :          END IF
     329           4 :          rc = trexio_write_electron_up_num(f, nel_spin(1))
     330           4 :          CALL trexio_error(rc)
     331           4 :          rc = trexio_write_electron_dn_num(f, nel_spin(2))
     332           4 :          CALL trexio_error(rc)
     333             : 
     334             :          !========================================================================================!
     335             :          ! State group
     336             :          !========================================================================================!
     337           4 :          CALL get_qs_env(qs_env, energy=energy)
     338             : 
     339           4 :          rc = trexio_write_state_num(f, 1)
     340           4 :          CALL trexio_error(rc)
     341             : 
     342           4 :          rc = trexio_write_state_id(f, 1)
     343           4 :          CALL trexio_error(rc)
     344             : 
     345           4 :          rc = trexio_write_state_energy(f, energy%total)
     346           4 :          CALL trexio_error(rc)
     347             : 
     348             :       END IF ! ionode
     349             : 
     350             :       !========================================================================================!
     351             :       ! Basis group
     352             :       !========================================================================================!
     353           8 :       CALL get_qs_env(qs_env, qs_kind_set=kind_set, natom=natoms, particle_set=particle_set)
     354           8 :       CALL get_qs_kind_set(kind_set, nshell=shell_num, npgf_seg=prim_num)
     355             : 
     356           8 :       IF (ionode) THEN
     357           4 :          rc = trexio_write_basis_type(f, 'Gaussian', LEN_TRIM('Gaussian') + 1)
     358           4 :          CALL trexio_error(rc)
     359             : 
     360           4 :          rc = trexio_write_basis_shell_num(f, shell_num)
     361           4 :          CALL trexio_error(rc)
     362             : 
     363           4 :          rc = trexio_write_basis_prim_num(f, prim_num)
     364           4 :          CALL trexio_error(rc)
     365             :       END IF ! ionode
     366             : 
     367             :       ! one-to-one mapping between shells and ...
     368          24 :       ALLOCATE (nucleus_index(shell_num)) ! ...atomic indices
     369          16 :       ALLOCATE (shell_ang_mom(shell_num)) ! ...angular momenta
     370          24 :       ALLOCATE (shell_index(prim_num))    ! ...indices of primitive functions
     371          24 :       ALLOCATE (exponents(prim_num))      ! ...primitive exponents
     372          16 :       ALLOCATE (coefficients(prim_num))   ! ...contraction coefficients
     373          16 :       ALLOCATE (prim_factor(prim_num))    ! ...primitive normalization factors
     374             : 
     375           8 :       ishell = 0
     376           8 :       ipgf = 0
     377          34 :       DO iatom = 1, natoms
     378             :          ! get the qs_kind (index position in kind_set) for this atom (atomic_kind)
     379          26 :          CALL get_atomic_kind(particle_set(iatom)%atomic_kind, kind_number=ikind)
     380             :          ! get the primary (orbital) basis set associated to this qs_kind
     381          26 :          CALL get_qs_kind(kind_set(ikind), basis_set=basis_set, basis_type="ORB")
     382             :          ! get the info from the basis set
     383             :          CALL get_gto_basis_set(basis_set, &
     384             :                                 nset=nset, &
     385             :                                 nshell=nshell, &
     386             :                                 npgf=npgf, &
     387             :                                 zet=zetas, &
     388             :                                 gcc=gcc, &
     389          26 :                                 l=l_shell_set)
     390             : 
     391         148 :          DO iset = 1, nset
     392         252 :             DO ishell_loc = 1, nshell(iset)
     393         138 :                ishell = ishell + 1
     394             : 
     395             :                ! nucleus_index array
     396         138 :                nucleus_index(ishell) = iatom
     397             : 
     398             :                ! shell_ang_mom array
     399         138 :                l = l_shell_set(ishell_loc, iset)
     400         138 :                shell_ang_mom(ishell) = l
     401             : 
     402             :                ! shell_index array
     403         512 :                shell_index(ipgf + 1:ipgf + npgf(iset)) = ishell
     404             : 
     405             :                ! exponents array
     406         512 :                exponents(ipgf + 1:ipgf + npgf(iset)) = zetas(1:npgf(iset), iset)
     407             : 
     408             :                ! compute on the fly the normalization factor as in normalise_gcc_orb
     409             :                ! and recover the original contraction coefficients to store them separately
     410         138 :                expzet = 0.25_dp*REAL(2*l + 3, dp)
     411         138 :                prefac = 2.0_dp**l*(2.0_dp/pi)**0.75_dp
     412         512 :                DO i = 1, npgf(iset)
     413         374 :                   gcca = gcc(i, ishell_loc, iset)
     414         374 :                   zeta = zetas(i, iset)
     415             : 
     416             :                   ! primitives normalization factors array
     417         374 :                   prim_factor(i + ipgf) = prefac*zeta**expzet
     418             : 
     419             :                   ! contractio coefficients array
     420         512 :                   coefficients(i + ipgf) = gcca/prim_factor(i + ipgf)
     421             :                END DO
     422             : 
     423         226 :                ipgf = ipgf + npgf(iset)
     424             :             END DO
     425             :          END DO
     426             :       END DO
     427             :       ! just a failsafe check
     428           8 :       CPASSERT(ishell == shell_num)
     429           8 :       CPASSERT(ipgf == prim_num)
     430             : 
     431           8 :       IF (ionode) THEN
     432           4 :          rc = trexio_write_basis_nucleus_index(f, nucleus_index)
     433           4 :          CALL trexio_error(rc)
     434             : 
     435           4 :          rc = trexio_write_basis_shell_ang_mom(f, shell_ang_mom)
     436           4 :          CALL trexio_error(rc)
     437             : 
     438             :          ! Normalization factors are shoved in the AO group
     439          12 :          ALLOCATE (shell_factor(shell_num))  ! 1-to-1 map bw shells and normalization factors
     440          73 :          shell_factor(:) = 1.0_dp
     441           4 :          rc = trexio_write_basis_shell_factor(f, shell_factor)
     442           4 :          CALL trexio_error(rc)
     443           4 :          DEALLOCATE (shell_factor)
     444             : 
     445             :          ! This is always 0 for Gaussian basis sets
     446          12 :          ALLOCATE (r_power(shell_num))       ! 1-to-1 map bw shells radial function powers
     447          73 :          r_power(:) = 0
     448           4 :          rc = trexio_write_basis_r_power(f, r_power)
     449           4 :          CALL trexio_error(rc)
     450           4 :          DEALLOCATE (r_power)
     451             : 
     452           4 :          rc = trexio_write_basis_shell_index(f, shell_index)
     453           4 :          CALL trexio_error(rc)
     454             : 
     455           4 :          rc = trexio_write_basis_exponent(f, exponents)
     456           4 :          CALL trexio_error(rc)
     457             : 
     458           4 :          rc = trexio_write_basis_coefficient(f, coefficients)
     459           4 :          CALL trexio_error(rc)
     460             : 
     461             :          ! Normalization factors are shoved in the AO group
     462           4 :          rc = trexio_write_basis_prim_factor(f, prim_factor)
     463           4 :          CALL trexio_error(rc)
     464             :       END IF
     465             : 
     466           8 :       DEALLOCATE (nucleus_index)
     467           8 :       DEALLOCATE (shell_index)
     468           8 :       DEALLOCATE (exponents)
     469           8 :       DEALLOCATE (coefficients)
     470           8 :       DEALLOCATE (prim_factor)
     471             :       ! shell_ang_mom is needed in the MO group, so will be deallocated there
     472             : 
     473             :       !========================================================================================!
     474             :       ! ECP group
     475             :       !========================================================================================!
     476           8 :       IF (ionode) THEN
     477           4 :          CALL get_qs_kind_set(kind_set, sgp_potential_present=sgp_potential_present)
     478             : 
     479             :          ! figure out whether we actually have ECP potentials
     480           4 :          ecp_num = 0
     481           4 :          IF (sgp_potential_present) THEN
     482           4 :             DO iatom = 1, natoms
     483             :                ! get the qs_kind (index position in kind_set) for this atom (atomic_kind)
     484           2 :                CALL get_atomic_kind(particle_set(iatom)%atomic_kind, kind_number=ikind)
     485             :                ! get the the sgp_potential associated to this qs_kind
     486           2 :                CALL get_qs_kind(kind_set(ikind), sgp_potential=sgp_potential)
     487             : 
     488             :                ! get the info on the potential
     489           6 :                IF (ASSOCIATED(sgp_potential)) THEN
     490           2 :                   CALL get_potential(potential=sgp_potential, ecp_local=ecp_local, ecp_semi_local=ecp_semi_local)
     491           2 :                   IF (ecp_local) THEN
     492             :                      ! get number of local terms
     493           2 :                      CALL get_potential(potential=sgp_potential, nloc=nloc)
     494           2 :                      ecp_num = ecp_num + nloc
     495             :                   END IF
     496           2 :                   IF (ecp_semi_local) THEN
     497             :                      ! get number of semilocal terms
     498           2 :                      CALL get_potential(potential=sgp_potential, npot=npot)
     499          24 :                      ecp_num = ecp_num + SUM(npot)
     500             :                   END IF
     501             :                END IF
     502             :             END DO
     503             :          END IF
     504             : 
     505             :          ! if we have ECP potentials, populate the ECP group
     506           2 :          IF (ecp_num > 0) THEN
     507           6 :             ALLOCATE (z_core(natoms))
     508           4 :             ALLOCATE (max_ang_mom_plus_1(natoms))
     509           4 :             max_ang_mom_plus_1(:) = 0
     510             : 
     511           6 :             ALLOCATE (ang_mom(ecp_num))
     512           4 :             ALLOCATE (nucleus_index(ecp_num))
     513           6 :             ALLOCATE (exponents(ecp_num))
     514           4 :             ALLOCATE (coefficients(ecp_num))
     515           4 :             ALLOCATE (powers(ecp_num))
     516             : 
     517           4 :             iecp = 0
     518           4 :             DO iatom = 1, natoms
     519             :                ! get the qs_kind (index position in kind_set) for this atom (atomic_kind)
     520           2 :                CALL get_atomic_kind(particle_set(iatom)%atomic_kind, kind_number=ikind, z=z)
     521             :                ! get the the sgp_potential associated to this qs_kind
     522           2 :                CALL get_qs_kind(kind_set(ikind), sgp_potential=sgp_potential, zeff=zeff)
     523             : 
     524             :                ! number of core electrons removed by the ECP
     525           2 :                z_core(iatom) = z - INT(zeff)
     526             : 
     527             :                ! get the info on the potential
     528           4 :                IF (ASSOCIATED(sgp_potential)) THEN
     529           2 :                   CALL get_potential(potential=sgp_potential, ecp_local=ecp_local, ecp_semi_local=ecp_semi_local)
     530             : 
     531             :                   ! deal with the local part
     532           2 :                   IF (ecp_local) THEN
     533           2 :                      CALL get_potential(potential=sgp_potential, nloc=nloc, sl_lmax=sl_lmax)
     534           4 :                      ang_mom(iecp + 1:iecp + nloc) = sl_lmax + 1
     535           4 :                      nucleus_index(iecp + 1:iecp + nloc) = iatom
     536           4 :                      exponents(iecp + 1:iecp + nloc) = sgp_potential%bloc(1:nloc)
     537           4 :                      coefficients(iecp + 1:iecp + nloc) = sgp_potential%aloc(1:nloc)
     538           4 :                      powers(iecp + 1:iecp + nloc) = sgp_potential%nrloc(1:nloc) - 2
     539             :                      iecp = iecp + nloc
     540             :                   END IF
     541             : 
     542             :                   ! deal with the semilocal part
     543           2 :                   IF (ecp_semi_local) THEN
     544           2 :                      CALL get_potential(potential=sgp_potential, npot=npot, sl_lmax=sl_lmax)
     545           2 :                      max_ang_mom_plus_1(iatom) = sl_lmax + 1
     546             : 
     547           8 :                      DO sl_l = 0, sl_lmax
     548           6 :                         nsemiloc = npot(sl_l)
     549          16 :                         ang_mom(iecp + 1:iecp + nsemiloc) = sl_l
     550          16 :                         nucleus_index(iecp + 1:iecp + nsemiloc) = iatom
     551          16 :                         exponents(iecp + 1:iecp + nsemiloc) = sgp_potential%bpot(1:nsemiloc, sl_l)
     552          16 :                         coefficients(iecp + 1:iecp + nsemiloc) = sgp_potential%apot(1:nsemiloc, sl_l)
     553          16 :                         powers(iecp + 1:iecp + nsemiloc) = sgp_potential%nrpot(1:nsemiloc, sl_l) - 2
     554           8 :                         iecp = iecp + nsemiloc
     555             :                      END DO
     556             :                   END IF
     557             :                END IF
     558             :             END DO
     559             : 
     560             :             ! fail-safe check
     561           2 :             CPASSERT(iecp == ecp_num)
     562             : 
     563           2 :             rc = trexio_write_ecp_num(f, ecp_num)
     564           2 :             CALL trexio_error(rc)
     565             : 
     566           2 :             rc = trexio_write_ecp_z_core(f, z_core)
     567           2 :             CALL trexio_error(rc)
     568           2 :             DEALLOCATE (z_core)
     569             : 
     570           2 :             rc = trexio_write_ecp_max_ang_mom_plus_1(f, max_ang_mom_plus_1)
     571           2 :             CALL trexio_error(rc)
     572           2 :             DEALLOCATE (max_ang_mom_plus_1)
     573             : 
     574           2 :             rc = trexio_write_ecp_ang_mom(f, ang_mom)
     575           2 :             CALL trexio_error(rc)
     576           2 :             DEALLOCATE (ang_mom)
     577             : 
     578           2 :             rc = trexio_write_ecp_nucleus_index(f, nucleus_index)
     579           2 :             CALL trexio_error(rc)
     580           2 :             DEALLOCATE (nucleus_index)
     581             : 
     582           2 :             rc = trexio_write_ecp_exponent(f, exponents)
     583           2 :             CALL trexio_error(rc)
     584           2 :             DEALLOCATE (exponents)
     585             : 
     586           2 :             rc = trexio_write_ecp_coefficient(f, coefficients)
     587           2 :             CALL trexio_error(rc)
     588           2 :             DEALLOCATE (coefficients)
     589             : 
     590           2 :             rc = trexio_write_ecp_power(f, powers)
     591           2 :             CALL trexio_error(rc)
     592           2 :             DEALLOCATE (powers)
     593             :          END IF
     594             : 
     595             :       END IF ! ionode
     596             : 
     597             :       !========================================================================================!
     598             :       ! Grid group
     599             :       !========================================================================================!
     600             :       ! TODO
     601             : 
     602             :       !========================================================================================!
     603             :       ! AO group
     604             :       !========================================================================================!
     605           8 :       CALL get_qs_env(qs_env, qs_kind_set=kind_set)
     606           8 :       CALL get_qs_kind_set(kind_set, ncgf=ncgf, nsgf=nsgf)
     607             : 
     608           8 :       CALL section_vals_val_get(trexio_section, "CARTESIAN", l_val=save_cartesian)
     609           8 :       IF (save_cartesian) THEN
     610           4 :          ao_num = ncgf
     611             :       ELSE
     612           4 :          ao_num = nsgf
     613             :       END IF
     614             : 
     615           8 :       IF (ionode) THEN
     616           4 :          IF (save_cartesian) THEN
     617           2 :             rc = trexio_write_ao_cartesian(f, 1)
     618             :          ELSE
     619           2 :             rc = trexio_write_ao_cartesian(f, 0)
     620             :          END IF
     621           4 :          CALL trexio_error(rc)
     622             : 
     623           4 :          rc = trexio_write_ao_num(f, ao_num)
     624           4 :          CALL trexio_error(rc)
     625             :       END IF
     626             : 
     627             :       ! one-to-one mapping between AOs and ...
     628          24 :       ALLOCATE (ao_shell(ao_num))         ! ..shells
     629          24 :       ALLOCATE (ao_normalization(ao_num)) ! ..normalization factors
     630             : 
     631             :       ! we need to be consistent with the basis group on the shell indices
     632           8 :       ishell = 0  ! global shell index
     633           8 :       igf = 0     ! global AO index
     634          34 :       DO iatom = 1, natoms
     635             :          ! get the qs_kind (index position in kind_set) for this atom (atomic_kind)
     636          26 :          CALL get_atomic_kind(particle_set(iatom)%atomic_kind, kind_number=ikind)
     637             :          ! get the primary (orbital) basis set associated to this qs_kind
     638          26 :          CALL get_qs_kind(kind_set(ikind), basis_set=basis_set, basis_type="ORB")
     639             :          ! get the info from the basis set
     640             :          CALL get_gto_basis_set(basis_set, &
     641             :                                 nset=nset, &
     642             :                                 nshell=nshell, &
     643             :                                 norm_cgf=norm_cgf, &
     644             :                                 ncgf=ncgf, &
     645             :                                 nsgf=nsgf, &
     646          26 :                                 l=l_shell_set)
     647             : 
     648          26 :          icgf = 0
     649         114 :          DO iset = 1, nset
     650         252 :             DO ishell_loc = 1, nshell(iset)
     651             :                ! global shell index
     652         138 :                ishell = ishell + 1
     653             :                ! angular momentum l of this shell
     654         138 :                lshell = l_shell_set(ishell_loc, iset)
     655             : 
     656             :                ! number of AOs in this shell
     657         138 :                IF (save_cartesian) THEN
     658          34 :                   ngf_shell = nco(lshell)
     659             :                ELSE
     660         104 :                   ngf_shell = nso(lshell)
     661             :                END IF
     662             : 
     663             :                ! one-to-one mapping between AOs and shells
     664         524 :                ao_shell(igf + 1:igf + ngf_shell) = ishell
     665             : 
     666             :                ! one-to-one mapping between AOs and normalization factors
     667         138 :                IF (save_cartesian) THEN
     668         136 :                   ao_normalization(igf + 1:igf + ngf_shell) = norm_cgf(icgf + 1:icgf + ngf_shell)
     669             :                ELSE
     670             :                   ! allocate some temporary arrays
     671         416 :                   ALLOCATE (diag_ncgf(nco(lshell), nco(lshell)))
     672         416 :                   ALLOCATE (diag_nsgf(nso(lshell), nso(lshell)))
     673         416 :                   ALLOCATE (temp(nso(lshell), nco(lshell)))
     674        1808 :                   diag_ncgf = 0.0_dp
     675        1436 :                   diag_nsgf = 0.0_dp
     676        1616 :                   temp = 0.0_dp
     677             : 
     678         416 :                   DO i = 1, nco(lshell)
     679         416 :                      diag_ncgf(i, i) = norm_cgf(icgf + i)
     680             :                   END DO
     681             : 
     682             :                   ! transform the normalization factors from Cartesian to solid harmonics
     683       42200 :                   temp(:, :) = MATMUL(orbtramat(lshell)%c2s, diag_ncgf)
     684       24520 :                   diag_nsgf(:, :) = MATMUL(temp, TRANSPOSE(orbtramat(lshell)%s2c))
     685         388 :                   DO i = 1, nso(lshell)
     686         388 :                      ao_normalization(igf + i) = diag_nsgf(i, i)
     687             :                   END DO
     688             : 
     689         104 :                   DEALLOCATE (diag_ncgf)
     690         104 :                   DEALLOCATE (diag_nsgf)
     691         104 :                   DEALLOCATE (temp)
     692             :                END IF
     693             : 
     694         138 :                igf = igf + ngf_shell
     695         226 :                icgf = icgf + nco(lshell)
     696             :             END DO
     697             :          END DO
     698             :          ! just a failsafe check
     699          86 :          CPASSERT(icgf == ncgf)
     700             :       END DO
     701             : 
     702           8 :       IF (ionode) THEN
     703           4 :          rc = trexio_write_ao_shell(f, ao_shell)
     704           4 :          CALL trexio_error(rc)
     705             : 
     706           4 :          rc = trexio_write_ao_normalization(f, ao_normalization)
     707           4 :          CALL trexio_error(rc)
     708             :       END IF
     709             : 
     710           8 :       DEALLOCATE (ao_shell)
     711           8 :       DEALLOCATE (ao_normalization)
     712             : 
     713             :       !========================================================================================!
     714             :       ! MO group
     715             :       !========================================================================================!
     716             :       CALL get_qs_env(qs_env, do_kpoints=do_kpoints, kpoints=kpoints, dft_control=dft_control, &
     717           8 :                       particle_set=particle_set, qs_kind_set=kind_set, blacs_env=blacs_env)
     718           8 :       nspins = dft_control%nspins
     719           8 :       CALL get_qs_kind_set(kind_set, nsgf=nsgf)
     720           8 :       nmo_spin = 0
     721             : 
     722             :       ! figure out that total number of MOs
     723           8 :       mo_num = 0
     724           8 :       IF (do_kpoints) THEN
     725           2 :          CALL get_kpoint_info(kpoints, kp_env=kp_env, nkp=nkp, use_real_wfn=use_real_wfn)
     726           2 :          CALL get_kpoint_env(kp_env(1)%kpoint_env, mos=mos_kp)
     727           4 :          DO ispin = 1, nspins
     728           2 :             CALL get_mo_set(mos_kp(1, ispin), nmo=nmo)
     729           4 :             nmo_spin(ispin) = nmo
     730             :          END DO
     731           6 :          mo_num = nkp*SUM(nmo_spin)
     732             : 
     733             :          ! we create a distributed fm matrix to gather the MOs from everywhere (in sph basis)
     734             :          CALL cp_fm_struct_create(fm_struct, para_env=para_env, context=blacs_env, &
     735           2 :                                   nrow_global=nsgf, ncol_global=mo_num)
     736           2 :          CALL cp_fm_create(fm_mo_coeff, fm_struct)
     737           2 :          CALL cp_fm_set_all(fm_mo_coeff, 0.0_dp)
     738           2 :          IF (.NOT. use_real_wfn) THEN
     739           2 :             CALL cp_fm_create(fm_mo_coeff_im, fm_struct)
     740           2 :             CALL cp_fm_set_all(fm_mo_coeff_im, 0.0_dp)
     741             :          END IF
     742           2 :          CALL cp_fm_struct_release(fm_struct)
     743             :       ELSE
     744           6 :          CALL get_qs_env(qs_env, mos=mos)
     745          14 :          DO ispin = 1, nspins
     746           8 :             CALL get_mo_set(mos(ispin), nmo=nmo)
     747          14 :             nmo_spin(ispin) = nmo
     748             :          END DO
     749          18 :          mo_num = SUM(nmo_spin)
     750             :       END IF
     751             : 
     752             :       ! allocate all the arrays
     753          32 :       ALLOCATE (mo_coefficient(ao_num, mo_num))
     754       33432 :       mo_coefficient(:, :) = 0.0_dp
     755          24 :       ALLOCATE (mo_energy(mo_num))
     756         436 :       mo_energy(:) = 0.0_dp
     757          16 :       ALLOCATE (mo_occupation(mo_num))
     758         436 :       mo_occupation(:) = 0.0_dp
     759          24 :       ALLOCATE (mo_spin(mo_num))
     760         436 :       mo_spin(:) = 0
     761             :       ! extra arrays for kpoints
     762           8 :       IF (do_kpoints) THEN
     763           6 :          ALLOCATE (mo_coefficient_im(ao_num, mo_num))
     764       26882 :          mo_coefficient_im(:, :) = 0.0_dp
     765           4 :          ALLOCATE (mo_kpoint(mo_num))
     766         258 :          mo_kpoint(:) = 0
     767             :       END IF
     768             : 
     769             :       ! in case of kpoints, we do this in 2 steps:
     770             :       ! 1. we gather the MOs of each kpt and pipe them into a single large distributed fm matrix;
     771             :       ! 2. we possibly transform the MOs of each kpt to Cartesian AOs and write them in the single large local array;
     772           8 :       IF (do_kpoints) THEN
     773           2 :          CALL get_kpoint_info(kpoints, kp_env=kp_env, nkp=nkp, kp_range=kp_range)
     774             : 
     775           4 :          DO ispin = 1, nspins
     776          20 :             DO ikp = 1, nkp
     777          16 :                nmo = nmo_spin(ispin)
     778             :                ! global index to store the MOs
     779          16 :                imo = (ikp - 1)*nmo + (ispin - 1)*nmo_spin(1)*nkp
     780             : 
     781             :                ! do I have this kpoint on this rank?
     782          18 :                IF (ikp >= kp_range(1) .AND. ikp <= kp_range(2)) THEN
     783          16 :                   ikp_loc = ikp - kp_range(1) + 1
     784             :                   ! get the mo set for this kpoint
     785          16 :                   CALL get_kpoint_env(kp_env(ikp_loc)%kpoint_env, mos=mos_kp)
     786             : 
     787             :                   ! if MOs are stored with dbcsr, copy them to fm
     788          16 :                   IF (mos_kp(1, ispin)%use_mo_coeff_b) THEN
     789           0 :                      CALL copy_dbcsr_to_fm(mos_kp(1, ispin)%mo_coeff_b, mos_kp(1, ispin)%mo_coeff)
     790             :                   END IF
     791             :                   ! copy real part of MO coefficients to large distributed fm matrix
     792             :                   CALL cp_fm_to_fm_submat_general(mos_kp(1, ispin)%mo_coeff, fm_mo_coeff, &
     793          16 :                                                   nsgf, nmo, 1, 1, 1, imo + 1, blacs_env)
     794             : 
     795             :                   ! copy MO energies to local arrays
     796         272 :                   mo_energy(imo + 1:imo + nmo) = mos_kp(1, ispin)%eigenvalues(1:nmo)
     797             : 
     798             :                   ! copy MO occupations to local arrays
     799         272 :                   mo_occupation(imo + 1:imo + nmo) = mos_kp(1, ispin)%occupation_numbers(1:nmo)
     800             : 
     801             :                   ! same for the imaginary part of MO coefficients
     802          16 :                   IF (.NOT. use_real_wfn) THEN
     803          16 :                      IF (mos_kp(2, ispin)%use_mo_coeff_b) THEN
     804           0 :                         CALL copy_dbcsr_to_fm(mos_kp(2, ispin)%mo_coeff_b, mos_kp(2, ispin)%mo_coeff)
     805             :                      END IF
     806             :                      CALL cp_fm_to_fm_submat_general(mos_kp(2, ispin)%mo_coeff, fm_mo_coeff_im, &
     807          16 :                                                      nsgf, nmo, 1, 1, 1, imo + 1, blacs_env)
     808             :                   END IF
     809             :                ELSE
     810             :                   ! call with a dummy fm for receiving the data
     811             :                   CALL cp_fm_to_fm_submat_general(fm_dummy, fm_mo_coeff, &
     812           0 :                                                   nsgf, nmo, 1, 1, 1, imo + 1, blacs_env)
     813           0 :                   IF (.NOT. use_real_wfn) THEN
     814             :                      CALL cp_fm_to_fm_submat_general(fm_dummy, fm_mo_coeff_im, &
     815           0 :                                                      nsgf, nmo, 1, 1, 1, imo + 1, blacs_env)
     816             :                   END IF
     817             :                END IF
     818             :             END DO
     819             :          END DO
     820             :       END IF
     821             : 
     822             :       ! reduce MO energies and occupations to the master node
     823           8 :       IF (do_kpoints) THEN
     824           2 :          CALL get_kpoint_info(kpoints, para_env_inter_kp=para_env_inter_kp)
     825           2 :          CALL para_env_inter_kp%sum(mo_energy)
     826           2 :          CALL para_env_inter_kp%sum(mo_occupation)
     827             :       END IF
     828             : 
     829             :       ! second step: here we actually put everything in the local arrays for writing to trexio
     830          18 :       DO ispin = 1, nspins
     831             :          ! get number of MOs for this spin
     832          10 :          nmo = nmo_spin(ispin)
     833             :          ! allocate local temp array to transform the MOs of each kpoint/spin
     834          40 :          ALLOCATE (mos_sgf(nsgf, nmo))
     835             : 
     836          10 :          IF (do_kpoints) THEN
     837          18 :             DO ikp = 1, nkp
     838             :                ! global index to store the MOs
     839          16 :                imo = (ikp - 1)*nmo + (ispin - 1)*nmo_spin(1)*nkp
     840             : 
     841             :                ! store kpoint index
     842         272 :                mo_kpoint(imo + 1:imo + nmo) = ikp
     843             :                ! store the MO spins
     844         272 :                mo_spin(imo + 1:imo + nmo) = ispin - 1
     845             : 
     846             :                ! transform and store the MO coefficients
     847          16 :                CALL cp_fm_get_submatrix(fm_mo_coeff, mos_sgf, 1, imo + 1, nsgf, nmo)
     848          16 :                IF (save_cartesian) THEN
     849           0 :                   CALL spherical_to_cartesian_mo(mos_sgf, particle_set, kind_set, mo_coefficient(:, imo + 1:imo + nmo))
     850             :                ELSE
     851             :                   ! we have to reorder the MOs since CP2K and TREXIO have different conventions
     852             :                   ! from m = -l, -l+1, ..., 0, ..., l-1, l   of CP2K
     853             :                   ! to   m =  0, +1, -1, +2, -2, ..., +l, -l of TREXIO
     854          16 :                   i = 0
     855         656 :                   DO ishell = 1, shell_num
     856         640 :                      l = shell_ang_mom(ishell)
     857        2304 :                      DO k = 1, 2*l + 1
     858             :                         ! map from running index k: 1...2l+1 to magnetic quantum number m in TREXIO convention
     859        1664 :                         m = (-1)**k*FLOOR(REAL(k, KIND=dp)/2.0_dp)
     860       28928 :                         mo_coefficient(i + k, imo + 1:imo + nmo) = mos_sgf(i + l + m + 1, :)
     861             :                      END DO
     862         656 :                      i = i + 2*l + 1
     863             :                   END DO
     864          16 :                   CPASSERT(i == nsgf)
     865             :                END IF
     866             : 
     867             :                ! we have to do it for the imaginary part as well
     868          18 :                IF (.NOT. use_real_wfn) THEN
     869          16 :                   CALL cp_fm_get_submatrix(fm_mo_coeff_im, mos_sgf, 1, imo + 1, nsgf, nmo)
     870          16 :                   IF (save_cartesian) THEN
     871           0 :                      CALL spherical_to_cartesian_mo(mos_sgf, particle_set, kind_set, mo_coefficient_im(:, imo + 1:imo + nmo))
     872             :                   ELSE
     873             :                      ! we have to reorder the MOs since CP2K and TREXIO have different conventions
     874             :                      ! from m = -l, -l+1, ..., 0, ..., l-1, l   of CP2K
     875             :                      ! to   m =  0, +1, -1, +2, -2, ..., +l, -l of TREXIO
     876          16 :                      i = 0
     877         656 :                      DO ishell = 1, shell_num
     878         640 :                         l = shell_ang_mom(ishell)
     879        2304 :                         DO k = 1, 2*l + 1
     880             :                            ! map from running index k: 1...2l+1 to magnetic quantum number m in TREXIO convention
     881        1664 :                            m = (-1)**k*FLOOR(REAL(k, KIND=dp)/2.0_dp)
     882       28928 :                            mo_coefficient_im(i + k, imo + 1:imo + nmo) = mos_sgf(i + l + m + 1, :)
     883             :                         END DO
     884         656 :                         i = i + 2*l + 1
     885             :                      END DO
     886          16 :                      CPASSERT(i == nsgf)
     887             :                   END IF
     888             :                END IF
     889             :             END DO
     890             :          ELSE ! no k-points
     891             :             ! global index to store the MOs
     892           8 :             imo = (ispin - 1)*nmo_spin(1)
     893             :             ! store the MO energies
     894         180 :             mo_energy(imo + 1:imo + nmo) = mos(ispin)%eigenvalues
     895             :             ! store the MO occupations
     896         180 :             mo_occupation(imo + 1:imo + nmo) = mos(ispin)%occupation_numbers
     897             :             ! store the MO spins
     898         180 :             mo_spin(imo + 1:imo + nmo) = ispin - 1
     899             : 
     900             :             ! check if we are using the dbcsr mo_coeff and copy them to fm if needed
     901           8 :             IF (mos(ispin)%use_mo_coeff_b) CALL copy_dbcsr_to_fm(mos(ispin)%mo_coeff_b, mos(ispin)%mo_coeff)
     902             : 
     903             :             ! allocate a normal fortran array to store the spherical MO coefficients
     904           8 :             CALL cp_fm_get_submatrix(mos(ispin)%mo_coeff, mos_sgf)
     905             : 
     906           8 :             IF (save_cartesian) THEN
     907           6 :                CALL spherical_to_cartesian_mo(mos_sgf, particle_set, kind_set, mo_coefficient(:, imo + 1:imo + nmo))
     908             :             ELSE
     909             :                ! we have to reorder the MOs since CP2K and TREXIO have different conventions
     910             :                ! from m = -l, -l+1, ..., 0, ..., l-1, l   of CP2K
     911             :                ! to   m =  0, +1, -1, +2, -2, ..., +l, -l of TREXIO
     912           2 :                i = 0
     913          26 :                DO ishell = 1, shell_num
     914          24 :                   l = shell_ang_mom(ishell)
     915         100 :                   DO k = 1, 2*l + 1
     916             :                      ! map from running index k: 1...2l+1 to magnetic quantum number m in TREXIO convention
     917          76 :                      m = (-1)**k*FLOOR(REAL(k, KIND=dp)/2.0_dp)
     918        2988 :                      mo_coefficient(i + k, imo + 1:imo + nmo) = mos_sgf(i + l + m + 1, :)
     919             :                   END DO
     920          26 :                   i = i + 2*l + 1
     921             :                END DO
     922           2 :                CPASSERT(i == nsgf)
     923             :             END IF
     924             :          END IF
     925             : 
     926          18 :          DEALLOCATE (mos_sgf)
     927             :       END DO
     928             : 
     929           8 :       IF (ionode) THEN
     930           4 :          rc = trexio_write_mo_type(f, 'Canonical', LEN_TRIM('Canonical') + 1)
     931           4 :          CALL trexio_error(rc)
     932             : 
     933           4 :          rc = trexio_write_mo_num(f, mo_num)
     934           4 :          CALL trexio_error(rc)
     935             : 
     936           4 :          rc = trexio_write_mo_coefficient(f, mo_coefficient)
     937           4 :          CALL trexio_error(rc)
     938             : 
     939           4 :          rc = trexio_write_mo_energy(f, mo_energy)
     940           4 :          CALL trexio_error(rc)
     941             : 
     942           4 :          rc = trexio_write_mo_occupation(f, mo_occupation)
     943           4 :          CALL trexio_error(rc)
     944             : 
     945           4 :          rc = trexio_write_mo_spin(f, mo_spin)
     946           4 :          CALL trexio_error(rc)
     947             : 
     948           4 :          IF (do_kpoints) THEN
     949           1 :             rc = trexio_write_mo_coefficient_im(f, mo_coefficient_im)
     950           1 :             CALL trexio_error(rc)
     951             : 
     952           1 :             rc = trexio_write_mo_k_point(f, mo_kpoint)
     953           1 :             CALL trexio_error(rc)
     954             :          END IF
     955             :       END IF
     956             : 
     957           8 :       DEALLOCATE (shell_ang_mom)
     958           8 :       DEALLOCATE (mo_coefficient)
     959           8 :       DEALLOCATE (mo_energy)
     960           8 :       DEALLOCATE (mo_occupation)
     961           8 :       DEALLOCATE (mo_spin)
     962           8 :       IF (do_kpoints) THEN
     963           2 :          DEALLOCATE (mo_coefficient_im)
     964           2 :          DEALLOCATE (mo_kpoint)
     965           2 :          CALL cp_fm_release(fm_mo_coeff)
     966           2 :          CALL cp_fm_release(fm_mo_coeff_im)
     967             :       END IF
     968             : 
     969             :       !========================================================================================!
     970             :       ! RDM group
     971             :       !========================================================================================!
     972             :       !TODO
     973             : 
     974             :       !========================================================================================!
     975             :       ! Close the TREXIO file
     976             :       !========================================================================================!
     977           8 :       IF (ionode) THEN
     978           4 :          rc = trexio_close(f)
     979           4 :          CALL trexio_error(rc)
     980             :       END IF
     981             : #else
     982             :       MARK_USED(qs_env)
     983             :       MARK_USED(trexio_section)
     984             :       CPWARN('TREXIO support has not been enabled in this build.')
     985             : #endif
     986           8 :       CALL timestop(handle)
     987             : 
     988          48 :    END SUBROUTINE write_trexio
     989             : 
     990             : ! **************************************************************************************************
     991             : !> \brief Read a trexio file
     992             : !> \param qs_env the qs environment with all the info of the computation
     993             : !> \param trexio_filename the trexio filename without the extension
     994             : !> \param mo_set_trexio the MO set to read from the trexio file
     995             : !> \param energy_derivative the energy derivative to read from the trexio file
     996             : ! **************************************************************************************************
     997           0 :    SUBROUTINE read_trexio(qs_env, trexio_filename, mo_set_trexio, energy_derivative)
     998             :       TYPE(qs_environment_type), INTENT(IN), POINTER                    :: qs_env
     999             :       CHARACTER(len=*), INTENT(IN), OPTIONAL                            :: trexio_filename
    1000             :       TYPE(mo_set_type), INTENT(OUT), DIMENSION(:), POINTER, OPTIONAL   :: mo_set_trexio
    1001             :       TYPE(dbcsr_p_type), INTENT(OUT), DIMENSION(:), POINTER, OPTIONAL  :: energy_derivative
    1002             : 
    1003             :       CHARACTER(LEN=*), PARAMETER :: routineN = 'read_trexio'
    1004             : 
    1005             :       INTEGER                                            :: handle
    1006             : 
    1007             : #ifdef __TREXIO
    1008             :       INTEGER                                            :: output_unit, unit_dE
    1009             :       CHARACTER(len=default_path_length)                 :: filename, filename_dE
    1010             :       INTEGER(trexio_t)                                  :: f        ! The TREXIO file handle
    1011             :       INTEGER(trexio_exit_code)                          :: rc       ! TREXIO return code
    1012             : 
    1013             :       LOGICAL                                            :: ionode
    1014             : 
    1015             :       CHARACTER(LEN=2)                                   :: element_symbol
    1016           0 :       CHARACTER(LEN=2), DIMENSION(:), ALLOCATABLE        :: label
    1017             : 
    1018             :       INTEGER                                            :: ao_num, mo_num, nmo, nspins, ispin, nsgf, &
    1019             :                                                             save_cartesian, i, j, k, l, m, imo, ishell, &
    1020             :                                                             nshell, shell_num, nucleus_num, natoms, ikind, &
    1021             :                                                             iatom, nelectron, nrows, ncols, &
    1022             :                                                             row, col, row_size, col_size, &
    1023             :                                                             row_offset, col_offset, myprint
    1024             :       INTEGER, DIMENSION(2)                              :: nmo_spin, electron_num
    1025           0 :       INTEGER, DIMENSION(:), ALLOCATABLE                 :: mo_spin, shell_ang_mom, trexio_to_cp2k_ang_mom
    1026             : 
    1027             :       REAL(KIND=dp)                                      :: zeff, maxocc
    1028           0 :       REAL(KIND=dp), DIMENSION(:), ALLOCATABLE           :: mo_energy, mo_occupation, charge
    1029           0 :       REAL(KIND=dp), DIMENSION(:, :), ALLOCATABLE        :: mo_coefficient, mos_sgf, coord, dEdP, temp
    1030           0 :       REAL(KIND=dp), DIMENSION(:, :), POINTER             :: data_block
    1031             : 
    1032             :       TYPE(cp_logger_type), POINTER                      :: logger => Null()
    1033             :       TYPE(cp_fm_type), POINTER                          :: mo_coeff_ref, mo_coeff_target
    1034             :       TYPE(mp_para_env_type), POINTER                    :: para_env => Null()
    1035             :       TYPE(dft_control_type), POINTER                    :: dft_control => Null()
    1036             :       TYPE(dbcsr_p_type), DIMENSION(:), POINTER          :: matrix_s => Null()
    1037             :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: kind_set => Null()
    1038             :       TYPE(mo_set_type), DIMENSION(:), POINTER           :: mos => Null()
    1039             :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set => Null()
    1040             :       TYPE(dbcsr_iterator_type)                          :: iter
    1041             : #endif
    1042             : 
    1043           0 :       CALL timeset(routineN, handle)
    1044             : 
    1045             : #ifdef __TREXIO
    1046           0 :       logger => cp_get_default_logger()
    1047           0 :       output_unit = cp_logger_get_default_io_unit(logger)
    1048           0 :       myprint = logger%iter_info%print_level
    1049             : 
    1050           0 :       CPASSERT(ASSOCIATED(qs_env))
    1051             : 
    1052             :       ! get filename
    1053           0 :       IF (.NOT. PRESENT(trexio_filename)) THEN
    1054           0 :          filename = TRIM(logger%iter_info%project_name)//'-TREXIO.h5'
    1055           0 :          filename_dE = TRIM(logger%iter_info%project_name)//'-TREXIO.dEdP.dat'
    1056             :       ELSE
    1057           0 :          filename = TRIM(trexio_filename)//'.h5'
    1058           0 :          filename_dE = TRIM(trexio_filename)//'.dEdP.dat'
    1059             :       END IF
    1060             : 
    1061           0 :       CALL get_qs_env(qs_env, para_env=para_env)
    1062           0 :       ionode = para_env%is_source()
    1063             : 
    1064             :       ! Open the TREXIO file and check that we have the same molecule as in qs_env
    1065           0 :       IF (ionode) THEN
    1066           0 :          WRITE (output_unit, "((T2,A,A))") 'TREXIO| Opening file named ', TRIM(filename)
    1067           0 :          f = trexio_open(filename, 'r', TREXIO_HDF5, rc)
    1068           0 :          CALL trexio_error(rc)
    1069             : 
    1070           0 :          IF (myprint > medium_print_level) THEN
    1071           0 :             WRITE (output_unit, "((T2,A))") 'TREXIO| Reading molecule information...'
    1072             :          END IF
    1073           0 :          rc = trexio_read_nucleus_num(f, nucleus_num)
    1074           0 :          CALL trexio_error(rc)
    1075             : 
    1076           0 :          IF (myprint > medium_print_level) THEN
    1077           0 :             WRITE (output_unit, "((T2,A))") 'TREXIO| Reading nuclear coordinates...'
    1078             :          END IF
    1079           0 :          ALLOCATE (coord(3, nucleus_num))
    1080           0 :          rc = trexio_read_nucleus_coord(f, coord)
    1081           0 :          CALL trexio_error(rc)
    1082             : 
    1083           0 :          IF (myprint > medium_print_level) THEN
    1084           0 :             WRITE (output_unit, "((T2,A))") 'TREXIO| Reading nuclear labels...'
    1085             :          END IF
    1086           0 :          ALLOCATE (label(nucleus_num))
    1087           0 :          rc = trexio_read_nucleus_label(f, label, 3)
    1088           0 :          CALL trexio_error(rc)
    1089             : 
    1090           0 :          IF (myprint > medium_print_level) THEN
    1091           0 :             WRITE (output_unit, "((T2,A))") 'TREXIO| Reading nuclear charges...'
    1092             :          END IF
    1093           0 :          ALLOCATE (charge(nucleus_num))
    1094           0 :          rc = trexio_read_nucleus_charge(f, charge)
    1095           0 :          CALL trexio_error(rc)
    1096             : 
    1097             :          ! get the same info from qs_env
    1098           0 :          CALL get_qs_env(qs_env, particle_set=particle_set, qs_kind_set=kind_set, natom=natoms)
    1099             : 
    1100             :          ! check that we have the same number of atoms
    1101           0 :          CPASSERT(nucleus_num == natoms)
    1102             : 
    1103           0 :          DO iatom = 1, natoms
    1104             :             ! compare the coordinates within a certain tolerance
    1105           0 :             DO i = 1, 3
    1106           0 :                CPASSERT(ABS(coord(i, iatom) - particle_set(iatom)%r(i)) < 1.0E-6_dp)
    1107             :             END DO
    1108             : 
    1109             :             ! figure out the element symbol and to which kind_set entry this atomic_kind corresponds to
    1110           0 :             CALL get_atomic_kind(particle_set(iatom)%atomic_kind, element_symbol=element_symbol, kind_number=ikind)
    1111             :             ! check that the element symbol is the same
    1112           0 :             CPASSERT(TRIM(element_symbol) == TRIM(label(iatom)))
    1113             : 
    1114             :             ! get the effective nuclear charge for this kind
    1115           0 :             CALL get_qs_kind(kind_set(ikind), zeff=zeff)
    1116             :             ! check that the nuclear charge is also the same
    1117           0 :             CPASSERT(charge(iatom) == zeff)
    1118             :          END DO
    1119             : 
    1120           0 :          WRITE (output_unit, "((T2,A))") 'TREXIO| Molecule is the same as in qs_env'
    1121             :          ! if we get here, we have the same molecule
    1122           0 :          DEALLOCATE (coord)
    1123           0 :          DEALLOCATE (label)
    1124           0 :          DEALLOCATE (charge)
    1125             :       END IF
    1126             : 
    1127             :       ! check whether we want to read something
    1128           0 :       IF (PRESENT(mo_set_trexio)) THEN
    1129           0 :          IF (output_unit > 1) THEN
    1130           0 :             WRITE (output_unit, "((T2,A))") 'TREXIO| Reading molecular orbitals...'
    1131             :          END IF
    1132             : 
    1133             :          ! at the moment, we assume that the basis set is the same
    1134             :          ! first we read all arrays lengths we need from the trexio file
    1135           0 :          IF (ionode) THEN
    1136           0 :             rc = trexio_read_ao_cartesian(f, save_cartesian)
    1137           0 :             CALL trexio_error(rc)
    1138             : 
    1139           0 :             rc = trexio_read_ao_num(f, ao_num)
    1140           0 :             CALL trexio_error(rc)
    1141             : 
    1142           0 :             rc = trexio_read_mo_num(f, mo_num)
    1143           0 :             CALL trexio_error(rc)
    1144             : 
    1145           0 :             rc = trexio_read_basis_shell_num(f, shell_num)
    1146           0 :             CALL trexio_error(rc)
    1147             : 
    1148           0 :             rc = trexio_read_electron_up_num(f, electron_num(1))
    1149           0 :             CALL trexio_error(rc)
    1150             : 
    1151           0 :             rc = trexio_read_electron_dn_num(f, electron_num(2))
    1152           0 :             CALL trexio_error(rc)
    1153             :          END IF
    1154             : 
    1155             :          ! broadcast information to all processors and allocate arrays
    1156           0 :          CALL para_env%bcast(save_cartesian, para_env%source)
    1157           0 :          CALL para_env%bcast(ao_num, para_env%source)
    1158           0 :          CALL para_env%bcast(mo_num, para_env%source)
    1159           0 :          CALL para_env%bcast(electron_num, para_env%source)
    1160           0 :          CALL para_env%bcast(shell_num, para_env%source)
    1161             : 
    1162           0 :          IF (save_cartesian == 1) THEN
    1163           0 :             CPABORT('Reading Cartesian AOs is not yet supported.')
    1164             :          END IF
    1165             : 
    1166             :          ! check that the number of AOs and shells is the same
    1167           0 :          CALL get_qs_env(qs_env, qs_kind_set=kind_set)
    1168           0 :          CALL get_qs_kind_set(kind_set, nsgf=nsgf, nshell=nshell)
    1169           0 :          CPASSERT(ao_num == nsgf)
    1170           0 :          CPASSERT(shell_num == nshell)
    1171             : 
    1172             :          ! check that the number of MOs is the same
    1173           0 :          CALL get_qs_env(qs_env, mos=mos, dft_control=dft_control)
    1174           0 :          nspins = dft_control%nspins
    1175           0 :          nmo_spin(:) = 0
    1176           0 :          DO ispin = 1, nspins
    1177           0 :             CALL get_mo_set(mos(ispin), nmo=nmo)
    1178           0 :             nmo_spin(ispin) = nmo
    1179             :          END DO
    1180           0 :          CPASSERT(mo_num == SUM(nmo_spin))
    1181             : 
    1182           0 :          ALLOCATE (mo_coefficient(ao_num, mo_num))
    1183           0 :          ALLOCATE (mo_energy(mo_num))
    1184           0 :          ALLOCATE (mo_occupation(mo_num))
    1185           0 :          ALLOCATE (mo_spin(mo_num))
    1186           0 :          ALLOCATE (shell_ang_mom(shell_num))
    1187             : 
    1188           0 :          mo_coefficient(:, :) = 0.0_dp
    1189           0 :          mo_energy(:) = 0.0_dp
    1190           0 :          mo_occupation(:) = 0.0_dp
    1191           0 :          mo_spin(:) = 0
    1192           0 :          shell_ang_mom(:) = 0
    1193             : 
    1194             :          ! read the MOs info
    1195           0 :          IF (ionode) THEN
    1196           0 :             IF (myprint > medium_print_level) THEN
    1197           0 :                WRITE (output_unit, "((T2,A))") 'TREXIO| Reading MO coefficients...'
    1198             :             END IF
    1199           0 :             rc = trexio_read_mo_coefficient(f, mo_coefficient)
    1200           0 :             CALL trexio_error(rc)
    1201             : 
    1202           0 :             IF (myprint > medium_print_level) THEN
    1203           0 :                WRITE (output_unit, "((T2,A))") 'TREXIO| Reading MO energies...'
    1204             :             END IF
    1205           0 :             rc = trexio_read_mo_energy(f, mo_energy)
    1206           0 :             CALL trexio_error(rc)
    1207             : 
    1208           0 :             IF (myprint > medium_print_level) THEN
    1209           0 :                WRITE (output_unit, "((T2,A))") 'TREXIO| Reading MO occupations...'
    1210             :             END IF
    1211           0 :             rc = trexio_read_mo_occupation(f, mo_occupation)
    1212           0 :             CALL trexio_error(rc)
    1213             : 
    1214           0 :             IF (myprint > medium_print_level) THEN
    1215           0 :                WRITE (output_unit, "((T2,A))") 'TREXIO| Reading MO spins...'
    1216             :             END IF
    1217           0 :             rc = trexio_read_mo_spin(f, mo_spin)
    1218           0 :             CALL trexio_error(rc)
    1219             : 
    1220           0 :             IF (myprint > medium_print_level) THEN
    1221           0 :                WRITE (output_unit, "((T2,A))") 'TREXIO| Reading shell angular momenta...'
    1222             :             END IF
    1223           0 :             rc = trexio_read_basis_shell_ang_mom(f, shell_ang_mom)
    1224           0 :             CALL trexio_error(rc)
    1225             :          END IF
    1226             : 
    1227             :          ! broadcast the data to all processors
    1228           0 :          CALL para_env%bcast(mo_coefficient, para_env%source)
    1229           0 :          CALL para_env%bcast(mo_energy, para_env%source)
    1230           0 :          CALL para_env%bcast(mo_occupation, para_env%source)
    1231           0 :          CALL para_env%bcast(mo_spin, para_env%source)
    1232           0 :          CALL para_env%bcast(shell_ang_mom, para_env%source)
    1233             : 
    1234             :          ! assume nspins and nmo_spin match the ones in the trexio file
    1235             :          ! reorder magnetic quantum number
    1236           0 :          DO ispin = 1, nspins
    1237             :             ! global MOs index
    1238           0 :             imo = (ispin - 1)*nmo_spin(1)
    1239             :             ! get number of MOs for this spin
    1240           0 :             nmo = nmo_spin(ispin)
    1241             :             ! allocate local temp array to read MOs
    1242           0 :             ALLOCATE (mos_sgf(nsgf, nmo))
    1243           0 :             mos_sgf(:, :) = 0.0_dp
    1244             : 
    1245             :             ! we need to reorder the MOs according to CP2K convention
    1246             :             ! from m =  0, +1, -1, +2, -2, ..., +l, -l of TREXIO
    1247             :             !   to m = -l, -l+1, ..., 0, ..., l-1, l   of CP2K
    1248           0 :             i = 0
    1249           0 :             DO ishell = 1, shell_num
    1250           0 :                l = shell_ang_mom(ishell)
    1251           0 :                DO k = 1, 2*l + 1
    1252             :                   ! map from running index k: 1...2l+1 to magnetic quantum number m in TREXIO convention
    1253           0 :                   m = (-1)**k*FLOOR(REAL(k, KIND=dp)/2.0_dp)
    1254           0 :                   mos_sgf(i + l + 1 + m, :) = mo_coefficient(i + k, imo + 1:imo + nmo)
    1255             :                END DO
    1256           0 :                i = i + 2*l + 1
    1257             :             END DO
    1258           0 :             CPASSERT(i == nsgf)
    1259             : 
    1260           0 :             IF (nspins == 1) THEN
    1261           0 :                maxocc = 2.0_dp
    1262           0 :                nelectron = electron_num(1) + electron_num(2)
    1263             :             ELSE
    1264           0 :                maxocc = 1.0_dp
    1265           0 :                nelectron = electron_num(ispin)
    1266             :             END IF
    1267             :             ! the right number of active electrons per spin channel is initialized further down
    1268           0 :             CALL allocate_mo_set(mo_set_trexio(ispin), nsgf, nmo, nelectron, 0.0_dp, maxocc, 0.0_dp)
    1269             : 
    1270           0 :             CALL get_mo_set(mos(ispin), mo_coeff=mo_coeff_ref)
    1271           0 :             CALL init_mo_set(mo_set_trexio(ispin), fm_ref=mo_coeff_ref, name="TREXIO MOs")
    1272             : 
    1273           0 :             CALL get_mo_set(mo_set_trexio(ispin), mo_coeff=mo_coeff_target)
    1274           0 :             DO j = 1, nmo
    1275             :                ! make sure I copy the right spin channel
    1276           0 :                CPASSERT(mo_spin(j) == ispin - 1)
    1277           0 :                mo_set_trexio(ispin)%eigenvalues(j) = mo_energy(imo + j)
    1278           0 :                mo_set_trexio(ispin)%occupation_numbers(j) = mo_occupation(imo + j)
    1279           0 :                DO i = 1, nsgf
    1280           0 :                   CALL cp_fm_set_element(mo_coeff_target, i, j, mos_sgf(i, j))
    1281             :                END DO
    1282             :             END DO
    1283             : 
    1284           0 :             DEALLOCATE (mos_sgf)
    1285             :          END DO
    1286             : 
    1287           0 :          DEALLOCATE (mo_coefficient)
    1288           0 :          DEALLOCATE (mo_energy)
    1289           0 :          DEALLOCATE (mo_occupation)
    1290           0 :          DEALLOCATE (mo_spin)
    1291             :          ! DEALLOCATE (shell_ang_mom)
    1292             : 
    1293             :       END IF ! if MOs should be read
    1294             : 
    1295             :       ! check whether we want to read derivatives
    1296           0 :       IF (PRESENT(energy_derivative)) THEN
    1297           0 :          IF (output_unit > 1) THEN
    1298           0 :             WRITE (output_unit, "((T2,A))") 'TREXIO| Reading energy derivatives...'
    1299             :          END IF
    1300             : 
    1301             :          ! Temporary solution: allocate here the energy derivatives matrix here
    1302             :          ! assuming that nsgf is the same as the number read from the dEdP file
    1303             :          ! TODO: once available in TREXIO, first read size and then allocate
    1304             :          ! in the same way done for the MOs
    1305           0 :          ALLOCATE (dEdP(nsgf, nsgf))
    1306           0 :          dEdP(:, :) = 0.0_dp
    1307             : 
    1308             :          ! check if file exists and open it
    1309           0 :          IF (ionode) THEN
    1310           0 :             IF (file_exists(filename_dE)) THEN
    1311           0 :                CALL open_file(file_name=filename_dE, file_status="OLD", unit_number=unit_dE)
    1312             :             ELSE
    1313           0 :                CPABORT("Energy derivatives file "//TRIM(filename_dE)//" not found")
    1314             :             END IF
    1315             : 
    1316             :             ! read the header and check everything is fine
    1317           0 :             IF (myprint > medium_print_level) THEN
    1318           0 :                WRITE (output_unit, "((T2,A))") 'TREXIO| Reading header information...'
    1319             :             END IF
    1320           0 :             READ (unit_dE, *) nrows, ncols
    1321           0 :             IF (myprint > medium_print_level) THEN
    1322           0 :                WRITE (output_unit, "((T2,A))") 'TREXIO| Check size of dEdP matrix...'
    1323             :             END IF
    1324           0 :             CPASSERT(nrows == nsgf)
    1325           0 :             CPASSERT(ncols == nsgf)
    1326             : 
    1327             :             ! read the data
    1328           0 :             IF (myprint > medium_print_level) THEN
    1329           0 :                WRITE (output_unit, "((T2,A))") 'TREXIO| Reading dEdP matrix...'
    1330             :             END IF
    1331             :             ! Read the data matrix
    1332           0 :             DO i = 1, nrows
    1333           0 :                READ (unit_dE, *) (dEdP(i, j), j=1, ncols)
    1334             :             END DO
    1335             : 
    1336           0 :             CALL close_file(unit_number=unit_dE)
    1337             :          END IF
    1338             : 
    1339             :          ! send data to all processes
    1340           0 :          CALL para_env%bcast(dEdP, para_env%source)
    1341             : 
    1342             :          ! AO order map from TREXIO to CP2K
    1343           0 :          ALLOCATE (trexio_to_cp2k_ang_mom(nsgf))
    1344           0 :          i = 0
    1345           0 :          DO ishell = 1, shell_num
    1346           0 :             l = shell_ang_mom(ishell)
    1347           0 :             DO k = 1, 2*l + 1
    1348           0 :                m = (-1)**k*FLOOR(REAL(k, KIND=dp)/2.0_dp)
    1349           0 :                trexio_to_cp2k_ang_mom(i + l + 1 + m) = i + k
    1350             :             END DO
    1351           0 :             i = i + 2*l + 1
    1352             :          END DO
    1353           0 :          CPASSERT(i == nsgf)
    1354             : 
    1355             :          ! Reshuffle
    1356           0 :          ALLOCATE (temp(nsgf, nsgf))
    1357           0 :          temp(:, :) = dEdP(:, :)
    1358             : 
    1359             :          ! Reorder rows and columns according to trexio_to_cp2k_ang_mom mapping
    1360           0 :          DO j = 1, nsgf
    1361           0 :             DO i = 1, nsgf
    1362           0 :                dEdP(trexio_to_cp2k_ang_mom(i), trexio_to_cp2k_ang_mom(j)) = temp(i, j)
    1363             :             END DO
    1364             :          END DO
    1365             : 
    1366           0 :          DEALLOCATE (temp)
    1367             : 
    1368           0 :          CALL get_qs_env(qs_env, matrix_s=matrix_s)
    1369           0 :          DO ispin = 1, nspins
    1370           0 :             ALLOCATE (energy_derivative(ispin)%matrix)
    1371             : 
    1372             :             ! we use the overlap matrix as a template, copying it but removing the sparsity
    1373             :             CALL dbcsr_copy(energy_derivative(ispin)%matrix, matrix_s(1)%matrix, &
    1374           0 :                             name='Energy Derivative', keep_sparsity=.FALSE.)
    1375           0 :             CALL dbcsr_set(energy_derivative(ispin)%matrix, 0.0_dp)
    1376             : 
    1377             :             !!!! DEBUG
    1378             :             ! CALL cp_dbcsr_write_sparse_matrix(energy_derivative(ispin)%matrix, 4, 4, qs_env, para_env, &
    1379             :             !                                   output_unit=output_unit, omit_headers=.false.)
    1380             :             !!!!
    1381             : 
    1382             :             ! CALL dbcsr_reserve_all_blocks(matrix)
    1383           0 :             CALL dbcsr_iterator_start(iter, energy_derivative(ispin)%matrix)
    1384           0 :             DO WHILE (dbcsr_iterator_blocks_left(iter))
    1385             :                CALL dbcsr_iterator_next_block(iter, row, col, data_block, &
    1386             :                                               row_size=row_size, col_size=col_size, &
    1387           0 :                                               row_offset=row_offset, col_offset=col_offset)
    1388             : 
    1389             :                ! Copy data from array to block
    1390           0 :                DO i = 1, row_size
    1391           0 :                   DO j = 1, col_size
    1392           0 :                      data_block(i, j) = dEdP(row_offset + i - 1, col_offset + j - 1)
    1393             :                   END DO
    1394             :                END DO
    1395             :             END DO
    1396           0 :             CALL dbcsr_iterator_stop(iter)
    1397             :             !!!! DEBUG
    1398             :             ! CALL cp_dbcsr_write_sparse_matrix(energy_derivative(ispin)%matrix, 4, 4, qs_env, para_env, &
    1399             :             !                                   output_unit=output_unit, omit_headers=.false.)
    1400             :             !!!!
    1401             :          END DO
    1402             : 
    1403             :       END IF ! finished reading energy derivatives
    1404             : 
    1405             :       ! Clean up
    1406           0 :       IF (ALLOCATED(shell_ang_mom)) DEALLOCATE (shell_ang_mom)
    1407           0 :       IF (ALLOCATED(trexio_to_cp2k_ang_mom)) DEALLOCATE (trexio_to_cp2k_ang_mom)
    1408           0 :       IF (ALLOCATED(dEdP)) DEALLOCATE (dEdP)
    1409             : 
    1410             :       ! Close the TREXIO file
    1411           0 :       IF (ionode) THEN
    1412           0 :          WRITE (output_unit, "((T2,A,A))") 'TREXIO| Closing file named ', TRIM(filename)
    1413           0 :          rc = trexio_close(f)
    1414           0 :          CALL trexio_error(rc)
    1415             :       END IF
    1416             : 
    1417             : #else
    1418             :       MARK_USED(qs_env)
    1419             :       MARK_USED(trexio_filename)
    1420             :       MARK_USED(mo_set_trexio)
    1421             :       MARK_USED(energy_derivative)
    1422             :       CPWARN('TREXIO support has not been enabled in this build.')
    1423             :       CPABORT('TREXIO Not Available')
    1424             : #endif
    1425           0 :       CALL timestop(handle)
    1426             : 
    1427           0 :    END SUBROUTINE read_trexio
    1428             : 
    1429             : #ifdef __TREXIO
    1430             : ! **************************************************************************************************
    1431             : !> \brief Handles TREXIO errors
    1432             : !> \param rc the TREXIO return code
    1433             : ! **************************************************************************************************
    1434         195 :    SUBROUTINE trexio_error(rc)
    1435             :       INTEGER(trexio_exit_code), INTENT(IN)              :: rc
    1436             : 
    1437             :       CHARACTER(LEN=128)                                 :: err_msg
    1438             : 
    1439         195 :       IF (rc /= TREXIO_SUCCESS) THEN
    1440           0 :          CALL trexio_string_of_error(rc, err_msg)
    1441           0 :          CPABORT('TREXIO Error: '//TRIM(err_msg))
    1442             :       END IF
    1443             : 
    1444         195 :    END SUBROUTINE trexio_error
    1445             : 
    1446             : ! **************************************************************************************************
    1447             : !> \brief Computes the nuclear repulsion energy of a molecular system
    1448             : !> \param particle_set the set of particles in the system
    1449             : !> \param kind_set the set of qs_kinds in the system
    1450             : !> \param e_nn the nuclear repulsion energy
    1451             : ! **************************************************************************************************
    1452           2 :    SUBROUTINE nuclear_repulsion_energy(particle_set, kind_set, e_nn)
    1453             :       TYPE(particle_type), DIMENSION(:), INTENT(IN), &
    1454             :          POINTER                                         :: particle_set
    1455             :       TYPE(qs_kind_type), DIMENSION(:), INTENT(IN), &
    1456             :          POINTER                                         :: kind_set
    1457             :       REAL(KIND=dp), INTENT(OUT)                         :: e_nn
    1458             : 
    1459             :       INTEGER                                            :: i, ikind, j, jkind, natoms
    1460             :       REAL(KIND=dp)                                      :: r_ij, zeff_i, zeff_j
    1461             : 
    1462           2 :       natoms = SIZE(particle_set)
    1463           2 :       e_nn = 0.0_dp
    1464           4 :       DO i = 1, natoms
    1465           2 :          CALL get_atomic_kind(particle_set(i)%atomic_kind, kind_number=ikind)
    1466           2 :          CALL get_qs_kind(kind_set(ikind), zeff=zeff_i)
    1467           4 :          DO j = i + 1, natoms
    1468           0 :             r_ij = NORM2(particle_set(i)%r - particle_set(j)%r)
    1469             : 
    1470           0 :             CALL get_atomic_kind(particle_set(j)%atomic_kind, kind_number=jkind)
    1471           0 :             CALL get_qs_kind(kind_set(jkind), zeff=zeff_j)
    1472             : 
    1473           2 :             e_nn = e_nn + zeff_i*zeff_j/r_ij
    1474             :          END DO
    1475             :       END DO
    1476             : 
    1477           2 :    END SUBROUTINE nuclear_repulsion_energy
    1478             : 
    1479             : ! **************************************************************************************************
    1480             : !> \brief Computes a spherical to cartesian MO transformation (solid harmonics in reality)
    1481             : !> \param mos_sgf the MO coefficients in spherical AO basis
    1482             : !> \param particle_set the set of particles in the system
    1483             : !> \param qs_kind_set the set of qs_kinds in the system
    1484             : !> \param mos_cgf the transformed MO coefficients in Cartesian AO basis
    1485             : ! **************************************************************************************************
    1486           6 :    SUBROUTINE spherical_to_cartesian_mo(mos_sgf, particle_set, qs_kind_set, mos_cgf)
    1487             :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: mos_sgf
    1488             :       TYPE(particle_type), DIMENSION(:), INTENT(IN), &
    1489             :          POINTER                                         :: particle_set
    1490             :       TYPE(qs_kind_type), DIMENSION(:), INTENT(IN), &
    1491             :          POINTER                                         :: qs_kind_set
    1492             :       REAL(KIND=dp), DIMENSION(:, :), INTENT(OUT)        :: mos_cgf
    1493             : 
    1494             :       INTEGER                                            :: iatom, icgf, ikind, iset, isgf, ishell, &
    1495             :                                                             lshell, ncgf, nmo, nset, nsgf
    1496           6 :       INTEGER, DIMENSION(:), POINTER                     :: nshell
    1497           6 :       INTEGER, DIMENSION(:, :), POINTER                  :: l
    1498             :       TYPE(gto_basis_set_type), POINTER                  :: orb_basis_set
    1499             : 
    1500           6 :       CALL get_qs_kind_set(qs_kind_set, ncgf=ncgf, nsgf=nsgf)
    1501             : 
    1502        3586 :       mos_cgf = 0.0_dp
    1503           6 :       nmo = SIZE(mos_sgf, 2)
    1504             : 
    1505             :       ! Transform spherical MOs to Cartesian MOs
    1506           6 :       icgf = 1
    1507           6 :       isgf = 1
    1508          20 :       DO iatom = 1, SIZE(particle_set)
    1509          14 :          NULLIFY (orb_basis_set)
    1510          14 :          CALL get_atomic_kind(particle_set(iatom)%atomic_kind, kind_number=ikind)
    1511          14 :          CALL get_qs_kind(qs_kind_set(ikind), basis_set=orb_basis_set)
    1512             : 
    1513          34 :          IF (ASSOCIATED(orb_basis_set)) THEN
    1514             :             CALL get_gto_basis_set(gto_basis_set=orb_basis_set, &
    1515             :                                    nset=nset, &
    1516             :                                    nshell=nshell, &
    1517          14 :                                    l=l)
    1518          54 :             DO iset = 1, nset
    1519          98 :                DO ishell = 1, nshell(iset)
    1520          44 :                   lshell = l(ishell, iset)
    1521             :                   CALL dgemm("T", "N", nco(lshell), nmo, nso(lshell), 1.0_dp, &
    1522             :                              orbtramat(lshell)%c2s, nso(lshell), &
    1523             :                              mos_sgf(isgf, 1), nsgf, 0.0_dp, &
    1524          44 :                              mos_cgf(icgf, 1), ncgf)
    1525          44 :                   icgf = icgf + nco(lshell)
    1526          84 :                   isgf = isgf + nso(lshell)
    1527             :                END DO
    1528             :             END DO
    1529             :          ELSE
    1530             :             ! assume atom without basis set
    1531           0 :             CPABORT("Unknown basis set type")
    1532             :          END IF
    1533             :       END DO ! iatom
    1534             : 
    1535           6 :    END SUBROUTINE spherical_to_cartesian_mo
    1536             : #endif
    1537             : 
    1538             : END MODULE trexio_utils

Generated by: LCOV version 1.15