LCOV - code coverage report
Current view: top level - src - trexio_utils.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:ca6acae) Lines: 63.4 % 759 481
Test Date: 2026-01-02 06:29:53 Functions: 71.4 % 7 5

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

Generated by: LCOV version 2.0-1