LCOV - code coverage report
Current view: top level - src/emd - rt_bse_io.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:24d69ee) Lines: 85.2 % 393 335
Test Date: 2026-09-03 07:32:15 Functions: 94.4 % 18 17

            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 Input/output from the propagation via RT-BSE method.
      10              : !> \author Stepan Marek (08.24)
      11              : ! **************************************************************************************************
      12              : 
      13              : MODULE rt_bse_io
      14              :    USE cp_fm_types, ONLY: cp_fm_type, &
      15              :                           cp_fm_read_unformatted, &
      16              :                           cp_fm_write_unformatted, &
      17              :                           cp_fm_write_formatted
      18              :    USE cp_cfm_types, ONLY: cp_cfm_type, &
      19              :                            cp_fm_to_cfm, &
      20              :                            cp_cfm_to_fm
      21              :    USE kinds, ONLY: dp, &
      22              :                     default_path_length
      23              :    USE cp_fm_basic_linalg, ONLY: cp_fm_trace, &
      24              :                                  cp_fm_transpose
      25              :    USE cp_log_handling, ONLY: cp_logger_type, &
      26              :                               cp_get_default_logger
      27              :    USE cp_output_handling, ONLY: cp_print_key_unit_nr, &
      28              :                                  cp_print_key_finished_output, &
      29              :                                  cp_print_key_generate_filename, &
      30              :                                  low_print_level, &
      31              :                                  medium_print_level
      32              :    USE input_section_types, ONLY: section_vals_type
      33              :    USE rt_bse_types, ONLY: rtbse_env_type, &
      34              :                            multiply_cfm_fm, &
      35              :                            multiply_fm_cfm
      36              :    USE cp_files, ONLY: open_file, &
      37              :                        file_exists, &
      38              :                        close_file
      39              :    USE input_constants, ONLY: do_exact, &
      40              :                               do_bch, &
      41              :                               evGW0, &
      42              :                               rtp_bse_ham_gw, &
      43              :                               rtp_bse_ham_ks, &
      44              :                               use_rt_restart, &
      45              :                               restart_guess
      46              :    USE qs_environment_types, ONLY: get_qs_env
      47              :    USE scf_control_types, ONLY: scf_control_type
      48              :    USE physcon, ONLY: evolt, femtoseconds
      49              :    USE rt_propagation_output, ONLY: print_moments, &
      50              :                                     print_rt_file, &
      51              :                                     rt_file_comp_real
      52              : 
      53              : #include "../base/base_uses.f90"
      54              : 
      55              :    IMPLICIT NONE
      56              : 
      57              :    PRIVATE
      58              : 
      59              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = "rt_bse_io"
      60              : 
      61              :    ! RESTART.trace on-disk format version - bump whenever the header or record layout changes
      62              :    INTEGER, PARAMETER, PRIVATE :: restart_trace_version = 1
      63              : 
      64              :    #:include "rt_bse_macros.fypp"
      65              : 
      66              :    PUBLIC :: output_moments, &
      67              :              output_field, &
      68              :              read_field, &
      69              :              output_mos_contravariant, &
      70              :              output_mos_covariant, &
      71              :              output_restart, &
      72              :              read_restart, &
      73              :              output_restart_linearized, &
      74              :              read_restart_info, &
      75              :              read_restart_trace, &
      76              :              read_restart_density, &
      77              :              read_restart_C, &
      78              :              check_restart_eps_consistency, &
      79              :              print_etrs_info_header, &
      80              :              print_etrs_info, &
      81              :              print_timestep_info, &
      82              :              print_rtbse_header_info
      83              : 
      84              : CONTAINS
      85              : 
      86              : ! **************************************************************************************************
      87              : !> \brief Writes the header and basic info to the standard output
      88              : !> \param rtbse_env Entry point - rtbse environment
      89              : ! **************************************************************************************************
      90           14 :    SUBROUTINE print_rtbse_header_info(rtbse_env)
      91              :       TYPE(rtbse_env_type)                               :: rtbse_env
      92              :       TYPE(cp_logger_type), POINTER                      :: logger
      93              : 
      94           14 :       logger => cp_get_default_logger()
      95              : 
      96           14 :       IF (rtbse_env%unit_nr > 0) THEN
      97            7 :          WRITE (rtbse_env%unit_nr, *) ''
      98              :          WRITE (rtbse_env%unit_nr, '(A)') ' /-----------------------------------------------'// &
      99            7 :             '------------------------------\'
     100              :          WRITE (rtbse_env%unit_nr, '(A)') ' |                                               '// &
     101            7 :             '                              |'
     102              :          WRITE (rtbse_env%unit_nr, '(A)') ' |                    Real Time Bethe-Salpeter Propagation'// &
     103            7 :             '                     |'
     104              :          WRITE (rtbse_env%unit_nr, '(A)') ' |                                               '// &
     105            7 :             '                              |'
     106              :          WRITE (rtbse_env%unit_nr, '(A)') ' \-----------------------------------------------'// &
     107            7 :             '------------------------------/'
     108            7 :          WRITE (rtbse_env%unit_nr, *) ''
     109              : 
     110              :          ! Methods used
     111            7 :          WRITE (rtbse_env%unit_nr, '(A19)', advance="no") ' Exponential method'
     112           14 :          SELECT CASE (rtbse_env%mat_exp_method)
     113              :          CASE (do_bch)
     114            7 :             WRITE (rtbse_env%unit_nr, '(A61)') 'BCH'
     115              :          CASE (do_exact)
     116            7 :             WRITE (rtbse_env%unit_nr, '(A61)') 'EXACT'
     117              :          END SELECT
     118              : 
     119            7 :          WRITE (rtbse_env%unit_nr, '(A22)', advance="no") ' Reference Hamiltonian'
     120           14 :          SELECT CASE (rtbse_env%ham_reference_type)
     121              :          CASE (rtp_bse_ham_gw)
     122            7 :             IF (rtbse_env%bs_env%gw_flavour == evGW0) THEN
     123            0 :                WRITE (rtbse_env%unit_nr, '(A58)') 'evGW0'
     124              :             ELSE
     125            7 :                WRITE (rtbse_env%unit_nr, '(A58)') 'G0W0'
     126              :             END IF
     127              :          CASE (rtp_bse_ham_ks)
     128            7 :             WRITE (rtbse_env%unit_nr, '(A58)') 'Kohn-Sham'
     129              :          END SELECT
     130              : 
     131            7 :          WRITE (rtbse_env%unit_nr, '(A18,L62)') ' Apply delta pulse', &
     132           14 :             rtbse_env%dft_control%rtp_control%apply_delta_pulse
     133              : 
     134            7 :          WRITE (rtbse_env%unit_nr, '(A)') ''
     135              :       END IF
     136              : 
     137           14 :    END SUBROUTINE print_rtbse_header_info
     138              : 
     139              : ! **************************************************************************************************
     140              : !> \brief Writes the update after single etrs iteration - only for log level > medium
     141              : !> \param rtbse_env Entry point - rtbse environment
     142              : ! **************************************************************************************************
     143         1732 :    SUBROUTINE print_etrs_info(rtbse_env, step, metric)
     144              :       TYPE(rtbse_env_type)                               :: rtbse_env
     145              :       INTEGER                                            :: step
     146              :       REAL(kind=dp)                                      :: metric
     147              :       TYPE(cp_logger_type), POINTER                      :: logger
     148              : 
     149         1732 :       logger => cp_get_default_logger()
     150              : 
     151         1732 :       IF (logger%iter_info%print_level > medium_print_level .AND. rtbse_env%unit_nr > 0) THEN
     152            0 :          WRITE (rtbse_env%unit_nr, '(A7,I5, E20.8E3)') ' RTBSE|', step, metric
     153              :       END IF
     154              : 
     155         1732 :    END SUBROUTINE print_etrs_info
     156              : ! **************************************************************************************************
     157              : !> \brief Writes the header for the etrs iteration updates - only for log level > medium
     158              : !> \param rtbse_env Entry point - rtbse environment
     159              : ! **************************************************************************************************
     160          678 :    SUBROUTINE print_etrs_info_header(rtbse_env)
     161              :       TYPE(rtbse_env_type)                               :: rtbse_env
     162              :       TYPE(cp_logger_type), POINTER                      :: logger
     163              : 
     164          678 :       logger => cp_get_default_logger()
     165              : 
     166          678 :       IF (logger%iter_info%print_level > medium_print_level .AND. rtbse_env%unit_nr > 0) THEN
     167            0 :          WRITE (rtbse_env%unit_nr, '(A13, A20)') ' RTBSE| Iter.', 'Convergence'
     168              :       END IF
     169              : 
     170          678 :    END SUBROUTINE print_etrs_info_header
     171              : ! **************************************************************************************************
     172              : !> \brief Writes the summary line of a completed propagation timestep
     173              : !> \param rtbse_env Entry point - rtbse environment
     174              : !> \param step Index of the completed timestep
     175              : !> \param electron_num_re Real part of the electron number, one entry per spin channel
     176              : !> \param convergence Optional convergence metric reached by the ETRS iteration
     177              : !> \param etrs_num Optional number of ETRS iterations used in this timestep
     178              : !> \param step_walltime Optional wall time of this timestep, in seconds
     179              : ! **************************************************************************************************
     180         1508 :    SUBROUTINE print_timestep_info(rtbse_env, step, electron_num_re, convergence, etrs_num, &
     181              :                                   step_walltime)
     182              :       TYPE(rtbse_env_type)                               :: rtbse_env
     183              :       INTEGER                                            :: step
     184              :       REAL(kind=dp), DIMENSION(:), INTENT(IN)            :: electron_num_re
     185              :       REAL(kind=dp), OPTIONAL                            :: convergence
     186              :       INTEGER, OPTIONAL                                  :: etrs_num
     187              :       REAL(kind=dp), OPTIONAL                            :: step_walltime
     188              :       TYPE(cp_logger_type), POINTER                      :: logger
     189              :       LOGICAL                                          :: flag_lrrtbse
     190              :       INTEGER                                            :: nch
     191              : 
     192         1508 :       logger => cp_get_default_logger()
     193              :       ! one electron-number column per spin channel (1 = closed shell, 2 = open shell alpha/beta)
     194         1508 :       nch = SIZE(electron_num_re)
     195              : 
     196         1508 :       IF (.NOT. PRESENT(convergence) .OR. .NOT. PRESENT(etrs_num)) THEN
     197              :          flag_lrrtbse = .TRUE.
     198              :       ELSE
     199          678 :          flag_lrrtbse = .FALSE.
     200              :       END IF
     201              : 
     202         1508 :       IF (logger%iter_info%print_level > low_print_level .AND. rtbse_env%unit_nr > 0) THEN
     203          754 :          IF (flag_lrrtbse) THEN
     204          415 :             IF (step == 0) THEN
     205           22 :                IF (PRESENT(step_walltime)) THEN
     206              :                   WRITE (rtbse_env%unit_nr, '(A45,T70,F11.3)') &
     207           22 :                      " RTBSE| Estimated runtime for propagation [s]", &
     208           44 :                      step_walltime*REAL(rtbse_env%sim_nsteps, dp)
     209              :                   WRITE (rtbse_env%unit_nr, '(A)') &
     210           22 :                      " RTBSE|"
     211           22 :                   IF (nch == 1) THEN
     212              :                      WRITE (rtbse_env%unit_nr, '(A23,T27,A13,T66,A15)') &
     213           19 :                         " RTBSE| Simulation step", "Step time [s]", "Electron number"
     214              :                   ELSE
     215              :                      ! T67/T88 (not T66/T86): each α/β is 2 bytes but 1 display col, so the byte
     216              :                      ! anchor is +1 per unicode char to right-align ')' under the value's last digit.
     217              :                      ! T anchors carry +1 byte per α/β before the ')' (α col → +1, β col → +2),
     218              :                      ! since each is 2 bytes but 1 display col; right-aligns ')' on the last digit.
     219              :                      WRITE (rtbse_env%unit_nr, '(A23,T27,A13,T47,A15,T68,A15)') &
     220            3 :                         " RTBSE| Simulation step", "Step time [s]", "El. number (α)", "El. number (β)"
     221              :                   END IF
     222              :                ELSE
     223            0 :                   IF (nch == 1) THEN
     224            0 :                      WRITE (rtbse_env%unit_nr, '(A23,T66,A15)') " RTBSE| Simulation step", "Electron number"
     225              :                   ELSE
     226              :                      WRITE (rtbse_env%unit_nr, '(A23,T47,A15,T68,A15)') &
     227            0 :                         " RTBSE| Simulation step", "El. number (α)", "El. number (β)"
     228              :                   END IF
     229              :                END IF
     230              :             END IF
     231          415 :             IF (PRESENT(step_walltime)) THEN
     232          415 :                IF (nch == 1) THEN
     233              :                   WRITE (rtbse_env%unit_nr, '(A7,I16,T30,F10.3,T69,E12.3E3)') &
     234          355 :                      ' RTBSE|', step, step_walltime, electron_num_re(1)
     235              :                ELSE
     236              :                   WRITE (rtbse_env%unit_nr, '(A7,I16,T30,F10.3,T49,E12.3E3,T69,E12.3E3)') &
     237           60 :                      ' RTBSE|', step, step_walltime, electron_num_re(1), electron_num_re(2)
     238              :                END IF
     239              :             ELSE
     240            0 :                IF (nch == 1) THEN
     241            0 :                   WRITE (rtbse_env%unit_nr, '(A7,I16,T61,E20.8E3)') ' RTBSE|', step, electron_num_re(1)
     242              :                ELSE
     243              :                   WRITE (rtbse_env%unit_nr, '(A7,I16,T49,E12.3E3,T69,E12.3E3)') &
     244            0 :                      ' RTBSE|', step, electron_num_re(1), electron_num_re(2)
     245              :                END IF
     246              :             END IF
     247              :          ELSE
     248          339 :             WRITE (rtbse_env%unit_nr, '(A23,A20,A20,A17)') " RTBSE| Simulation step", "Convergence", &
     249          678 :                "Electron number", "ETRS Iterations"
     250          339 :             WRITE (rtbse_env%unit_nr, '(A7,I16,E20.8E3,E20.8E3,I17)') ' RTBSE|', step, convergence, &
     251          678 :                electron_num_re(1), etrs_num
     252              :          END IF
     253              :       END IF
     254              : 
     255         1508 :    END SUBROUTINE print_timestep_info
     256              : 
     257              : ! **************************************************************************************************
     258              : !> \brief Outputs the matrix in MO basis for matrix coefficients corresponding to contravariant
     259              : !>        operator, i.e. density matrix
     260              : !> \param rtbse_env Entry point - gwbse environment
     261              : !> \param rho Density matrix in AO basis
     262              : !> \param rtp_section RTP input section
     263              : ! **************************************************************************************************
     264         1508 :    SUBROUTINE output_mos_contravariant(rtbse_env, rho, print_key_section)
     265              :       TYPE(rtbse_env_type)                               :: rtbse_env
     266              :       TYPE(cp_cfm_type), DIMENSION(:), POINTER           :: rho
     267              :       TYPE(section_vals_type), POINTER                   :: print_key_section
     268              :       TYPE(cp_logger_type), POINTER                      :: logger
     269              :       INTEGER                                            :: j, rho_unit_re, rho_unit_im
     270              :       CHARACTER(len=14), DIMENSION(4)                    :: file_labels
     271              : 
     272         1508 :       file_labels(1) = "_SPIN_A_RE.dat"
     273         1508 :       file_labels(2) = "_SPIN_A_IM.dat"
     274         1508 :       file_labels(3) = "_SPIN_B_RE.dat"
     275         1508 :       file_labels(4) = "_SPIN_B_IM.dat"
     276         1508 :       logger => cp_get_default_logger()
     277              : 
     278              :       ! In the linearized RT-BSE active-MO path, rho is already in the MO basis
     279              :       ! restricted to the active window (sized mo_active x mo_active).  Dump it
     280              :       ! directly without the AO-side C^T S * rho * S C transformation.
     281         1508 :       IF (ASSOCIATED(rtbse_env%real_workspace_mo)) THEN
     282         1780 :          DO j = 1, rtbse_env%n_spin
     283          950 :             rho_unit_re = cp_print_key_unit_nr(logger, print_key_section, extension=file_labels(2*j - 1))
     284          950 :             rho_unit_im = cp_print_key_unit_nr(logger, print_key_section, extension=file_labels(2*j))
     285          950 :             CALL cp_cfm_to_fm(rho(j), rtbse_env%real_workspace_mo(1), rtbse_env%real_workspace_mo(2))
     286          950 :             CALL cp_fm_write_formatted(rtbse_env%real_workspace_mo(1), rho_unit_re)
     287          950 :             CALL cp_fm_write_formatted(rtbse_env%real_workspace_mo(2), rho_unit_im)
     288          950 :             CALL cp_print_key_finished_output(rho_unit_re, logger, print_key_section)
     289         1780 :             CALL cp_print_key_finished_output(rho_unit_im, logger, print_key_section)
     290              :          END DO
     291          830 :          RETURN
     292              :       END IF
     293              :       ! Start by multiplying the current density by MOS
     294         1356 :       DO j = 1, rtbse_env%n_spin
     295          678 :          rho_unit_re = cp_print_key_unit_nr(logger, print_key_section, extension=file_labels(2*j - 1))
     296          678 :          rho_unit_im = cp_print_key_unit_nr(logger, print_key_section, extension=file_labels(2*j))
     297              :          ! Transform the density matrix into molecular orbitals basis and print it out
     298              :          ! S * rho
     299              :          CALL multiply_fm_cfm("N", "N", rtbse_env%n_ao, rtbse_env%n_ao, rtbse_env%n_ao, &
     300              :                               1.0_dp, rtbse_env%S_fm, rho(j), &
     301          678 :                               0.0_dp, rtbse_env%rho_workspace(1))
     302              :          ! C^T * S * rho
     303              :          CALL multiply_fm_cfm("T", "N", rtbse_env%n_ao, rtbse_env%n_ao, rtbse_env%n_ao, &
     304              :                               1.0_dp, rtbse_env%bs_env%fm_mo_coeff_Gamma(j), rtbse_env%rho_workspace(1), &
     305          678 :                               0.0_dp, rtbse_env%rho_workspace(2))
     306              :          ! C^T * S * rho * S
     307              :          CALL multiply_cfm_fm("N", "N", rtbse_env%n_ao, rtbse_env%n_ao, rtbse_env%n_ao, &
     308              :                               1.0_dp, rtbse_env%rho_workspace(2), rtbse_env%S_fm, &
     309          678 :                               0.0_dp, rtbse_env%rho_workspace(1))
     310              :          ! C^T * S * rho * S * C
     311              :          CALL multiply_cfm_fm("N", "N", rtbse_env%n_ao, rtbse_env%n_ao, rtbse_env%n_ao, &
     312              :                               1.0_dp, rtbse_env%rho_workspace(1), rtbse_env%bs_env%fm_mo_coeff_Gamma(j), &
     313          678 :                               0.0_dp, rtbse_env%rho_workspace(2))
     314              :          ! Print real and imaginary parts separately
     315              :          CALL cp_cfm_to_fm(rtbse_env%rho_workspace(2), &
     316          678 :                            rtbse_env%real_workspace(1), rtbse_env%real_workspace(2))
     317          678 :          CALL cp_fm_write_formatted(rtbse_env%real_workspace(1), rho_unit_re)
     318          678 :          CALL cp_fm_write_formatted(rtbse_env%real_workspace(2), rho_unit_im)
     319          678 :          CALL cp_print_key_finished_output(rho_unit_re, logger, print_key_section)
     320         1356 :          CALL cp_print_key_finished_output(rho_unit_im, logger, print_key_section)
     321              :       END DO
     322              :    END SUBROUTINE output_mos_contravariant
     323              : ! **************************************************************************************************
     324              : !> \brief Outputs the matrix in MO basis for matrix components corresponding to covariant representation,
     325              : !>        i.e. the Hamiltonian matrix
     326              : !> \param rtbse_env Entry point - gwbse environment
     327              : !> \param cohsex cohsex matrix in AO basis, covariant representation
     328              : !> \param rtp_section RTP input section
     329              : ! **************************************************************************************************
     330            0 :    SUBROUTINE output_mos_covariant(rtbse_env, ham, print_key_section)
     331              :       TYPE(rtbse_env_type)                               :: rtbse_env
     332              :       TYPE(cp_cfm_type), DIMENSION(:), POINTER           :: ham
     333              :       TYPE(section_vals_type), POINTER                   :: print_key_section
     334              :       TYPE(cp_logger_type), POINTER                      :: logger
     335              :       INTEGER                                            :: j, rho_unit_re, rho_unit_im
     336              :       CHARACTER(len=21), DIMENSION(4)                    :: file_labels
     337              : 
     338            0 :       file_labels(1) = "_SPIN_A_RE.dat"
     339            0 :       file_labels(2) = "_SPIN_A_IM.dat"
     340            0 :       file_labels(3) = "_SPIN_B_RE.dat"
     341            0 :       file_labels(4) = "_SPIN_B_IM.dat"
     342            0 :       logger => cp_get_default_logger()
     343            0 :       DO j = 1, rtbse_env%n_spin
     344            0 :          rho_unit_re = cp_print_key_unit_nr(logger, print_key_section, extension=file_labels(2*j - 1))
     345            0 :          rho_unit_im = cp_print_key_unit_nr(logger, print_key_section, extension=file_labels(2*j))
     346              :          ! C^T * cohsex
     347              :          CALL multiply_fm_cfm("T", "N", rtbse_env%n_ao, rtbse_env%n_ao, rtbse_env%n_ao, &
     348              :                               1.0_dp, rtbse_env%bs_env%fm_mo_coeff_Gamma(j), ham(j), &
     349            0 :                               0.0_dp, rtbse_env%rho_workspace(1))
     350              :          ! C^T * cohsex * C
     351              :          CALL multiply_cfm_fm("N", "N", rtbse_env%n_ao, rtbse_env%n_ao, rtbse_env%n_ao, &
     352              :                               1.0_dp, rtbse_env%rho_workspace(1), rtbse_env%bs_env%fm_mo_coeff_Gamma(j), &
     353            0 :                               0.0_dp, rtbse_env%rho_workspace(2))
     354              :          ! Print real and imaginary parts separately
     355              :          CALL cp_cfm_to_fm(rtbse_env%rho_workspace(2), &
     356            0 :                            rtbse_env%real_workspace(1), rtbse_env%real_workspace(2))
     357            0 :          CALL cp_fm_write_formatted(rtbse_env%real_workspace(1), rho_unit_re)
     358            0 :          CALL cp_fm_write_formatted(rtbse_env%real_workspace(2), rho_unit_im)
     359            0 :          CALL cp_print_key_finished_output(rho_unit_re, logger, print_key_section)
     360            0 :          CALL cp_print_key_finished_output(rho_unit_im, logger, print_key_section)
     361              :       END DO
     362            0 :    END SUBROUTINE output_mos_covariant
     363              : ! **************************************************************************************************
     364              : !> \brief Prints the current field components into a file provided by input
     365              : !> \param rtbse_env Entry point - gwbse environment
     366              : !> \param rtp_section RTP input section
     367              : ! **************************************************************************************************
     368         1562 :    SUBROUTINE output_field(rtbse_env, append_opt)
     369              :       TYPE(rtbse_env_type)                               :: rtbse_env
     370              :       LOGICAL, OPTIONAL                                  :: append_opt
     371              :       TYPE(cp_logger_type), POINTER                      :: logger
     372              :       INTEGER                                            :: field_unit, n, i
     373              :       LOGICAL                                            :: append
     374              : 
     375              :       ! Figure out whether we are appending or not, true by default
     376         1562 :       append = .TRUE.
     377         1562 :       IF (PRESENT(append_opt)) append = .FALSE.
     378              : 
     379              :       ! First, write the current field to memory
     380              :       ! Need the absolute index
     381         1562 :       n = rtbse_env%sim_step - rtbse_env%sim_start_orig + 1
     382         6248 :       DO i = 1, 3
     383         6248 :          rtbse_env%field_trace(i, n) = CMPLX(rtbse_env%field(i), 0.0, kind=dp)
     384              :       END DO
     385         1562 :       rtbse_env%time_trace(n) = rtbse_env%sim_time
     386              : 
     387              :       ! Now, continue to file output
     388              :       ! Get logger
     389         1562 :       logger => cp_get_default_logger()
     390              :       ! Get file descriptor
     391         1562 :       field_unit = cp_print_key_unit_nr(logger, rtbse_env%field_section, extension=".dat")
     392         1562 :       IF (append) THEN
     393              :          CALL print_rt_file(field_unit, xvals=rtbse_env%time_trace(n:n), &
     394              :                             yvals=rtbse_env%field_trace(:, n:n), &
     395         1552 :                             xscale_opt=femtoseconds, comp_opt=rt_file_comp_real)
     396              :       ELSE
     397              :          CALL print_rt_file(field_unit, [ &
     398              :                             "#          Time [fs]", &
     399              :                             "     field x [at.u.]", &
     400              :                             "     field y [at.u.]", &
     401              :                             "     field z [at.u.]"], &
     402              :                             rtbse_env%time_trace(n:n), rtbse_env%field_trace(:, n:n), &
     403           50 :                             xscale_opt=femtoseconds, comp_opt=rt_file_comp_real)
     404              :       END IF
     405         1562 :       CALL cp_print_key_finished_output(field_unit, logger, rtbse_env%field_section)
     406              : 
     407         1562 :    END SUBROUTINE output_field
     408              : ! **************************************************************************************************
     409              : !> \brief Reads the field from the files provided by input - useful for the continuation run
     410              : !> \param rtbse_env Entry point - gwbse environment
     411              : !> \param rtp_section RTP input section
     412              : ! **************************************************************************************************
     413           14 :    SUBROUTINE read_field(rtbse_env)
     414              :       TYPE(rtbse_env_type)                               :: rtbse_env
     415              :       TYPE(cp_logger_type), POINTER                      :: logger
     416              :       CHARACTER(len=default_path_length)                 :: save_name
     417              :       INTEGER                                            :: k, n, field_unit
     418              :       REAL(kind=dp), DIMENSION(3)                        :: real_field
     419              : 
     420              :       ! Get logger
     421           14 :       logger => cp_get_default_logger()
     422              :       ! Get file name
     423           14 :       save_name = cp_print_key_generate_filename(logger, rtbse_env%field_section, extension=".dat", my_local=.FALSE.)
     424           14 :       IF (file_exists(save_name)) THEN
     425              :          CALL open_file(save_name, file_status="OLD", file_form="FORMATTED", file_action="READ", &
     426            0 :                         unit_number=field_unit)
     427              :          ! Skip the first line - it contains headers
     428            0 :          READ (field_unit, '()')
     429            0 :          DO k = rtbse_env%sim_start_orig, rtbse_env%sim_start
     430            0 :             n = k - rtbse_env%sim_start_orig + 1
     431            0 :             READ (field_unit, '(E20.8E3,E20.8E3,E20.8E3,E20.8E3)') rtbse_env%time_trace(n), &
     432            0 :                real_field(1), real_field(2), real_field(3)
     433            0 :             rtbse_env%field_trace(:, n) = CMPLX(real_field(:), 0.0, kind=dp)
     434              :             ! Set the time units back to atomic units
     435            0 :             rtbse_env%time_trace(n) = rtbse_env%time_trace(n)/femtoseconds
     436              :          END DO
     437            0 :          CALL close_file(field_unit)
     438           14 :       ELSE IF (.NOT. rtbse_env%dft_control%rtp_control%apply_delta_pulse .AND. &
     439              :                rtbse_env%dft_control%rtp_control%initial_wfn == use_rt_restart) THEN
     440            2 :          CPWARN("Restart without RT field file - unknown field trace set to zero.")
     441              :       END IF
     442           14 :    END SUBROUTINE read_field
     443              : 
     444              : ! **************************************************************************************************
     445              : !> \brief Outputs the expectation value of moments from a given density matrix
     446              : !> \note  Moments matrix is provided by the rtbse_env, uses rho_workspace(1:3)
     447              : !> \param rtbse_env Entry point - gwbse environment
     448              : !> \param rho Density matrix in AO basis
     449              : !> \param rtp_section RTP section of the input parameters, where moments destination may be present
     450              : ! **************************************************************************************************
     451         1562 :    SUBROUTINE output_moments(rtbse_env, rho)
     452              :       TYPE(rtbse_env_type)                               :: rtbse_env
     453              :       TYPE(cp_cfm_type), DIMENSION(:), POINTER           :: rho
     454              :       INTEGER                                            :: i, j, n
     455              :       REAL(kind=dp), DIMENSION(3)                        :: moments_re
     456         1562 :       TYPE(cp_fm_type), DIMENSION(:), POINTER            :: ws
     457              : 
     458         1562 :       n = rtbse_env%sim_step - rtbse_env%sim_start_orig + 1
     459              : 
     460              :       ! In linearized RT-BSE rho and moments are MO-active sized; otherwise AO sized.
     461         1562 :       IF (ASSOCIATED(rtbse_env%real_workspace_mo)) THEN
     462          874 :          ws => rtbse_env%real_workspace_mo
     463              :       ELSE
     464          688 :          ws => rtbse_env%real_workspace
     465              :       END IF
     466              : 
     467         3250 :       DO j = 1, rtbse_env%n_spin
     468              :          ! Need to transpose due to the definition of trace function
     469         1688 :          CALL cp_cfm_to_fm(msource=rho(j), mtargetr=ws(2))
     470         6752 :          DO i = 1, 3
     471              :             ! Moments should be symmetric, test without transopose?
     472         5064 :             CALL cp_fm_transpose(rtbse_env%moments(i, j), ws(1))
     473         5064 :             CALL cp_fm_trace(ws(1), ws(2), moments_re(i))
     474              :             ! Scale by spin degeneracy and electron charge
     475         5064 :             moments_re(i) = -moments_re(i)*rtbse_env%spin_degeneracy
     476         6752 :             rtbse_env%moments_trace(j, i, n) = CMPLX(moments_re(i), 0.0, kind=dp)
     477              :          END DO
     478              :          ! Same for imaginary part
     479         1688 :          CALL cp_cfm_to_fm(msource=rho(j), mtargeti=ws(2))
     480         8314 :          DO i = 1, 3
     481         5064 :             CALL cp_fm_transpose(rtbse_env%moments(i, j), ws(1))
     482         5064 :             CALL cp_fm_trace(ws(1), ws(2), moments_re(i))
     483              :             ! Scale by spin degeneracy and electron charge
     484         5064 :             moments_re(i) = -moments_re(i)*rtbse_env%spin_degeneracy
     485         6752 :             rtbse_env%moments_trace(j, i, n) = rtbse_env%moments_trace(j, i, n) + CMPLX(0.0, moments_re(i), kind=dp)
     486              :          END DO
     487              :       END DO
     488              :       ! Output to the file
     489              :       CALL print_moments(rtbse_env%moments_section, rtbse_env%unit_nr, rtbse_env%moments_trace(:, :, n), &
     490         1562 :                          rtbse_env%sim_time, .TRUE., append_opt=(rtbse_env%sim_step /= rtbse_env%sim_start_orig))
     491         1562 :    END SUBROUTINE output_moments
     492              : ! **************************************************************************************************
     493              : !> \brief Outputs the restart info (last finished iteration step) + restard density matrix
     494              : !> \param restart_section Print key section for the restart files
     495              : !> \param rho Density matrix in AO basis
     496              : !> \param time_index Time index to be written into the info file
     497              : ! **************************************************************************************************
     498          678 :    SUBROUTINE output_restart(rtbse_env, rho, time_index)
     499              :       TYPE(rtbse_env_type), POINTER                      :: rtbse_env
     500              :       TYPE(cp_cfm_type), DIMENSION(:), POINTER           :: rho
     501              :       INTEGER                                            :: time_index
     502          678 :       TYPE(cp_fm_type), DIMENSION(:), POINTER            :: workspace
     503              :       CHARACTER(len=17), DIMENSION(4)                    :: file_labels
     504              :       TYPE(cp_logger_type), POINTER                      :: logger
     505              :       INTEGER                                            :: rho_unit_nr, i
     506              : 
     507              :       ! Default labels distinguishing up to two spin species and real/imaginary parts
     508          678 :       file_labels(1) = "_SPIN_A_RE.matrix"
     509          678 :       file_labels(2) = "_SPIN_A_IM.matrix"
     510          678 :       file_labels(3) = "_SPIN_B_RE.matrix"
     511          678 :       file_labels(4) = "_SPIN_B_IM.matrix"
     512              : 
     513         1356 :       logger => cp_get_default_logger()
     514              : 
     515          678 :       workspace => rtbse_env%real_workspace
     516              : 
     517         1356 :       DO i = 1, rtbse_env%n_spin
     518          678 :          CALL cp_cfm_to_fm(rho(i), workspace(1), workspace(2))
     519              :          ! Real part
     520              :          rho_unit_nr = cp_print_key_unit_nr(logger, rtbse_env%restart_section, extension=file_labels(2*i - 1), &
     521          678 :                                             file_form="UNFORMATTED", file_position="REWIND")
     522          678 :          CALL cp_fm_write_unformatted(workspace(1), rho_unit_nr)
     523          678 :          CALL cp_print_key_finished_output(rho_unit_nr, logger, rtbse_env%restart_section)
     524              :          ! Imag part
     525              :          rho_unit_nr = cp_print_key_unit_nr(logger, rtbse_env%restart_section, extension=file_labels(2*i), &
     526          678 :                                             file_form="UNFORMATTED", file_position="REWIND")
     527          678 :          CALL cp_fm_write_unformatted(workspace(2), rho_unit_nr)
     528          678 :          CALL cp_print_key_finished_output(rho_unit_nr, logger, rtbse_env%restart_section)
     529              :          ! Info
     530              :          rho_unit_nr = cp_print_key_unit_nr(logger, rtbse_env%restart_section, extension=".info", &
     531          678 :                                             file_form="UNFORMATTED", file_position="REWIND")
     532          678 :          IF (rho_unit_nr > 0) WRITE (rho_unit_nr) time_index
     533         1356 :          CALL cp_print_key_finished_output(rho_unit_nr, logger, rtbse_env%restart_section)
     534              :       END DO
     535          678 :    END SUBROUTINE output_restart
     536              : ! **************************************************************************************************
     537              : !> \brief Reads the density matrix from restart files and updates the starting time
     538              : !> \param restart_section Print key section for the restart files
     539              : !> \param rho Density matrix in AO basis
     540              : !> \param time_index Time index to be written into the info file
     541              : ! **************************************************************************************************
     542            6 :    SUBROUTINE read_restart(rtbse_env)
     543              :       TYPE(rtbse_env_type), POINTER                      :: rtbse_env
     544              :       TYPE(cp_logger_type), POINTER                      :: logger
     545              :       CHARACTER(len=default_path_length)                 :: save_name, save_name_2
     546              :       INTEGER                                            :: rho_unit_nr, j
     547              :       CHARACTER(len=17), DIMENSION(4)                    :: file_labels
     548              : 
     549              :       ! This allows the delta kick and output of moment at time 0 in all cases
     550              :       ! except the case when both imaginary and real parts of the density are read
     551            6 :       rtbse_env%restart_extracted = .FALSE.
     552            6 :       logger => cp_get_default_logger()
     553              :       ! Start by probing/loading info file
     554            6 :       save_name = cp_print_key_generate_filename(logger, rtbse_env%restart_section, extension=".info", my_local=.FALSE.)
     555            6 :       IF (file_exists(save_name)) THEN
     556              :          CALL open_file(save_name, file_status="OLD", file_form="UNFORMATTED", file_action="READ", &
     557            4 :                         unit_number=rho_unit_nr)
     558            4 :          READ (rho_unit_nr) rtbse_env%sim_start
     559            4 :          CALL close_file(rho_unit_nr)
     560            6 :          IF (rtbse_env%unit_nr > 0) WRITE (rtbse_env%unit_nr, '(A31,I25,A24)') " RTBSE| Starting from timestep ", &
     561            4 :             rtbse_env%sim_start, ", delta kick NOT applied"
     562              :       ELSE
     563            2 :          CPWARN("Restart required but no info file found - starting from sim_step given in input")
     564              :       END IF
     565              : 
     566              :       ! Default labels distinguishing up to two spin species and real/imaginary parts
     567            6 :       file_labels(1) = "_SPIN_A_RE.matrix"
     568            6 :       file_labels(2) = "_SPIN_A_IM.matrix"
     569            6 :       file_labels(3) = "_SPIN_B_RE.matrix"
     570            6 :       file_labels(4) = "_SPIN_B_IM.matrix"
     571           12 :       DO j = 1, rtbse_env%n_spin
     572              :          save_name = cp_print_key_generate_filename(logger, rtbse_env%restart_section, &
     573            6 :                                                     extension=file_labels(2*j - 1), my_local=.FALSE.)
     574              :          save_name_2 = cp_print_key_generate_filename(logger, rtbse_env%restart_section, &
     575            6 :                                                       extension=file_labels(2*j), my_local=.FALSE.)
     576           12 :          IF (file_exists(save_name) .AND. file_exists(save_name_2)) THEN
     577              :             CALL open_file(save_name, file_status="OLD", file_form="UNFORMATTED", file_action="READ", &
     578            4 :                            unit_number=rho_unit_nr)
     579            4 :             CALL cp_fm_read_unformatted(rtbse_env%real_workspace(1), rho_unit_nr)
     580            4 :             CALL close_file(rho_unit_nr)
     581              :             CALL open_file(save_name_2, file_status="OLD", file_form="UNFORMATTED", file_action="READ", &
     582            4 :                            unit_number=rho_unit_nr)
     583            4 :             CALL cp_fm_read_unformatted(rtbse_env%real_workspace(2), rho_unit_nr)
     584            4 :             CALL close_file(rho_unit_nr)
     585              :             CALL cp_fm_to_cfm(rtbse_env%real_workspace(1), rtbse_env%real_workspace(2), &
     586            4 :                               rtbse_env%rho(j))
     587            4 :             rtbse_env%restart_extracted = .TRUE.
     588              :          ELSE
     589            2 :             CPWARN("Restart without some restart matrices - starting from SCF density.")
     590              :          END IF
     591              :       END DO
     592            6 :    END SUBROUTINE read_restart
     593              : ! **************************************************************************************************
     594              : !> \brief Linearized RT-BSE restart writer. Writes the restart set: lab-frame MO-active density
     595              : !>        matrices, the .info step index (sim_step = steps completed = the resume step), the
     596              : !>        once-per-run C_active gauge reference, and the appended RESTART.trace record feeding the
     597              : !>        FT prefix on continuation. All indices derive from sim_step so the .info/.trace
     598              : !>        bookkeeping cannot drift apart. (Full RTBSE uses the upstream output_restart above.)
     599              : !> \param rtbse_env RT-BSE environment
     600              : !> \param rho Density matrix (lab frame) to store
     601              : ! **************************************************************************************************
     602          830 :    SUBROUTINE output_restart_linearized(rtbse_env, rho)
     603              :       TYPE(rtbse_env_type), POINTER                      :: rtbse_env
     604              :       TYPE(cp_cfm_type), DIMENSION(:), POINTER           :: rho
     605          830 :       TYPE(cp_fm_type), DIMENSION(:), POINTER            :: workspace
     606              :       CHARACTER(len=17), DIMENSION(4)                    :: file_labels
     607              :       CHARACTER(len=16), DIMENSION(2)                    :: c_file_labels
     608              :       TYPE(cp_logger_type), POINTER                      :: logger
     609              :       INTEGER                                            :: rho_unit_nr, i
     610              : 
     611              :       ! Default labels distinguishing up to two spin species and real/imaginary parts
     612          830 :       file_labels(1) = "_SPIN_A_RE.matrix"
     613          830 :       file_labels(2) = "_SPIN_A_IM.matrix"
     614          830 :       file_labels(3) = "_SPIN_B_RE.matrix"
     615          830 :       file_labels(4) = "_SPIN_B_IM.matrix"
     616          830 :       c_file_labels(1) = "_SPIN_A_C.matrix"
     617          830 :       c_file_labels(2) = "_SPIN_B_C.matrix"
     618              : 
     619         1660 :       logger => cp_get_default_logger()
     620              : 
     621              :       ! In linearized RT-BSE rho is MO-active sized; otherwise AO sized.
     622          830 :       IF (ASSOCIATED(rtbse_env%real_workspace_mo)) THEN
     623          830 :          workspace => rtbse_env%real_workspace_mo
     624              :       ELSE
     625            0 :          workspace => rtbse_env%real_workspace
     626              :       END IF
     627              : 
     628         1780 :       DO i = 1, rtbse_env%n_spin
     629          950 :          CALL cp_cfm_to_fm(rho(i), workspace(1), workspace(2))
     630              :          ! Real part
     631              :          rho_unit_nr = cp_print_key_unit_nr(logger, rtbse_env%restart_section, extension=file_labels(2*i - 1), &
     632          950 :                                             file_form="UNFORMATTED", file_position="REWIND")
     633          950 :          CALL cp_fm_write_unformatted(workspace(1), rho_unit_nr)
     634          950 :          CALL cp_print_key_finished_output(rho_unit_nr, logger, rtbse_env%restart_section)
     635              :          ! Imag part
     636              :          rho_unit_nr = cp_print_key_unit_nr(logger, rtbse_env%restart_section, extension=file_labels(2*i), &
     637          950 :                                             file_form="UNFORMATTED", file_position="REWIND")
     638          950 :          CALL cp_fm_write_unformatted(workspace(2), rho_unit_nr)
     639          950 :          CALL cp_print_key_finished_output(rho_unit_nr, logger, rtbse_env%restart_section)
     640              :          ! Info
     641              :          rho_unit_nr = cp_print_key_unit_nr(logger, rtbse_env%restart_section, extension=".info", &
     642          950 :                                             file_form="UNFORMATTED", file_position="REWIND")
     643          950 :          IF (rho_unit_nr > 0) WRITE (rho_unit_nr) rtbse_env%sim_step
     644         1780 :          CALL cp_print_key_finished_output(rho_unit_nr, logger, rtbse_env%restart_section)
     645              :       END DO
     646              : 
     647              :       ! Once-per-run C_active dump (linearized only): gauge reference for the restart basis bridge
     648          830 :       IF (ASSOCIATED(rtbse_env%real_workspace_mo) .AND. .NOT. rtbse_env%restart_C_written) THEN
     649          124 :          DO i = 1, rtbse_env%n_spin
     650              :             rho_unit_nr = cp_print_key_unit_nr(logger, rtbse_env%restart_section, extension=c_file_labels(i), &
     651           66 :                                                file_form="UNFORMATTED", file_position="REWIND")
     652           66 :             CALL cp_fm_write_unformatted(rtbse_env%C_active(i), rho_unit_nr)
     653          124 :             CALL cp_print_key_finished_output(rho_unit_nr, logger, rtbse_env%restart_section)
     654              :          END DO
     655           58 :          rtbse_env%restart_C_written = .TRUE.
     656              :       END IF
     657              : 
     658          830 :       CALL write_restart_trace(rtbse_env)
     659          830 :    END SUBROUTINE output_restart_linearized
     660              : ! **************************************************************************************************
     661              : !> \brief Appends the current observable-trace record to RESTART.trace. Records are keyed by the
     662              : !>        observable slot n_slot = sim_step - sim_start_orig + 1 - the SAME index output_moments/
     663              : !>        output_field write (both drivers bump sim_step inside the propagation call before the
     664              : !>        output calls run). On the first call of a run the file is rewritten from memory (header +
     665              : !>        records 1..n_slot), truncating leftovers from an aborted run; subsequent calls append one
     666              : !>        record. Ionode writes; layout matches read_restart_trace verbatim.
     667              : !> \param rtbse_env RT-BSE environment
     668              : ! **************************************************************************************************
     669          830 :    SUBROUTINE write_restart_trace(rtbse_env)
     670              :       TYPE(rtbse_env_type), POINTER                      :: rtbse_env
     671              :       TYPE(cp_logger_type), POINTER                      :: logger
     672              :       CHARACTER(len=default_path_length)                 :: save_name
     673              :       INTEGER                                            :: trace_unit, n, n_eps, n_slot
     674              : 
     675          830 :       logger => cp_get_default_logger()
     676          830 :       save_name = cp_print_key_generate_filename(logger, rtbse_env%restart_section, extension=".trace", my_local=.FALSE.)
     677              : 
     678          830 :       IF (rtbse_env%unit_nr > 0) THEN
     679          415 :          n_slot = rtbse_env%sim_step - rtbse_env%sim_start_orig + 1
     680          415 :          IF (ASSOCIATED(rtbse_env%real_workspace_mo)) THEN
     681              :             ! scalar row count is safe: determine_active_mo_window takes the union window across
     682              :             ! spins, so eps_active is rectangular (mo_active, n_spin) by construction
     683          415 :             n_eps = SIZE(rtbse_env%eps_active, 1)
     684              :          ELSE
     685            0 :             n_eps = 0
     686              :          END IF
     687          415 :          IF (.NOT. rtbse_env%restart_trace_written) THEN
     688              :             CALL open_file(save_name, file_status="UNKNOWN", file_form="UNFORMATTED", file_action="WRITE", &
     689           29 :                            file_position="REWIND", unit_number=trace_unit)
     690           29 :             WRITE (trace_unit) restart_trace_version
     691           29 :             IF (ASSOCIATED(rtbse_env%real_workspace_mo)) THEN
     692           29 :                WRITE (trace_unit) rtbse_env%n_spin, rtbse_env%mo_active, rtbse_env%n_ao
     693              :             ELSE
     694            0 :                WRITE (trace_unit) rtbse_env%n_spin, rtbse_env%n_ao, rtbse_env%n_ao
     695              :             END IF
     696           29 :             WRITE (trace_unit) rtbse_env%sim_dt
     697          116 :             WRITE (trace_unit) REAL(rtbse_env%dft_control%rtp_control%delta_pulse_direction, dp), &
     698           58 :                rtbse_env%dft_control%rtp_control%delta_pulse_scale
     699           29 :             WRITE (trace_unit) n_eps
     700          452 :             IF (n_eps > 0) WRITE (trace_unit) rtbse_env%eps_active
     701          164 :             DO n = 1, n_slot
     702          540 :                WRITE (trace_unit) n, rtbse_env%time_trace(n), rtbse_env%field_trace(:, n), &
     703         1163 :                   rtbse_env%moments_trace(:, :, n)
     704              :             END DO
     705              :          ELSE
     706              :             CALL open_file(save_name, file_status="OLD", file_form="UNFORMATTED", file_action="WRITE", &
     707          386 :                            file_position="APPEND", unit_number=trace_unit)
     708         1544 :             WRITE (trace_unit) n_slot, rtbse_env%time_trace(n_slot), rtbse_env%field_trace(:, n_slot), &
     709         3256 :                rtbse_env%moments_trace(:, :, n_slot)
     710              :          END IF
     711          415 :          CALL close_file(trace_unit)
     712              :       END IF
     713          830 :       rtbse_env%restart_trace_written = .TRUE.
     714          830 :    END SUBROUTINE write_restart_trace
     715              : ! **************************************************************************************************
     716              : !> \brief Early phase of the restart read: the starting step index from the .info file, the original
     717              : !>        run's dt peeked from the RESTART.trace header (so ENFORCE_MAX_DT can inherit it), and an
     718              : !>        SCF_GUESS hygiene check. Runs BEFORE initialize_maximum_timestep; the trace prefix records
     719              : !>        are loaded separately by read_restart_trace once the trace arrays are sized.
     720              : !> \param rtbse_env RT-BSE environment
     721              : ! **************************************************************************************************
     722           14 :    SUBROUTINE read_restart_info(rtbse_env)
     723              :       TYPE(rtbse_env_type), POINTER                      :: rtbse_env
     724              :       TYPE(cp_logger_type), POINTER                      :: logger
     725              :       CHARACTER(len=default_path_length)                 :: save_name
     726              :       INTEGER                                            :: info_unit, trace_unit, version
     727              :       TYPE(scf_control_type), POINTER                    :: scf_control
     728              : 
     729           14 :       logger => cp_get_default_logger()
     730              : 
     731           14 :       save_name = cp_print_key_generate_filename(logger, rtbse_env%restart_section, extension=".info", my_local=.FALSE.)
     732           14 :       IF (file_exists(save_name)) THEN
     733              :          CALL open_file(save_name, file_status="OLD", file_form="UNFORMATTED", file_action="READ", &
     734           14 :                         unit_number=info_unit)
     735           14 :          READ (info_unit) rtbse_env%sim_start
     736           14 :          CALL close_file(info_unit)
     737           21 :          IF (rtbse_env%unit_nr > 0) WRITE (rtbse_env%unit_nr, '(A31,I25,A24)') " RTBSE| Starting from timestep ", &
     738           14 :             rtbse_env%sim_start, ", delta kick NOT applied"
     739              :       ELSE
     740            0 :          CPWARN("Restart required but no info file found - starting from sim_step given in input")
     741              :       END IF
     742              : 
     743              :       ! Peek the original run's dt from the trace header (version, dims record, dt) so ENFORCE_MAX_DT
     744              :       ! can inherit it rather than recompute a window-dependent dt. The dims record is skipped here;
     745              :       ! read_restart_trace re-reads the full header and validates it once the trace arrays are sized.
     746           14 :       save_name = cp_print_key_generate_filename(logger, rtbse_env%restart_section, extension=".trace", my_local=.FALSE.)
     747           14 :       IF (file_exists(save_name)) THEN
     748              :          CALL open_file(save_name, file_status="OLD", file_form="UNFORMATTED", file_action="READ", &
     749           14 :                         unit_number=trace_unit)
     750           14 :          READ (trace_unit) version
     751           14 :          IF (version == restart_trace_version) THEN
     752           14 :             READ (trace_unit)
     753           14 :             READ (trace_unit) rtbse_env%sim_dt_restart
     754              :          END IF
     755           14 :          CALL close_file(trace_unit)
     756              :       END IF
     757              : 
     758              :       ! Hygiene nudge only - correctness is protected by the restart basis bridge (linearized path)
     759           14 :       NULLIFY (scf_control)
     760           14 :       CALL get_qs_env(rtbse_env%qs_env, scf_control=scf_control)
     761           14 :       IF (scf_control%density_guess /= restart_guess) THEN
     762            0 :          CPWARN("RT_RESTART without SCF_GUESS RESTART - SCF may reconverge to a gauge-rotated MO basis.")
     763              :       END IF
     764           14 :    END SUBROUTINE read_restart_info
     765              : ! **************************************************************************************************
     766              : !> \brief Reads the RESTART.trace prefix (records 1..sim_start) into the in-memory moment/field/
     767              : !>        time traces so the continuation FT covers the full history. Header guards: version,
     768              : !>        n_spin, dims, dt (abort); kick params (warn); eps_active > 0.1 meV (warn). All ranks read
     769              : !>        (the traces are replicated). Record layout matches write_restart_trace verbatim.
     770              : !> \param rtbse_env RT-BSE environment
     771              : ! **************************************************************************************************
     772           14 :    SUBROUTINE read_restart_trace(rtbse_env)
     773              :       TYPE(rtbse_env_type), POINTER                      :: rtbse_env
     774              :       TYPE(cp_logger_type), POINTER                      :: logger
     775              :       CHARACTER(len=default_path_length)                 :: save_name, err_msg
     776              :       INTEGER                                            :: trace_unit, version, n_spin_file, &
     777              :                                                             n_basis_file, n_ao_file, n_eps, n, &
     778              :                                                             n_read_max, ios
     779              :       REAL(kind=dp)                                      :: dt_file, kick_scale_file, t_rec
     780              :       REAL(kind=dp), DIMENSION(3)                        :: kick_dir_file
     781           14 :       REAL(kind=dp), DIMENSION(:, :), ALLOCATABLE        :: eps_file
     782              :       COMPLEX(kind=dp), DIMENSION(3)                     :: field_rec
     783           14 :       COMPLEX(kind=dp), DIMENSION(:, :), ALLOCATABLE     :: mom_rec
     784              : 
     785           14 :       logger => cp_get_default_logger()
     786           14 :       save_name = cp_print_key_generate_filename(logger, rtbse_env%restart_section, extension=".trace", my_local=.FALSE.)
     787              :       ! No trace + real prior history: the FT would run on a zero prefix - either a guaranteed
     788              :       ! multi_fft abort AFTER the full propagation (default FT%START_TIME=0, delta_t=0) or a
     789              :       ! silently wrong tail-only spectrum (START_TIME>0). Fail fast instead.
     790           14 :       IF (.NOT. file_exists(save_name)) THEN
     791            0 :          IF (rtbse_env%sim_start > 0) THEN
     792              :             CALL cp_abort(__LOCATION__, &
     793              :                           "RT_RESTART without RESTART.trace - the continuation FT would miss the pre-restart "// &
     794            0 :                           "history. Restore the original run's RESTART.trace next to the density restart files.")
     795              :          END IF
     796              :          RETURN
     797              :       END IF
     798              : 
     799              :       CALL open_file(save_name, file_status="OLD", file_form="UNFORMATTED", file_action="READ", &
     800           14 :                      unit_number=trace_unit)
     801           14 :       READ (trace_unit) version
     802           14 :       IF (version /= restart_trace_version) THEN
     803            0 :          WRITE (err_msg, '(A,I0,A,I0)') "RESTART.trace: format version ", version, &
     804            0 :             " does not match this binary's version ", restart_trace_version
     805            0 :          CALL cp_abort(__LOCATION__, TRIM(err_msg))
     806              :       END IF
     807           14 :       READ (trace_unit) n_spin_file, n_basis_file, n_ao_file
     808           14 :       IF (n_spin_file /= rtbse_env%n_spin) CPABORT("RESTART.trace: n_spin mismatch")
     809           14 :       IF (n_ao_file /= rtbse_env%n_ao) CPABORT("RESTART.trace: n_ao mismatch")
     810              :       ! v1 is strict same-window: the linearized active-MO count must match (D6)
     811           14 :       IF (ASSOCIATED(rtbse_env%real_workspace_mo) .AND. n_basis_file /= rtbse_env%mo_active) THEN
     812              :          CALL cp_abort(__LOCATION__, &
     813            0 :                        "RESTART.trace: active-MO count differs from the original run - same-window continuation only")
     814              :       END IF
     815           14 :       READ (trace_unit) dt_file
     816              :       ! ENFORCE_MAX_DT inherits dt_file (read_restart_info -> initialize_maximum_timestep), so this
     817              :       ! fires only on the manual path (ENFORCE off + user dt /= original); name the exact fix.
     818           14 :       IF (ABS(dt_file - rtbse_env%sim_dt) > 1.0e-12_dp*MAX(1.0_dp, ABS(rtbse_env%sim_dt))) THEN
     819              :          WRITE (err_msg, '(A,ES16.9,A)') &
     820              :             "RESTART.trace: TIMESTEP differs from the original run - continuation undefined. "// &
     821            0 :             "Set MD%TIMESTEP [fs] ", dt_file*femtoseconds, &
     822            0 :             " or enable RTBSE%ENFORCE_MAX_DT to inherit it automatically."
     823            0 :          CALL cp_abort(__LOCATION__, TRIM(err_msg))
     824              :       END IF
     825           14 :       READ (trace_unit) kick_dir_file, kick_scale_file
     826           56 :       IF (MAXVAL(ABS(kick_dir_file - REAL(rtbse_env%dft_control%rtp_control%delta_pulse_direction, dp))) > 1.0e-12_dp .OR. &
     827              :           ABS(kick_scale_file - rtbse_env%dft_control%rtp_control%delta_pulse_scale) > 1.0e-12_dp) THEN
     828              :          CALL cp_warn(__LOCATION__, &
     829            0 :                       "RESTART.trace: delta-kick parameters differ from the original run - FT normalization inconsistent.")
     830              :       END IF
     831           14 :       READ (trace_unit) n_eps
     832           14 :       IF (n_eps > 0) THEN
     833           56 :          ALLOCATE (eps_file(n_eps, rtbse_env%n_spin))
     834           14 :          READ (trace_unit) eps_file
     835           14 :          IF (ASSOCIATED(rtbse_env%real_workspace_mo)) THEN
     836           14 :             IF (n_eps /= SIZE(rtbse_env%eps_active, 1)) THEN
     837            0 :                CALL cp_abort(__LOCATION__, "RESTART.trace: active-window size mismatch")
     838              :             END IF
     839              :             ! eps_active is not populated yet (built later in the Hamiltonian init); stash for the
     840              :             ! post-Hamiltonian consistency check in check_restart_eps_consistency.
     841           56 :             ALLOCATE (rtbse_env%eps_active_restart(n_eps, rtbse_env%n_spin))
     842          222 :             rtbse_env%eps_active_restart(:, :) = eps_file
     843              :          END IF
     844           14 :          DEALLOCATE (eps_file)
     845              :       END IF
     846              : 
     847           42 :       ALLOCATE (mom_rec(rtbse_env%n_spin, 3))
     848           14 :       n_read_max = 0
     849          168 :       DO
     850          182 :          READ (trace_unit, IOSTAT=ios) n, t_rec, field_rec, mom_rec
     851          182 :          IF (ios /= 0) EXIT
     852              :          ! Fill through slot sim_start+1: both drivers bump sim_step inside the propagation call
     853              :          ! (etrs_scf_loop / solve_rk4_timestep), so the live loop's first step writes slot
     854              :          ! sim_start+2 and slot sim_start+1 must be reloaded here.
     855          168 :          IF (n > rtbse_env%sim_start + 1) EXIT
     856          168 :          IF (n > SIZE(rtbse_env%time_trace)) THEN
     857            0 :             CPABORT("RESTART.trace: record index exceeds trace size - increase MOTION%MD%STEPS")
     858              :          END IF
     859          168 :          rtbse_env%time_trace(n) = t_rec
     860          672 :          rtbse_env%field_trace(:, n) = field_rec
     861         1242 :          rtbse_env%moments_trace(:, :, n) = mom_rec
     862          182 :          n_read_max = MAX(n_read_max, n)
     863              :       END DO
     864           14 :       DEALLOCATE (mom_rec)
     865           14 :       CALL close_file(trace_unit)
     866           14 :       IF (n_read_max < rtbse_env%sim_start) THEN
     867            0 :          CPWARN("RESTART.trace: fewer records than restart step - trace prefix incomplete.")
     868              :       END IF
     869           14 :    END SUBROUTINE read_restart_trace
     870              : ! **************************************************************************************************
     871              : !> \brief Compares the original run's active eigenvalues (stashed by read_restart_trace) against
     872              : !>        the recomputed eps_active, once the Hamiltonian is built. Prints the max deviation and
     873              : !>        warns above 0.1 meV (GW analytic continuation gives run-to-run QP noise above FP, so the
     874              : !>        threshold is deliberately loose). Frees the stash. No-op if nothing was stashed.
     875              : !> \param rtbse_env RT-BSE environment
     876              : ! **************************************************************************************************
     877           58 :    SUBROUTINE check_restart_eps_consistency(rtbse_env)
     878              :       TYPE(rtbse_env_type), POINTER                      :: rtbse_env
     879              :       REAL(kind=dp)                                      :: eps_dev
     880              : 
     881           58 :       IF (.NOT. ASSOCIATED(rtbse_env%eps_active_restart)) RETURN
     882              : 
     883          222 :       eps_dev = MAXVAL(ABS(rtbse_env%eps_active_restart - rtbse_env%eps_active))
     884           14 :       IF (rtbse_env%unit_nr > 0) WRITE (rtbse_env%unit_nr, '(A,ES12.3,A)') &
     885            7 :          " RTBSE| Restart eps_active max deviation vs original run ", eps_dev, " Ha"
     886              :       ! 0.1 meV = 1.0e-4 eV, converted to Ha via evolt (Ha -> eV factor)
     887           14 :       IF (eps_dev > 1.0e-4_dp/evolt) THEN
     888              :          CALL cp_warn(__LOCATION__, &
     889              :                       "RESTART.trace: active eigenvalues deviate beyond 0.1 meV - "// &
     890            0 :                       "Hamiltonian changed; continuation is physically inconsistent.")
     891              :       END IF
     892              : 
     893           14 :       DEALLOCATE (rtbse_env%eps_active_restart)
     894           14 :       NULLIFY (rtbse_env%eps_active_restart)
     895              :    END SUBROUTINE check_restart_eps_consistency
     896              : ! **************************************************************************************************
     897              : !> \brief Late phase of the restart read: overwrites rho from the lab-frame restart matrices and
     898              : !>        sets restart_extracted. MO-active for the linearized path, AO otherwise
     899              : !>        (cp_fm_read_unformatted aborts on a size mismatch, guarding a changed active window).
     900              : !> \param rtbse_env RT-BSE environment
     901              : ! **************************************************************************************************
     902           14 :    SUBROUTINE read_restart_density(rtbse_env)
     903              :       TYPE(rtbse_env_type), POINTER                      :: rtbse_env
     904              :       TYPE(cp_logger_type), POINTER                      :: logger
     905              :       CHARACTER(len=default_path_length)                 :: save_name, save_name_2
     906              :       INTEGER                                            :: rho_unit_nr, j
     907              :       CHARACTER(len=17), DIMENSION(4)                    :: file_labels
     908           14 :       TYPE(cp_fm_type), DIMENSION(:), POINTER            :: ws
     909              : 
     910              :       ! This allows the delta kick and output of moment at time 0 in all cases
     911              :       ! except the case when both imaginary and real parts of the density are read
     912           14 :       rtbse_env%restart_extracted = .FALSE.
     913           28 :       logger => cp_get_default_logger()
     914              : 
     915           14 :       IF (ASSOCIATED(rtbse_env%real_workspace_mo)) THEN
     916           14 :          ws => rtbse_env%real_workspace_mo
     917              :       ELSE
     918            0 :          ws => rtbse_env%real_workspace
     919              :       END IF
     920              : 
     921              :       ! Default labels distinguishing up to two spin species and real/imaginary parts
     922           14 :       file_labels(1) = "_SPIN_A_RE.matrix"
     923           14 :       file_labels(2) = "_SPIN_A_IM.matrix"
     924           14 :       file_labels(3) = "_SPIN_B_RE.matrix"
     925           14 :       file_labels(4) = "_SPIN_B_IM.matrix"
     926           30 :       DO j = 1, rtbse_env%n_spin
     927              :          save_name = cp_print_key_generate_filename(logger, rtbse_env%restart_section, &
     928           16 :                                                     extension=file_labels(2*j - 1), my_local=.FALSE.)
     929              :          save_name_2 = cp_print_key_generate_filename(logger, rtbse_env%restart_section, &
     930           16 :                                                       extension=file_labels(2*j), my_local=.FALSE.)
     931           30 :          IF (file_exists(save_name) .AND. file_exists(save_name_2)) THEN
     932              :             CALL open_file(save_name, file_status="OLD", file_form="UNFORMATTED", file_action="READ", &
     933           16 :                            unit_number=rho_unit_nr)
     934           16 :             CALL cp_fm_read_unformatted(ws(1), rho_unit_nr)
     935           16 :             CALL close_file(rho_unit_nr)
     936              :             CALL open_file(save_name_2, file_status="OLD", file_form="UNFORMATTED", file_action="READ", &
     937           16 :                            unit_number=rho_unit_nr)
     938           16 :             CALL cp_fm_read_unformatted(ws(2), rho_unit_nr)
     939           16 :             CALL close_file(rho_unit_nr)
     940              :             CALL cp_fm_to_cfm(ws(1), ws(2), &
     941           16 :                               rtbse_env%rho(j))
     942           16 :             rtbse_env%restart_extracted = .TRUE.
     943              :          ELSE
     944            0 :             CPWARN("Restart without some restart matrices - starting from SCF density.")
     945              :          END IF
     946              :       END DO
     947           14 :    END SUBROUTINE read_restart_density
     948              : ! **************************************************************************************************
     949              : !> \brief Reads the previous run's C_active slabs (gauge reference for the restart basis bridge).
     950              : !> \param rtbse_env RT-BSE environment
     951              : !> \param C_old Caller-created fm array (n_spin) on fm_struct_ao_mo_active, filled on success
     952              : !> \param found .TRUE. iff all per-spin C files were present and read
     953              : ! **************************************************************************************************
     954           14 :    SUBROUTINE read_restart_C(rtbse_env, C_old, found)
     955              :       TYPE(rtbse_env_type), POINTER                      :: rtbse_env
     956              :       TYPE(cp_fm_type), DIMENSION(:), POINTER            :: C_old
     957              :       LOGICAL, INTENT(OUT)                               :: found
     958              :       TYPE(cp_logger_type), POINTER                      :: logger
     959              :       CHARACTER(len=default_path_length)                 :: save_name
     960              :       CHARACTER(len=16), DIMENSION(2)                    :: c_file_labels
     961              :       INTEGER                                            :: c_unit, j
     962              : 
     963           14 :       c_file_labels(1) = "_SPIN_A_C.matrix"
     964           14 :       c_file_labels(2) = "_SPIN_B_C.matrix"
     965           14 :       logger => cp_get_default_logger()
     966           14 :       found = .FALSE.
     967           30 :       DO j = 1, rtbse_env%n_spin
     968              :          save_name = cp_print_key_generate_filename(logger, rtbse_env%restart_section, &
     969           16 :                                                     extension=c_file_labels(j), my_local=.FALSE.)
     970           16 :          IF (.NOT. file_exists(save_name)) THEN
     971            0 :             CPWARN("Restart without C_active file - assuming identical MO gauge (no basis bridge).")
     972            0 :             RETURN
     973              :          END IF
     974              :          CALL open_file(save_name, file_status="OLD", file_form="UNFORMATTED", file_action="READ", &
     975           16 :                         unit_number=c_unit)
     976           16 :          CALL cp_fm_read_unformatted(C_old(j), c_unit)
     977           30 :          CALL close_file(c_unit)
     978              :       END DO
     979           14 :       found = .TRUE.
     980              :    END SUBROUTINE read_restart_C
     981              : END MODULE rt_bse_io
        

Generated by: LCOV version 2.0-1