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

Generated by: LCOV version 2.0-1