!# This source file is part of code Pégase.3.0.1 (2019-02-21).
!# Copyright: Michel Fioc (Michel.Fioc@iap.fr), Sorbonne université,
!# Institut d'astrophysique de Paris/CNRS, France.
!#
!# Pégase.3.0.1 is governed by the CeCILL license under French law and abides
!# by the rules of distribution of free software. You can use, modify and/or
!# redistribute this software under the terms of the CeCILL license as circulated
!# by CEA, CNRS and INRIA at "http://www.cecill.info". The text of this license
!# is also available in French and in English in directory "doc_dir/" of this
!# code.
!#
!# As a counterpart to the access to the source code and to the rights to copy,
!# modify and redistribute it granted by the license, users are provided only
!# with a limited warranty, and the software's author, the holder of the
!# economic rights, and the successive licensors have only limited
!# liability.
!#
!# The fact that you are presently reading this means that you have had
!# knowledge of the CeCILL license and that you accept its terms.
!#======================================================================

module mod_write_spectra_output

  implicit none
  private

  public :: write_spectra_main_preamble, write_spectra_various, write_spectra_SED, &
       write_spectra_warning, write_spectra_grain_preamble

contains

!#======================================================================

  subroutine write_spectra_main_preamble(unit_spectra, spectra_output, time_step, &
       dim_output_age, dim_elem, elem_id, &
       dim_species_SFC, dim_species_DISM, &
       species_id_SFC, species_id_DISM, dim_cont, dim_line, &
       lambda_cont, lambda_line, line_id, &
       max_dim_reserv, &
       reserv_warn_present, SF_warn_present, SF_warn_age, &
       infall_warn_present, infall_warn_age, &
       outflow_warn_present, outflow_warn_age, &
       run_begin_time, dim_SSP, header_SSPs)

    use mod_types
    use mod_convert_type, only : to_string
    use mod_strings, only : quote_string
    use mod_constants, only : in_um_to_in_A
    use mod_write_scenario_param, only : write_scenario_param
    use mod_code_version, only : version_id, version_date
    use mod_compiler, only : compiler
    use mod_linked_list
    implicit none
    integer, intent(in) :: unit_spectra, dim_output_age, dim_elem, dim_cont, dim_line, max_dim_reserv
    integer, intent(in) :: dim_species_SFC, dim_species_DISM
    character(len=*), intent(in) :: spectra_output
    character(len=*), dimension(:), intent(in) :: elem_id, line_id
    character(len=*), dimension(:), intent(in) :: species_id_SFC, species_id_DISM
    real(CDR), intent(in) :: time_step
    real(CDR), dimension(:), intent(in) :: lambda_cont, lambda_line
    logical, intent(in) :: reserv_warn_present, SF_warn_present, &
         outflow_warn_present
    logical, dimension(:), intent(in) :: infall_warn_present
    real(CDR), intent(in) :: SF_warn_age, &
         outflow_warn_age
    real(CDR), dimension(:), intent(in) :: infall_warn_age
    character(len=*), intent(in) :: run_begin_time
    integer, intent(in) :: dim_SSP
    type(lk_lst_long_string), dimension(:) :: header_SSPs
!#......................................................................
    integer :: i_elem, i_species, i_line, i_reserv, line_id_max_length
!#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

    call write_scenario_param( &
         version_id, &
         version_date, &
         compiler, &
         run_begin_time, &
         dim_SSP, &
         header_SSPs, &
         unit_spectra, line_prefix = "!#")

    line_id_max_length = maxval(len_trim(line_id(:)))

    if (spectra_output /= "none") then
       write(unit_spectra, "(f8.2,a)") time_step, " !# `time_step`."
       write(unit_spectra, "(i0,a)") dim_output_age, " !# `dim_output_age`."
       write(unit_spectra, "(i0,a)") dim_elem, " !# `dim_elem`."
       write(unit_spectra, "(" // to_string(dim_elem) // "(a,tr1),a)") &
            (quote_string(elem_id(i_elem)), i_elem=1,dim_elem), &
            "!# `elem_id(1:dim_elem)`."
       write(unit_spectra, "(i0,tr1,i0,a)") dim_species_SFC, &
            dim_species_DISM, " !# `dim_species_SFC`, `dim_species_DISM`."
       write(unit_spectra, "(" // to_string(dim_species_SFC) // "(a,tr1),a)") &
            (quote_string(species_id_SFC(i_species)), i_species=1,dim_species_SFC), &
            "!# `species_id_SFC(1:dim_species_SFC)`."
       write(unit_spectra, "(" // to_string(dim_species_DISM) // "(a,tr1),a)") &
            (quote_string(species_id_DISM(i_species)), i_species=1,dim_species_DISM), &
            "!# `species_id_DISM(1:dim_species_DISM)`."
       write(unit_spectra, "(i0,a)") dim_cont, " !# `dim_cont`."
       write(unit_spectra, "(i0,a)") dim_line, " !# `dim_line`."
       write(unit_spectra, "(a)") "!# `lambda_cont`:"
!# Unit conversion:
       write(unit_spectra, "(5(f10.1, tr2))") &
            lambda_cont(1:dim_cont)*in_um_to_in_A !# -> wavelength in angstroems.
       write(unit_spectra, "(a)") "!# `line_id`, `lambda_line`:"
       write(unit_spectra, "(a,t" // to_string(line_id_max_length+4) // ",f9.1)") &
            (quote_string(line_id(i_line)), &
            lambda_line(i_line)*in_um_to_in_A, & !# -> wavelength in angstroems.
            i_line = 1, dim_line)

       write(unit_spectra, "(2a)") to_string(reserv_warn_present), &
            " !# `reserv_warn_present`."

       write(unit_spectra, "(2a)") to_string(SF_warn_present), &
            " !# `SF_warn_present`."
       if (SF_warn_present) then
          write(unit_spectra, "(f8.2, a)") SF_warn_age, " !# `SF_warn_age`."
       endif

       write(unit_spectra, "(i0,a)") count(infall_warn_present), &
            " !# `dim_infall_warn`."
       do i_reserv = 1, max_dim_reserv
          if (infall_warn_present(i_reserv)) then
             write(unit_spectra, "(i0,tr1,f8.2, a)") i_reserv, &
                  infall_warn_age(i_reserv), &
                  " !# `infall_warn_present` for this reservoir, `infall_warn_age`."
          endif
       enddo

       write(unit_spectra, "(2a)") to_string(outflow_warn_present), &
            " !# `outflow_warn_present`."
       if (outflow_warn_present) then
          write(unit_spectra, "(f8.2, a)") outflow_warn_age, " !# `outflow_warn_age`."
       endif
    endif

  end subroutine write_spectra_main_preamble

!#======================================================================

  subroutine write_spectra_various(unit_spectra, spectra_output, &
       time, convol_time, cosmic_time, redshift, &
       galaxy_mass, live_stars_mass, WD_mass, BHNS_mass, &
       inert_mass, ISM_mass, ISM_Z, &
       stel_Z_mass_avrg, stel_Z_bol_avrg, carb_abund, sil_abund, &
       ISM_abund, L_bol, tau_V, dust_bol_ratio, SF_rate, &
       Lyman_cont_rate, CCSN_rate, SNIa_rate, &
       stel_age_mass_avrg, stel_age_bol_avrg, &
       Lyman_cont_gas_abs, Lyman_cont_dust_abs, &
       ejec_rate_tot, infall_rate, outflow_rate, &
       ejec_cumul_mass, SF_live_cumul_mass, &
       infall_cumul_mass, outflow_cumul_mass, &
       L_dust_SFC, L_dust_DISM, &
       opt_depth_warn_present, opt_depth_warn_min_lambda, opt_depth_warn_max_lambda)

    use mod_types
    use mod_convert_type, only : to_string

    implicit none
    integer, intent(in) :: unit_spectra
    character(len=*), intent(in) :: spectra_output
    real(CDR), intent(in) :: time, convol_time, cosmic_time, &
         redshift, galaxy_mass, live_stars_mass, WD_mass, BHNS_mass, &
         inert_mass, ISM_mass, ISM_Z, &
         stel_Z_mass_avrg, stel_Z_bol_avrg, carb_abund, sil_abund
    real(CDR), dimension(:), intent(in) :: ISM_abund
    real(CDR), intent(in) :: L_bol, tau_V, dust_bol_ratio, &
         Lyman_cont_rate, &
         stel_age_mass_avrg, stel_age_bol_avrg, Lyman_cont_gas_abs, Lyman_cont_dust_abs, &
         L_dust_SFC, L_dust_DISM
    real(DPR), intent(in) :: SF_rate, CCSN_rate, SNIa_rate, &
         ejec_rate_tot, infall_rate, outflow_rate, &
         ejec_cumul_mass, SF_live_cumul_mass, &
         infall_cumul_mass, outflow_cumul_mass
    logical :: opt_depth_warn_present
    real(CDR) :: opt_depth_warn_min_lambda, opt_depth_warn_max_lambda
!#......................................................................
    integer :: dim_elem
!#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

    if (spectra_output /= "none") then
       write(unit_spectra, "(f8.2, a)") time, " !# `output_age`."
       write(unit_spectra, "(f8.2, a)") convol_time, &
            " !# `convol_time`."
       write(unit_spectra, "(es11.4, a)") cosmic_time, &
            " !# `cosmic_time`."
       write(unit_spectra, "(es11.4, a)") redshift, &
            " !# `redshift`."
       write(unit_spectra, "(es10.4, a)") galaxy_mass, &
            " !# `galaxy_mass`."
       write(unit_spectra, "(es10.4, a)") live_stars_mass, &
            " !# `live_stars_mass`."
       write(unit_spectra, "(es10.4, a)") WD_mass, " !# `WD_mass`."
       write(unit_spectra, "(es10.4, a)") BHNS_mass, &
            " !# `BHNS_mass`."
       write(unit_spectra, "(es10.4, a)") inert_mass, &
            " !# `inert_mass`."
       write(unit_spectra, "(es10.4, a)") ISM_mass, &
            " !# `ISM_mass`."
       write(unit_spectra, "(es10.4, a)") ISM_Z, &
            " !# `ISM_Z`."
       write(unit_spectra, "(es10.4, a)") stel_Z_mass_avrg, &
            " !# `stel_Z_mass_avrg`."
       write(unit_spectra, "(es10.4, a)") stel_Z_bol_avrg, &
            " !# `stel_Z_bol_avrg`."
       write(unit_spectra, "(es10.4, a)") carb_abund, " !# `carb_abund`."
       write(unit_spectra, "(es10.4, a)") sil_abund, " !# `sil_abund`."
       dim_elem = size(ISM_abund)
       write(unit_spectra, &
            "(" // to_string(dim_elem) // "(es10.4,tr1), tl1, a)") &
            ISM_abund(:), &
            " !# `ISM_abund` for elements `elem_id(1:dim_elem)`."
       write(unit_spectra, "(es10.4, a)") L_bol, " !# `L_bol`."
       write(unit_spectra, "(es10.4, a)") tau_V, " !# `tau_V`."
       write(unit_spectra, "(es10.4, a)") dust_bol_ratio, " !# `dust_bol_ratio`."
       write(unit_spectra, "(es10.4, a)") SF_rate, " !# `SF_rate`."
       write(unit_spectra, "(es10.4, a)") Lyman_cont_rate, &
            " !# `Lyman_cont_rate`."
       write(unit_spectra, "(es10.4, a)") CCSN_rate, &
            " !# `CCSN_rate`."
       write(unit_spectra, "(es10.4, a)") SNIa_rate, &
            " !# `SNIa_rate`."
       write(unit_spectra, "(es10.4, a)")  stel_age_mass_avrg, &
            " !# `stel_age_mass_avrg`."
       write(unit_spectra, "(es10.4, a)") stel_age_bol_avrg, &
            " !# `stel_age_bol_avrg`."
       write(unit_spectra, "(es10.4, a)") Lyman_cont_gas_abs, &
            " !# `Lyman_cont_gas_abs`."
       write(unit_spectra, "(es10.4, a)") Lyman_cont_dust_abs, &
            " !# `Lyman_cont_dust_abs`."
       write(unit_spectra, "(es10.4, a)") ejec_rate_tot, &
            " !# `ejec_rate_tot`."
       write(unit_spectra, "(es10.4, a)") infall_rate, &
            " !# `infall_rate`."
       write(unit_spectra, "(es10.4, a)") outflow_rate, &
            " !# `outflow_rate`."
       write(unit_spectra, "(es10.4, a)") ejec_cumul_mass, &
            " !# `ejec_cumul_mass`."
       write(unit_spectra, "(es10.4, a)") SF_live_cumul_mass, &
            " !# `SF_live_cumul_mass`."
       write(unit_spectra, "(es10.4, a)") infall_cumul_mass, &
            " !# `infall_cumul_mass`."
       write(unit_spectra, "(es10.4, a)") outflow_cumul_mass, &
            " !# `outflow_cumul_mass`."
       write(unit_spectra, "(es10.4, a)") L_dust_SFC, " !# `L_dust_SFC`."
       write(unit_spectra, "(es10.4, a)") L_dust_DISM, " !# `L_dust_DISM`."

       write(unit_spectra, "(2a)") to_string(opt_depth_warn_present), " !# `opt_depth_warn_present`."
       if (opt_depth_warn_present) then
          write(unit_spectra, "(es10.4, a)") opt_depth_warn_min_lambda, &
               " !# `opt_depth_warn_min_lambda`."
          write(unit_spectra, "(es10.4, a)") opt_depth_warn_max_lambda, &
               " !# `opt_depth_warn_max_lambda`."
       endif
    endif

  end subroutine write_spectra_various

!#======================================================================

  subroutine write_spectra_SED(unit_spectra, spectra_output, &
       dim_species_SFC, dim_species_DISM, dim_cont, dim_line, &
       lum_cont, lum_stel_SFC_unatt, lum_stel_DISM_unatt, &
       lum_neb_cont_SFC_unatt, lum_neb_cont_DISM_unatt, &
       lum_species_SFC, lum_species_DISM, &
       L_line, L_line_SFC_unatt, L_line_DISM_unatt, &
       RF_output, RF_cont_SFC, RF_cont_DISM, RF_line_SFC, RF_line_DISM, &
       sublim_output, sublim_lum_species_SFC, sublim_lum_species_DISM)

    use mod_types
    use mod_convert_type, only : to_string, E_format
    use mod_constants, only : L_sol, per_um_to_per_A

    implicit none
    integer, intent(in) :: unit_spectra, dim_species_SFC, dim_species_DISM, dim_cont, dim_line
    character(len=*), intent(in) :: spectra_output
    real(CDR), dimension(:), intent(in) :: lum_cont, lum_stel_SFC_unatt, &
         lum_stel_DISM_unatt, lum_neb_cont_SFC_unatt, lum_neb_cont_DISM_unatt
    real(CDR), dimension(:,:), intent(in) :: lum_species_SFC, lum_species_DISM
    real(CDR), dimension(:), intent(in) :: L_line, L_line_SFC_unatt, &
         L_line_DISM_unatt
    logical, intent(in) :: RF_output
    real(CDR), dimension(:), intent(in) :: RF_cont_SFC, RF_cont_DISM
    real(CDR), dimension(:), intent(in) :: RF_line_SFC, RF_line_DISM
    logical, intent(in) :: sublim_output
    real(CDR), dimension(:,:), intent(in) :: sublim_lum_species_SFC, sublim_lum_species_DISM
!#......................................................................
    integer :: i_cont, i_line
!#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

!# `SFC`, `DISM`: star-forming clouds, diffuse ISM.
!#
!# `lum_stel_*_unatt`: unattenuated stellar spectrum from stars in region `*`.
!#
!# `lum_neb_cont_*_unatt`: unattenuated nebular continuum from ionized gas
!# in region `*`.
!#
!# `L_line_*_unatt`: the same for emission lines.
!#
!# `lum_species_*`: spectrum emitted by dust species in region `*`.
!#
!# `lum_cont`: emerging overall continuum spectrum (monochromatic luminosity in erg.s-1.angstroem-1.).
!#
!# `L_line`: the same for emission lines (in erg.s-1).
!#
!# `RF_cont` and `RF_line`: radiation field at continuum and line wavelengths.

    if (spectra_output == "detailed") then
       if (RF_output) then
          write(unit_spectra, "(a)") &
               "!# `lum_cont`, `lum_stel_SFC_unatt`, `lum_stel_DISM_unatt`, &
               &`lum_neb_cont_SFC_unatt`, `lum_neb_cont_DISM_unatt`, &
               &`RF_cont_SFC`, `RF_cont_DISM`:"
          do i_cont = 1, dim_cont
             write(unit_spectra, "(7(a9, tr1))") &
                  E_format(lum_cont(i_cont), 9, 3), &
                  E_format(lum_stel_SFC_unatt(i_cont)*L_sol, 9, 3), &
                  E_format(lum_stel_DISM_unatt(i_cont)*L_sol, 9, 3), &
                  E_format(lum_neb_cont_SFC_unatt(i_cont)*L_sol, 9, 3), &
                  E_format(lum_neb_cont_DISM_unatt(i_cont)*L_sol, 9, 3), &
                  E_format(RF_cont_SFC(i_cont), 9, 3), &
                  E_format(RF_cont_DISM(i_cont), 9, 3)
          enddo
          write(unit_spectra, "(a,i0,a,i0,a)") &
               "!# `lum_species_SFC(1:", dim_species_SFC, &
               ")`, `lum_species_DISM(1:", dim_species_DISM, &
               ")`:"
          do i_cont = 1, dim_cont
             write(unit_spectra, "(" // &
                  to_string(dim_species_SFC+dim_species_DISM) // &
                  "(a9, tr1))") &
!# Unit conversion:
                  E_format(lum_species_SFC(1:dim_species_SFC,i_cont)*per_um_to_per_A, 9, 3), &
                  E_format(lum_species_DISM(1:dim_species_DISM,i_cont)*per_um_to_per_A, 9, 3)
          enddo
       else !# `RF_output = .false.`.
          write(unit_spectra, "(a)") &
               "!# `lum_cont`, `lum_stel_SFC_unatt`, `lum_stel_DISM_unatt`, &
               &`lum_neb_cont_SFC_unatt`, `lum_neb_cont_DISM_unatt`:"
          do i_cont = 1, dim_cont
             write(unit_spectra, "(5(a9, tr1))") &
                  E_format(lum_cont(i_cont), 9, 3), &
                  E_format(lum_stel_SFC_unatt(i_cont)*L_sol, 9, 3), &
                  E_format(lum_stel_DISM_unatt(i_cont)*L_sol, 9, 3), &
                  E_format(lum_neb_cont_SFC_unatt(i_cont)*L_sol, 9, 3), &
                  E_format(lum_neb_cont_DISM_unatt(i_cont)*L_sol, 9, 3)
          enddo
          write(unit_spectra, "(a,i0,a,i0,a)") &
               "!# `lum_species_SFC(1:", dim_species_SFC, &
               ")`, `lum_species_DISM(1:", dim_species_DISM, &
               ")`:"
          do i_cont = 1, dim_cont
             write(unit_spectra, "(" // to_string(dim_species_SFC+dim_species_DISM) // "(a9, tr1))") &
!# Unit conversion:
                  E_format(lum_species_SFC(1:dim_species_SFC,i_cont)*per_um_to_per_A, 9, 3), &
                  E_format(lum_species_DISM(1:dim_species_DISM,i_cont)*per_um_to_per_A, 9, 3)
          enddo
       endif

       if (sublim_output) then
          write(unit_spectra, "(a,i0,a,i0,a)") &
               "!# `sublim_lum_species_SFC(1:", dim_species_SFC, &
               ")`, `sublim_lum_species_DISM(1:", dim_species_DISM, ")`:"
          do i_cont = 1, dim_cont
             write(unit_spectra, "(" // to_string(dim_species_SFC+dim_species_DISM) // "(a9, tr1))") &
!# Unit conversion:
                  E_format(sublim_lum_species_SFC(1:dim_species_SFC,i_cont)*per_um_to_per_A, 9, 3), &
                  E_format(sublim_lum_species_DISM(1:dim_species_DISM,i_cont)*per_um_to_per_A, 9, 3)
          enddo
       endif

       if (RF_output) then
          write(unit_spectra, "(a)") "!# `L_line`, `L_line_SFC_unatt`, &
               &`L_line_DISM_unatt`, `RF_line_SFC`, `RF_line_DISM`:"
          do i_line = 1, dim_line
             write(unit_spectra, "(5(a9, tr1))") &
                  E_format(L_line(i_line), 9, 3), &
                  E_format(L_line_SFC_unatt(i_line)*L_sol, 9, 3), &
                  E_format(L_line_DISM_unatt(i_line)*L_sol, 9, 3), &
                  E_format(RF_line_SFC(i_line), 9, 3), &
                  E_format(RF_line_DISM(i_line), 9, 3)
          enddo
       else
          write(unit_spectra, "(a)") "!# `L_line`, `L_line_SFC_unatt`, &
               &`L_line_DISM_unatt`:"
          do i_line = 1, dim_line
             write(unit_spectra, "(3(a9, tr1))") &
                  E_format(L_line(i_line), 9, 3), &
                  E_format(L_line_SFC_unatt(i_line)*L_sol, 9, 3), &
                  E_format(L_line_DISM_unatt(i_line)*L_sol, 9, 3)
          enddo
       endif

    else if (spectra_output == "basic") then
       write(unit_spectra, "(a)") "!# `lum_cont`:"
       write(unit_spectra, "(10(a9, tr1))") &
            E_format(lum_cont(1:dim_cont), 9, 3)
       write(unit_spectra, "(a)") "!# `L_line`:"
       write(unit_spectra, "(10(a9, tr1))") &
            E_format(L_line(1:dim_line), 9, 3)
    endif

  end subroutine write_spectra_SED

!#======================================================================

  subroutine write_spectra_warning(unit_spectra, spectra_file, &
       spectra_output, unit_log, verbosity, output_age, max_dim_reserv, &
       reserv_warn_present, &
       SF_warn_present, SF_warn_age, &
       infall_warn_present, infall_warn_age, &
       outflow_warn_present, outflow_warn_age, &
       opt_depth_warn_present, opt_depth_warn_min_lambda, opt_depth_warn_max_lambda)

    use mod_types
    use mod_strings, only : quote_string
    use mod_convert_type, only : to_string
    use mod_write_scenario_param, only : ord_scenario
    implicit none
    integer, intent(in) :: unit_spectra, unit_log, verbosity, max_dim_reserv
    character(len=*), intent(in) :: spectra_file, spectra_output
    real(CDR), dimension(:), intent(in) :: output_age, infall_warn_age, &
         opt_depth_warn_min_lambda, opt_depth_warn_max_lambda
    logical, dimension(:), intent(in) :: infall_warn_present, opt_depth_warn_present
    logical, intent(in) :: reserv_warn_present, SF_warn_present, outflow_warn_present
    real(CDR), intent(in) :: SF_warn_age, outflow_warn_age
!#......................................................................
    integer :: i_reserv, i_output_age
!#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

    if (reserv_warn_present .or. SF_warn_present .or. &
         any(infall_warn_present) .or. outflow_warn_present .or. &
         any(opt_depth_warn_present)) then
       write(unit_log, "(/a,i0)", advance="no") "Scenario number ", ord_scenario
       if (spectra_output /= "none") then
          write(unit_log, "(3a/)") ", file ", &
               quote_string(spectra_file), ":"
       else
          write(unit_log, "(a/)") ":"
       endif
    endif

    if (reserv_warn_present) then
       if (spectra_output /= "none") &
            write(unit_spectra, "(a/,a/,a)") "!# WARNING: &
            &the initial normalized mass of the &
            &galaxy computed from the normalized masses of the reservoir &
            &was negative.", &
            "Initial values of `reserv_mass(:)` have been scaled so that &
            &`galaxy_mass` is null at t = 0.", &
            "(The input parameters `reserv_init_mass(:)` are not changed.)"
       write(unit_log, "(a/,a/,a)") "!# WARNING: &
            &the initial normalized mass of the &
            &galaxy computed from the normalized masses of the reservoir &
            &was negative.", &
            "Initial values of `reserv_mass(:)` have been scaled so that &
            &`galaxy_mass` is null at t = 0.", &
            "(The input parameters `reserv_init_mass(:)` are not changed.)"
       if (verbosity >= 0) &
            write(*, "(2(a/),a)") &
            "WARNING: " // quote_string(spectra_file) // &
            ": the initial normalized mass of the &
            &galaxy computed from the normalized masses of the reservoir &
            &was negative.", &
            "Initial values of `reserv_mass(:)` have been scaled so that &
            &`galaxy_mass` is null at t = 0.", &
            "(The input parameters `reserv_init_mass(:)` are not changed.)"
    endif

    if (SF_warn_present) then
       if (spectra_output /= "none") &
            write(unit_spectra, "(a)") "!# WARNING: &
            &the SFR has exceeded the maximal possible rate at " // &
            to_string(SF_warn_age) // " Myr."
       if (verbosity >= 0) &
            write(*, "(a)") "WARNING: " // quote_string(spectra_file) // &
            ": the SFR has exceeded the maximal possible rate at " // &
            to_string(SF_warn_age) // " Myr."
    endif

    do i_reserv = 1, max_dim_reserv
       if (infall_warn_present(i_reserv)) then
          if (spectra_output /= "none") &
               write(unit_spectra, "(a,i0,a)") "!# WARNING: &
               &the infall rate from reservoir ", i_reserv, &
               " has exceeded the maximal possible rate at " // &
               to_string(infall_warn_age(i_reserv)) // " Myr."
          write(unit_log, "(a,i0,a)") "!# WARNING: &
               &the infall rate from reservoir ", i_reserv, &
               " has exceeded the maximal possible rate at " // &
               to_string(infall_warn_age(i_reserv)) // " Myr."
          if (verbosity >= 0) &
               write(*, "(a,i0,a)") "WARNING: " // quote_string(spectra_file) // &
               ": the infall rate from reservoir ", i_reserv, &
               " has exceeded the maximal possible rate at " // &
               to_string(infall_warn_age(i_reserv)) // " Myr."
       endif
    enddo

    if (outflow_warn_present) then
       if (spectra_output /= "none") &
            write(unit_spectra, "(a)") &
            "!# WARNING: the outflow rate &
            &has exceeded the maximal possible rate at " // &
            to_string(outflow_warn_age) // " Myr."
       write(unit_log, "(a)") &
            "!# WARNING: the outflow rate &
            &has exceeded the maximal possible rate at " // &
            to_string(outflow_warn_age) // " Myr."
       if (verbosity >= 0) &
            write(*, "(a)") "WARNING: " // quote_string(spectra_file) // &
            ": the outflow rate has exceeded the maximal possible &
            &rate at " // to_string(outflow_warn_age) // " Myr."
    endif

    do i_output_age = 1, size(output_age)
       if (opt_depth_warn_present(i_output_age)) then
          if (spectra_output /= "none")  &
               write(unit_spectra, "(a)") "!# WARNING: &
               &the optical depth exceeds the maximal value &
               &for which the radiative transfer has been precomputed at " // &
               to_string(output_age(i_output_age)) // &
               " Myr for wavelengths in [" // &
               to_string(opt_depth_warn_min_lambda(i_output_age)) // ", " // &
               to_string(opt_depth_warn_max_lambda(i_output_age)) // "] micrometers."
          write(unit_log, "(a)") "!# WARNING: &
               &the optical depth exceeds the maximal value &
               &for which the radiative transfer has been precomputed at " // &
               to_string(output_age(i_output_age)) // &
               " Myr for wavelengths in [" // &
               to_string(opt_depth_warn_min_lambda(i_output_age)) // ", " // &
               to_string(opt_depth_warn_max_lambda(i_output_age)) // "] micrometers."
          if (verbosity >= 0) write(*, "(a)") "WARNING: " // &
               quote_string(spectra_file) // &
               ": the optical depth exceeds the maximal value for which &
               &the radiative transfer has been precomputed at " // &
               to_string(output_age(i_output_age)) // &
               " Myr for wavelengths in [" // &
               to_string(opt_depth_warn_min_lambda(i_output_age)) // ", " // &
               to_string(opt_depth_warn_max_lambda(i_output_age)) // "] micrometers."
       endif
    enddo

  end subroutine write_spectra_warning

!#======================================================================

  subroutine write_spectra_grain_preamble(unit_grain_temp, unit_grain_SED, &
       dim_species_SFC, dim_species_DISM, &
       stoch_heating_SFC, stoch_heating_DISM, &
       species_id_SFC, species_id_DISM, output_age, &
       grain_output_min_age, grain_output_max_age, &
       grain_output_SFC, grain_output_DISM, &
       grain_temp_output, grain_SED_output, grain_abs_SFC, grain_abs_DISM, &
       run_begin_time, dim_SSP, header_SSPs)

    use mod_types
    use mod_grain_properties, only : struct_grain_abs
    use mod_strings, only : quote_string
    use mod_write_scenario_param, only : write_scenario_param, write_parameter
    use mod_code_version, only : version_id, version_date
    use mod_compiler, only : compiler
    use mod_linked_list
    implicit none
    integer, intent(in) :: unit_grain_temp, unit_grain_SED
    integer, intent(in) :: dim_species_SFC, dim_species_DISM
    logical, intent(in) :: stoch_heating_SFC, stoch_heating_DISM
    logical, intent(in) :: grain_output_SFC, grain_output_DISM
    logical, intent(in) :: grain_temp_output, grain_SED_output
    character(len=*), dimension(:), intent(in) :: species_id_SFC, species_id_DISM
    real(CDR), dimension(:), intent(in) :: output_age
    real(CDR), intent(in) :: grain_output_min_age, grain_output_max_age
    type(struct_grain_abs), dimension(:), intent(in) :: grain_abs_SFC, &
         grain_abs_DISM
    character(len=*), intent(in) :: run_begin_time
    integer, intent(in) :: dim_SSP
    type(lk_lst_long_string), dimension(:) :: header_SSPs
!#......................................................................
    integer :: output_grain_dim_time, output_grain_dim_region, i_species
!#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

    output_grain_dim_time = count(output_age(:) >= grain_output_min_age &
         .and. output_age(:) <= grain_output_max_age)
    output_grain_dim_region = &
         count((/grain_output_SFC, grain_output_DISM/))

    if (grain_temp_output) then
!# Note: main data are written by subroutine `write_grain_temp` of "mod_dust_emission.f90".
       call write_scenario_param( &
            version_id, &
            version_date, &
            compiler, &
            run_begin_time, &
            dim_SSP, &
            header_SSPs, &
            unit_grain_temp, line_prefix = "!#", for_grains = .true.)

       write(unit_grain_temp, "(i0,tr1,i0)") output_grain_dim_time, &
            output_grain_dim_region

!# Note: grain species and wavelengths written once for star-forming clouds and once for
!# the diffuse ISM, in case grain size distributions and properties depend
!# on the region.

       if (grain_output_SFC) then
          write(unit_grain_temp, "(a,tr1,i0,tr1,l1)") &
               quote_string("SFC"), dim_species_SFC, stoch_heating_SFC
          do i_species = 1, dim_species_SFC
             write(unit_grain_temp, "(a)") quote_string(species_id_SFC(i_species))
          enddo
       endif

       if (grain_output_DISM) then
          write(unit_grain_temp, "(a,tr1,i0,tr1,l1)") &
               quote_string("DISM"), dim_species_DISM, stoch_heating_DISM
          do i_species = 1, dim_species_DISM
             write(unit_grain_temp, "(a)") quote_string(species_id_DISM(i_species))
          enddo
       endif
    endif

    if (grain_SED_output) then
!# Note: main data are written by subroutine `write_grain_SED` of "mod_dust_emission.f90".
       call write_scenario_param( &
            version_id, &
            version_date, &
            compiler, &
            run_begin_time, &
            dim_SSP, &
            header_SSPs, &
            unit_grain_SED, line_prefix = "!#", for_grains = .true.)

       write(unit_grain_SED, "(i0,tr1,i0)") output_grain_dim_time, &
            output_grain_dim_region

!# Note: grain species and wavelengths written once for star-forming clouds and once for
!# the diffuse ISM, in case grain size distributions and properties depend
!# on the region.

       if (grain_output_SFC) then
          write(unit_grain_SED, "(a,tr1,i0,tr1,l1)") &
               quote_string("SFC"), dim_species_SFC, stoch_heating_SFC
          do i_species = 1, dim_species_SFC
             write(unit_grain_SED, "(a,tr1,i0)") &
                  quote_string(species_id_SFC(i_species)), grain_abs_SFC(i_species) % dim_lambda
             write(unit_grain_SED, "(10(es10.4,tr1))") &
                  grain_abs_SFC(i_species) % lambda(1:grain_abs_SFC(i_species) % dim_lambda)
          enddo
       endif

       if (grain_output_DISM) then
          write(unit_grain_SED, "(a,tr1,i0,tr1,l1)") &
               quote_string("DISM"), dim_species_DISM, stoch_heating_DISM
          do i_species = 1, dim_species_DISM
             write(unit_grain_SED, "(a,tr1,i0)") &
                  quote_string(species_id_DISM(i_species)), grain_abs_DISM(i_species) % dim_lambda
             write(unit_grain_SED, "(10(es10.4,tr1))") &
                  grain_abs_DISM(i_species) % lambda(1:grain_abs_DISM(i_species) % dim_lambda)
          enddo
       endif
    endif

  end subroutine write_spectra_grain_preamble

end module mod_write_spectra_output
