!# 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.
!#======================================================================

program spectra

  use mod_types
  use mod_linked_list
  use mod_directories, only : scenarios_dir, yields_dir, spectra_dir, &
       grain_temp_dir, grain_SED_dir, bin_dir, log_spectra_dir
  use mod_constants, only : Y_prim, Z_prim, L_sol, &
       lambda_Lyman_break_um, &
       in_A_to_in_um, &
       per_A_to_per_um, &
       per_um_to_per_A, &
       solar_abundances, &
       spectra_list, grain_temp_list, grain_SED_list
  use mod_spectra_constants, only : &
       dim_temp, T_CBR0, scenarios_list, eps_lambda
  use mod_stel_lib, only : spectra_read_stel_lib1, struct_stel_lib
  use mod_spectra_ages, only : read_spectra_ages
  use mod_nebular, only : nebular_computations
  use mod_RT, only : read_RT_data, struct_RT_sph_sym, struct_RT_cyl_sym
  use mod_grain_properties, only : read_dust, struct_grain_abs
  use mod_read_SSPs, only : read_SSP_Z, read_SSPs_set
  use mod_initialization, only : initialization
  use mod_evolution, only : evolution
  use mod_SFC_DISM, only : compute_stellar_spectrum, &
       compute_nebular_spectrum, SFC_DISM_properties
  use mod_extinction, only : compute_opacity_DISM, compute_opacity_SFC, &
       compute_RF_DISM, compute_RF_SFC, attenuate_DISM
  use mod_compute_various, only : compute_various
  use mod_file_access, only : open_file, close_file, file_name_decomposition, path_file, base_name
  use mod_dir_access, only : dir_sep
  use mod_dust_emission, only : temp_grid, compute_grain_bol_lum, compute_grain_energ, &
       compute_dust_emission, read_Debye, unit_grain_temp, unit_grain_SED, &
       do_output_grain, struct_grain
  use mod_write_scenario_param, only : write_scenario_param, ord_scenario, write_parameter
  use mod_write_spectra_output, only : write_spectra_main_preamble, write_spectra_various, &
       write_spectra_SED, write_spectra_warning, write_spectra_grain_preamble
  use mod_intersperse
  use mod_interp
  use mod_scenario
  use mod_parse_file, only : parse_file, file_list
  use mod_strings, only : quote_string, same_case_equal
  use mod_convert_type
  use mod_read_abundances, only : read_abundances
  use mod_ISO_8601, only : ISO_8601
  use mod_process_file_name, only : process_file_name, process_file_name_aux
  use mod_select_file, only : select_single_file
  use mod_interm_grid, only : interm_grid
  use mod_spectra_alloc, only : spectra_alloc1, spectra_alloc2
  use mod_compiler, only : compiler
  use mod_random, only : dim_seed, seed
  use mod_cosmo, only : fn_cosmic_time, fn_redshift
  implicit none
!#......................................................................
  character(len=25) :: run_begin_time
  character(len=14) :: time_stamp_value
  logical :: time_stamp_used
  integer :: unit_scenarios
  logical :: exist

  character(len=std_string) :: log_file

  integer :: dim_SSP
  character(std_string), dimension(:), pointer :: SSP_file => null()
  real(CDR), dimension(:), pointer :: Z_SSP => null()

  integer :: dim_cont, i_Lyman_break_cont, i_Lyman_break_stel, i_species, i_cont, i_inf, i_sup
  real(CDR), dimension(:), pointer :: lambda_cont => null()
  integer, dimension(:), allocatable :: i_bracket

  type(struct_grain), dimension(:), pointer :: grain_data_SFC => null(), &
       grain_data_DISM => null()

!# `_SFC` = from stars, gas or grains in star-forming clouds.
!# `_DISM` = from stars, gas or grains in the diffuse ISM.
!# `_unatt` = unattenuatted.
!# `_att_gas` = attenuated by gas only, and this in the region considered only.
  real(CDR), dimension(:), pointer :: lum_cont => null(), &
       lum_stel_SFC_att_gas => null(), &
       lum_stel_SFC_unatt => null(), &
       lum_stel_SFC => null(), &
       lum_stel_DISM_att_gas => null(), &
       lum_stel_DISM_unatt => null(), &
       lum_cont_unatt => null(), &
       lum_neb_cont_SFC_unatt => null(), &
       lum_neb_cont_SFC => null(), &
       lum_neb_cont_DISM_unatt => null(), &
       RF_cont_SFC => null(), &
       RF_line_SFC => null(), &
       RF_cont_DISM => null(), &
       RF_line_DISM => null()

  real(CDR) :: M_dust, L_dust, L_dust_SFC, L_dust_DISM, dust_bol_ratio
  real(CDR) :: M_dust_SFC
  real(CDR), dimension(:), pointer :: species_weight_SFC => null(), &
       species_weight_DISM => null()

  integer :: dim_output_age, i_output_age, i_time
  real(CDR), dimension(:), pointer :: output_age => null()
  real(CDR) :: time, time_step

  real(CDR), dimension(:), pointer :: lambda_stel => null() !# Wavelengths of the stellar spectra.
  integer :: dim_SL, dim_lambda_stel !# Number of stellar libraries ; number of wavelengths of the stellar spectra.
  integer, dimension(:), pointer :: dim_spec => null() !# Number of spectra for each stellar library.

  real(CDR), dimension(:,:,:), pointer :: rel_lum_neb_cont => null()
  integer :: dim_line
  real(CDR), dimension(:), pointer :: lambda_line => null()
  real(CDR), dimension(:,:,:), pointer :: rel_L_line => null()
  real(CDR), dimension(:,:), pointer :: rel_lum_other_lines => null()
  character(len=std_string), dimension(:), pointer :: line_id => null()
  real(CDR), dimension(:), pointer :: kappa_abs_cont => null()
  real(CDR), dimension(:), pointer :: kappa_abs_line => null()

  type(lk_lst_long_string), dimension(:), pointer :: &
       header_SSPs => null()

  real(CDR), dimension(:, :), pointer :: L_bol_SSP => null()
  real(CDR), dimension(:, :, :), pointer :: lum_SSP => null()
  real(CDR), dimension(:, :), pointer :: stellar_mass_SSP => null()
  integer, dimension(:), pointer :: inv_time => null()
  real(CDR), dimension(:), pointer :: beta => null()
  real(CDR), dimension(:, :), pointer :: LC_rate_SSP => null()
  real(CDR), dimension(:, :), pointer :: CCSN_rate_SSP => null(), &
       SNIa_rate_SSP => null()
  real(CDR), dimension(:, :), pointer :: ejec_rate_tot_SSP => null()
  real(CDR), dimension(:,:,:), pointer :: ejec_rate_elem_SSP => null()
  real(CDR), dimension(:, :), pointer :: carb_dust_prod_rate_SSP => null(), &
       sil_dust_prod_rate_SSP => null()
  real(CDR), dimension(:, :), pointer :: BHNS_mass_prod_rate_SSP => null(), &
       WD_mass_prod_rate_SSP => null()

  real(CDR), dimension(:), pointer :: lum_stel_SFC_att_gas0 => null(), &
       lum_stel_SFC_unatt0 => null(), &
       lum_stel_DISM_att_gas0 => null(), &
       lum_stel_DISM_unatt0 => null(), &
       lum_stel_SFC0 => null(), &
       lum_neb_cont_SFC_unatt0 => null(), &
       lum_neb_cont_SFC0 => null(), &
       lum_neb_cont_DISM_unatt0 => null()

  real(CDR), dimension(:), pointer :: L_line => null(), &
       L_line_SFC_unatt => null(), &
       L_line_SFC => null(), &
       L_line_DISM_unatt => null(), &
       L_line_unatt => null()

  real(CDR) :: Lyman_cont_rate

  real(CDR), dimension(:), pointer :: galaxy_mass => null(), &
       ISM_mass => null(), &
       BHNS_mass => null(), &
       WD_mass => null(), &
       carb_abund => null(), &
       sil_abund => null()
  real(CDR), dimension(max_dim_reserv) :: reserv_init_mass_scaled
  real(CDR) :: carb_init_mass, sil_init_mass, dust_abund
  logical :: SF_warn_present, outflow_warn_present, reserv_warn_present
  logical, dimension(max_dim_reserv) ::  infall_warn_present
  real(CDR) :: SF_warn_age, outflow_warn_age
  real(CDR), dimension(max_dim_reserv) :: infall_warn_age

  type(irreg_array_CDR), dimension(:), pointer :: RF_DISM => null(), &
       RF_SFC => null()

  real(CDR), dimension(:), pointer :: grain_density_SFC => null(), &
       grain_density_DISM => null(), &
       grain_mean_atomic_mass_SFC => null(), &
       grain_mean_atomic_mass_DISM => null()

  type(struct_grain_abs), dimension(:), pointer :: grain_abs_SFC => null(), &
       grain_abs_DISM => null()

  real(CDR), dimension(:), pointer :: ISM_elem_init_mass => null()
  real(CDR), dimension(:,:), pointer :: ISM_abund => null()
  real(CDR), dimension(:,:), pointer :: reserv_abund => null()

  real(CDR), dimension(:, :), pointer :: SSP_Z_weight => null()
  real(DPR), dimension(:), pointer :: SF_rate => null()
  real(DPR), dimension(:, :), pointer :: SF_live_d_mass => null()
  real(DPR), dimension(:), pointer :: CCSN_rate => null(), &
       SNIa_rate => null(), &
       ejec_rate_tot => null(), &
       infall_rate => null(), &
       outflow_rate => null(), &
       ejec_cumul_mass => null(), &
       SF_live_cumul_mass => null(), &
       infall_cumul_mass => null(), &
       outflow_cumul_mass => null()

  real(CDR), dimension(:), pointer :: inert_mass => null(), &
       ISM_over_H => null()

  real(CDR) :: stel_Z_mass_avrg, stel_age_mass_avrg, live_stars_mass

  real(CDR), dimension(:), pointer :: LC_gas_abs_birth_cloud => null(), &
       LC_dust_abs_birth_cloud => null(), &
       LC_gas_abs_DISM => null(), &
       dust_col_dens_birth_cloud => null(), &
       cloud_frac => null()
  real(CDR) :: Lyman_cont_dust_abs, Lyman_cont_gas_abs
  integer :: unit_spectra

  real(CDR) :: tau_V, stel_Z_bol_avrg, stel_age_bol_avrg, L_bol

  type(struct_RT_cyl_sym) :: slab, disk, bulge
  type(struct_RT_sph_sym) :: King

  character(std_string), dimension(:), pointer :: file_SL => null()
  type(irreg_array_int), dimension(:), pointer :: used_SL_global => null()

  integer :: i_SSP

  real(CDR), dimension(:), pointer :: cont_trans_DISM_tot => null(), &
       cont_trans_DISM_incl => null(), &
       cont_trans_SFC => null()

  real(CDR), dimension(:), pointer :: line_trans_DISM_tot => null(), &
       line_trans_DISM_incl => null(), &
       line_trans_SFC => null()

  real(CDR), dimension(:), pointer :: time_SSP => null()

  integer :: unit

  integer :: dim_Debye
  real(CDR), dimension(:), pointer :: u_Debye => null(), I2_Debye => null(), &
       I3_Debye => null()

  integer :: dim_elem_sol
  real(DPR), dimension(:), pointer :: abund_sol => null()
  character(len=std_string), dimension(:), pointer :: elem_sol => null()
  real(CDR) :: X_sol, Y_sol, Z_sol, dY_over_dZ

  logical, dimension(:), allocatable :: opt_depth_warn_present
  real(CDR), dimension(:), allocatable :: opt_depth_warn_min_lambda, &
       opt_depth_warn_max_lambda

  integer :: dim_species_SFC, dim_species_DISM
  real(CDR), dimension(:, :), pointer :: lum_species_SFC => null(), &
       lum_species_DISM => null()
  real(CDR), dimension(:, :), pointer :: sublim_lum_species_SFC => null(), &
       sublim_lum_species_DISM => null()

  type(struct_stel_lib), dimension(:), allocatable :: stel_lib

  integer :: i_Lyman_alpha

  integer :: dim_convol_time, dim_elem
  character(len=std_string), dimension(:), pointer :: elem_id => null()
  real(CDR), dimension(:), pointer :: convol_time => null()

  character(len=std_string), dimension(:), pointer :: &
       species_id_SFC => null(), &
       species_id_DISM => null()

  real(CDR), dimension(:), pointer :: carb_frac_SFC => null(), &
       carb_frac_DISM => null(), sil_frac_SFC => null(), &
       sil_frac_DISM => null()
  real(CDR), dimension(:), pointer :: sublim_temp_SFC => null(), &
       sublim_temp_DISM => null()

  logical :: first_scenario

  logical :: run, end_of_file
  logical :: end_statement, return_statement, initialize

  character(len=std_string) :: stel_lib_set, stel_lib_set_prev
  character(len=std_string) :: SSPs_set_prev, ages_file_prev
  character(len=std_string) :: grains_file_SFC_prev, grains_file_DISM_prev
  logical :: SSPs_set_changed, stel_lib_set_changed
  logical :: grains_file_SFC_changed, grains_file_DISM_changed
  real(CDR) :: close_bin_frac_prev
  logical :: opened

  integer :: unit_compiler

  real(CDR) :: t_for, redshift, cosmic_time

  real(CDR) :: inner_radius, H_density
  integer :: n_Z_neb, n_Q_neb
  real(CDR), dimension(:), pointer :: Z_neb => null(), Q_neb => null()
  real(CDR), dimension(:,:), pointer :: Z_neb_weight => null(), &
       Q_neb_weight_SFC => null(), Q_neb_weight_DISM => null()
  real(DPR), dimension(:,:), pointer :: rel_volume_neb => null()
  real(CDR), dimension(:,:), pointer :: LC_power_SSP => null()
!#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

!# Create time-stamp:
  call ISO_8601(local_time_extended = run_begin_time, UTC_time_basic = time_stamp_value)
  write(*,"(a,a/)") "Time-stamp: ", quote_string(time_stamp_value)

  call open_file(unit_compiler, path_file(bin_dir, "compil_spectra.txt"))
  read(unit_compiler,*) compiler
  call close_file(unit_compiler)

!# Open the file containing the scenarios:
  write(*, "(a)") "Name of the file of scenarios?"
  call select_single_file(scenarios_dir, scenarios_list, scenarios_file, &
       "file of scenarios", "files of scenarios", back = .true.)
  call open_file(unit_scenarios, path_file(scenarios_dir, scenarios_file))

!# Open log-file:
  log_file = trim(time_stamp_value) // "_" // base_name(scenarios_file, dir_sep)
  call open_file(unit_log, path_file(log_spectra_dir, log_file), &
       status = "replace")

!#

  first_scenario = .true.

  initialize = .true. !# `initialize` /= `first_scenario`.
  end_of_file = .false.
  end_statement = .false.
  return_statement = .false.

  call random_seed(size = dim_seed)
  if (.not.allocated(seed)) allocate(seed(dim_seed))
  call random_seed(get = seed)

  SSPs_set_prev = ""
  stel_lib_set_prev = ""
  grains_file_SFC_prev = ""
  grains_file_DISM_prev = ""
  ages_file_prev = ""
  close_bin_frac_prev = -1

  allocate(file_list)
  file_list % unit = unit_scenarios
  file_list % file_name = path_file(scenarios_dir, scenarios_file)
  file_list % line_number = 0
  file_list % included_file = .false.

!# Loop on scenarios:
  ord_scenario = 0
  do
     if (end_of_file .or. end_statement) exit
     ord_scenario = ord_scenario + 1

     spectra_file = ""
     grain_temp_file = ""
     grain_SED_file = ""
     time_stamp_used = .false.

     if (initialize) then
        call parse_file(file_list = file_list, &
             read_proc = read_spectra_input_param, &
             end_proc = end_proc, &
             run = run, &
             end_of_file = end_of_file, &
             end_statement = end_statement, &
             return_statement = return_statement, &
             init_proc = init_proc)
     else
        call parse_file(file_list = file_list, &
             read_proc = read_spectra_input_param, &
             end_proc = end_proc, &
             run = run, &
             end_of_file = end_of_file, &
             end_statement = end_statement, &
             return_statement = return_statement)
     endif
     initialize = .false.
     new_statement = .false.

     if (.not. run) then
        if (verbosity >= 0) then
           write(*, "(a)") repeat("*",80)
           if (check_only) &
                write(*, "(a,i0,a)") "Checked scenario number ", &
                ord_scenario, ". OK."
        endif
        cycle
     else
        if (verbosity >= 0) then
           write(*, "(a)") repeat("*",80)
           write(*, "(a,i0,a)") "Processing scenario number ", ord_scenario, "."
        endif
     endif

     call random_seed(get = seed)

     orig_spectra_file = spectra_file
     call process_file_name(spectra_file, prefix, spectra_dir, "spectra", &
          time_stamp_value, stamp_time, ord_scenario, verbosity, time_stamp_used, &
          overwrite)
     if (spectra_output /= "none") then
        if (overwrite) then
           call open_file(unit_spectra, &
                path_file(spectra_dir, spectra_file), status = "replace", action = "write")
        else
           call open_file(unit_spectra, &
                path_file(spectra_dir, spectra_file), status = "new", action = "write")
        endif
     endif
!# ??? If `spectra_output == "none"`, the file referred to by `spectra_file`
!# is not created although the variable `spectra_file` has been assigned and is
!# used later.

     orig_grain_temp_file = grain_temp_file
     orig_grain_SED_file = grain_SED_file
     if (grain_temp_output) then
        call process_file_name_aux(grain_temp_file, spectra_file, prefix, grain_temp_dir, "grain_temp", &
             time_stamp_value, stamp_time, ord_scenario, verbosity, time_stamp_used, overwrite)

        if (overwrite) then
           call open_file(unit_grain_temp, &
                path_file(grain_temp_dir, grain_temp_file), &
                status = "replace", action = "write")
        else
           call open_file(unit_grain_temp, &
                path_file(grain_temp_dir, grain_temp_file), &
                status = "new", action = "write")
        endif
     endif

     if (grain_SED_output) then
        call process_file_name_aux(grain_SED_file, spectra_file, prefix, grain_SED_dir, "grain_SED", &
             time_stamp_value, stamp_time, ord_scenario, verbosity, time_stamp_used, overwrite)

        if (overwrite) then
           call open_file(unit_grain_SED, &
                path_file(grain_SED_dir, grain_SED_file), &
                status = "replace", action = "write")
        else
           call open_file(unit_grain_SED, &
                path_file(grain_SED_dir, grain_SED_file), &
                status = "new", action = "write")
        endif
     endif

!#

     if (SSPs_set /= SSPs_set_prev) SSPs_set_changed = .true.

     if (SSPs_set_changed) then
        call read_SSPs_set(SSPs_set, dim_SSP, SSP_file, &
             used_SL_global, stel_lib_set)
     endif

     if (stel_lib_set /= stel_lib_set_prev) stel_lib_set_changed = .true.

     if (stel_lib_set_changed) then
        call spectra_read_stel_lib1(dim_SL, dim_spec, dim_lambda_stel, lambda_stel, &
             file_SL, stel_lib_set)

        call nebular_computations(inner_radius, H_density, &
             n_Z_neb, n_Q_neb, Z_neb, Q_neb, &
             dim_lambda_stel, lambda_stel, rel_lum_neb_cont, &
             dim_line, lambda_line, rel_L_line, line_id, i_Lyman_alpha, &
             i_Lyman_break_stel, Q_neb_weight_DISM, &
             rel_lum_other_lines, rel_volume_neb)

!# Units: wavelengths now in micrometers:
        lambda_stel(:) = lambda_stel(:) * in_A_to_in_um
        lambda_line(:) = lambda_line(:) * in_A_to_in_um
     endif

     if (first_scenario) then
        call read_RT_data(King, slab, disk, bulge)

        call read_Debye(dim_Debye, u_Debye, I2_Debye, I3_Debye)

!# Read solar abundances:
        call read_abundances(path_file(yields_dir, solar_abundances), &
             dim_elem_sol, abund_sol, elem_sol)
        X_sol = sum(abund_sol, mask = elem_sol == "1H" .or. elem_sol == "2H")
        Y_sol = sum(abund_sol, mask = elem_sol == "3He" .or. elem_sol == "4He")
        Z_sol = 1 - X_sol - Y_sol
        dY_over_dZ = (Y_sol - Y_prim)/(Z_sol - Z_prim)
     endif

     if (grains_file_SFC /= grains_file_SFC_prev) &
          grains_file_SFC_changed = .true.
     if (grains_file_DISM /= grains_file_DISM_prev) &
          grains_file_DISM_changed = .true.

     if (grains_file_SFC_changed) then
        call read_dust(grain_abs_SFC, grain_density_SFC, &
             grain_mean_atomic_mass_SFC, &
             grains_file_SFC, dim_species_SFC, species_id_SFC, &
             carb_frac_SFC, sil_frac_SFC, carb_sublim_temp_SFC, &
             sil_sublim_temp_SFC, sublim_temp_SFC)

        if (associated(grain_data_SFC)) then
           do i_species = 1, size(grain_data_SFC)
              if (associated(grain_data_SFC(i_species) % temp)) &
                   deallocate(grain_data_SFC(i_species) % temp)
              if (associated(grain_data_SFC(i_species) % grain_energ0)) &
                   deallocate(grain_data_SFC(i_species) % grain_energ0)
              if (associated(grain_data_SFC(i_species) % bol_lum)) &
                   deallocate(grain_data_SFC(i_species) % bol_lum)
           enddo
           deallocate(grain_data_SFC)
        endif
        allocate(grain_data_SFC(dim_species_SFC))
        do i_species = 1, dim_species_SFC
           grain_data_SFC(i_species) % sublim_temp = sublim_temp_SFC(i_species)
           grain_data_SFC(i_species) % dim_temp = dim_temp
           call temp_grid(dim_temp, grain_data_SFC(i_species) % temp)

           call compute_grain_bol_lum(grain_abs_SFC(i_species) % dim_radius, &
                grain_abs_SFC(i_species) % radius(:), &
                grain_abs_SFC(i_species) % dim_lambda, &
                grain_abs_SFC(i_species) % lambda(:), &
                grain_abs_SFC(i_species) % Q_abs(:,:), &
                grain_data_SFC(i_species) % temp, &
                grain_data_SFC(i_species) % bol_lum)

           call compute_grain_energ(species_id_SFC(i_species), &
                grain_data_SFC(i_species) % temp, &
                grain_data_SFC(i_species) % grain_energ0, &
                dim_Debye, u_Debye, I2_Debye, &
                I3_Debye)
        enddo
     endif

     if (grains_file_DISM_changed) then
        call read_dust(grain_abs_DISM, grain_density_DISM, &
             grain_mean_atomic_mass_DISM, &
             grains_file_DISM, dim_species_DISM, species_id_DISM, &
             carb_frac_DISM, sil_frac_DISM, carb_sublim_temp_DISM, &
             sil_sublim_temp_DISM, sublim_temp_DISM)

        if (associated(grain_data_DISM)) then
           do i_species = 1, size(grain_data_DISM)
              if (associated(grain_data_DISM(i_species) % temp)) &
                   deallocate(grain_data_DISM(i_species) % temp)
              if (associated(grain_data_DISM(i_species) % grain_energ0)) &
                   deallocate(grain_data_DISM(i_species) % grain_energ0)
              if (associated(grain_data_DISM(i_species) % bol_lum)) &
                   deallocate(grain_data_DISM(i_species) % bol_lum)
           enddo
           deallocate(grain_data_DISM)
        endif
        allocate(grain_data_DISM(dim_species_DISM))
        do i_species = 1, dim_species_DISM
           grain_data_DISM(i_species) % sublim_temp = sublim_temp_DISM(i_species)
           grain_data_DISM(i_species) % dim_temp = dim_temp
           call temp_grid(dim_temp, grain_data_DISM(i_species) % temp)

           call compute_grain_bol_lum(grain_abs_DISM(i_species) % dim_radius, &
                grain_abs_DISM(i_species) % radius(:), &
                grain_abs_DISM(i_species) % dim_lambda, &
                grain_abs_DISM(i_species) % lambda(:), &
                grain_abs_DISM(i_species) % Q_abs(:,:), &
                grain_data_DISM(i_species) % temp, &
                grain_data_DISM(i_species) % bol_lum)

           call compute_grain_energ(species_id_DISM(i_species), &
                grain_data_DISM(i_species) % temp, &
                grain_data_DISM(i_species) % grain_energ0, &
                dim_Debye, u_Debye, I2_Debye, &
                I3_Debye)
        enddo
     endif

     if (stel_lib_set_changed .or. grains_file_SFC_changed &
          .or. grains_file_DISM_changed) then
        dim_cont = dim_lambda_stel
        if (associated(lambda_cont)) deallocate(lambda_cont)
        allocate(lambda_cont(dim_cont))
        lambda_cont(1:dim_cont) = lambda_stel(1:dim_lambda_stel)

!# Units: `lambda_cont` and `grain_abs % lambda` are in micrometers.
        do i_species = 1, dim_species_SFC !# Redundancy if the same grid of wavelengths \
!#                             is used for various grains.

           call intersperse(dim_cont, grain_abs_SFC(i_species) % dim_lambda, lambda_cont, &
                grain_abs_SFC(i_species) % lambda(:), eps = eps_lambda)
        enddo

        do i_species = 1, dim_species_DISM !# Redundancy if the same grid of wavelengths \
!#                             is used for various grains.

           call intersperse(dim_cont, grain_abs_DISM(i_species) % dim_lambda, lambda_cont, &
                grain_abs_DISM(i_species) % lambda(:), eps = eps_lambda)
        enddo

        if (allocated(i_bracket)) deallocate(i_bracket)
        allocate(i_bracket(dim_cont))
        do i_cont = 1, dim_cont
           if (i_cont == 1) then
              i_bracket(1) = 0
           else
              i_bracket(i_cont) = i_bracket(i_cont-1)
           endif
           call bracket(dim_lambda_stel, lambda_stel, lambda_cont(i_cont), i_bracket(i_cont))
!# Do not interpolate through the Lyman break. Extrapolate instead.
           if (lambda_stel(i_bracket(i_cont)) <= lambda_Lyman_break_um .and. &
                lambda_stel(i_bracket(i_cont)+1) >= lambda_Lyman_break_um) then
              if (lambda_cont(i_cont) < lambda_Lyman_break_um) then
                 i_bracket(i_cont) = i_bracket(i_cont)-1
              else
                 i_bracket(i_cont) = i_bracket(i_cont)+1
              endif
           endif
        enddo

!# Find the indices of the continuum wavelengths bracketing the Lyman break:
        i_Lyman_break_cont = 0 !# ??? Or `1`?
        do while (lambda_cont(i_Lyman_break_cont+1) < lambda_Lyman_break_um)
           i_Lyman_break_cont = i_Lyman_break_cont+1
        enddo

        call spectra_alloc1(dim_cont = dim_cont, dim_lambda_stel = dim_lambda_stel, &
             dim_line = dim_line, dim_species_SFC = dim_species_SFC, &
             dim_species_DISM = dim_species_DISM, lum_cont = lum_cont, &
             lum_stel_SFC_att_gas = lum_stel_SFC_att_gas, &
             lum_stel_SFC_unatt = lum_stel_SFC_unatt, lum_stel_SFC = lum_stel_SFC, &
             lum_stel_DISM_att_gas = lum_stel_DISM_att_gas, &
             lum_stel_DISM_unatt = lum_stel_DISM_unatt, &
             lum_cont_unatt = lum_cont_unatt, &
             lum_neb_cont_SFC_unatt = lum_neb_cont_SFC_unatt, &
             lum_neb_cont_SFC = lum_neb_cont_SFC, lum_neb_cont_DISM_unatt = lum_neb_cont_DISM_unatt, &
             kappa_abs_cont = kappa_abs_cont, cont_trans_DISM_tot = cont_trans_DISM_tot, &
             cont_trans_DISM_incl = cont_trans_DISM_incl, &
             cont_trans_SFC = cont_trans_SFC, lum_species_SFC = lum_species_SFC, &
             lum_species_DISM = lum_species_DISM, &
             sublim_lum_species_SFC = sublim_lum_species_SFC, &
             sublim_lum_species_DISM = sublim_lum_species_DISM, &
             kappa_abs_line = kappa_abs_line, L_line = L_line, &
             L_line_SFC_unatt = L_line_SFC_unatt, L_line_SFC = L_line_SFC, &
             L_line_DISM_unatt = L_line_DISM_unatt, &
             L_line_unatt = L_line_unatt, &
             line_trans_DISM_tot = line_trans_DISM_tot, &
             line_trans_DISM_incl = line_trans_DISM_incl, &
             line_trans_SFC = line_trans_SFC, &
             RF_cont_SFC = RF_cont_SFC, RF_line_SFC = RF_line_SFC, &
             RF_cont_DISM = RF_cont_DISM, RF_line_DISM = RF_line_DISM, &
             lum_stel_SFC_att_gas0 = lum_stel_SFC_att_gas0, &
             lum_stel_SFC_unatt0 = lum_stel_SFC_unatt0, &
             lum_stel_DISM_att_gas0 = lum_stel_DISM_att_gas0, &
             lum_stel_DISM_unatt0 = lum_stel_DISM_unatt0, &
             lum_stel_SFC0 = lum_stel_SFC0, &
             lum_neb_cont_SFC_unatt0 = lum_neb_cont_SFC_unatt0, &
             lum_neb_cont_SFC0 = lum_neb_cont_SFC0, &
             lum_neb_cont_DISM_unatt0 = lum_neb_cont_DISM_unatt0, &
             species_weight_SFC = species_weight_SFC, &
             species_weight_DISM = species_weight_DISM)
     endif

!# Read output ages if needed:
     if (ages_file /= ages_file_prev) then
        call read_spectra_ages(ages_file, dim_output_age, output_age)
        if (allocated(opt_depth_warn_present)) deallocate(opt_depth_warn_present)
        allocate(opt_depth_warn_present(dim_output_age))
        if (allocated(opt_depth_warn_min_lambda)) &
             deallocate(opt_depth_warn_min_lambda)
        allocate(opt_depth_warn_min_lambda(dim_output_age))
        if (allocated(opt_depth_warn_max_lambda)) &
             deallocate(opt_depth_warn_max_lambda)
        allocate(opt_depth_warn_max_lambda(dim_output_age))
     endif
     opt_depth_warn_present(:) = .false.
     opt_depth_warn_min_lambda(:) = lambda_cont(dim_cont)
     opt_depth_warn_max_lambda(:) = lambda_cont(1)

     if (SSPs_set_changed .or. close_bin_frac /= close_bin_frac_prev) then
        allocate(stel_lib(dim_SL))
        do i_SSP = 1, dim_SSP
           if (verbosity >= 1) write(*, "(a,i0,a,i0)") "SSP ", i_SSP, "/", dim_SSP
           call read_SSP_Z(time_step, dim_output_age, output_age, &
                dim_convol_time, convol_time, &
                dim_SSP, i_SSP, SSP_file, Z_SSP, close_bin_frac, &
                maxval(dim_spec), &
                dim_lambda_stel, &
                L_bol_SSP, lum_SSP, stellar_mass_SSP, inv_time, beta, &
                LC_rate_SSP, &
                ejec_rate_tot_SSP, ejec_rate_elem_SSP, &
                carb_dust_prod_rate_SSP, sil_dust_prod_rate_SSP, &
                BHNS_mass_prod_rate_SSP, WD_mass_prod_rate_SSP, &
                file_SL, used_SL_global, dim_SL, &
                time_SSP, stel_lib, &
                CCSN_rate_SSP, SNIa_rate_SSP, &
                dim_elem, elem_id, header_SSPs, LC_power_SSP)
        enddo
        if (verbosity >= 1) write(*, "(a/)") "Done."
        deallocate(stel_lib)
     endif

     call spectra_alloc2(dim_convol_time, dim_elem, &
          galaxy_mass, ISM_mass, BHNS_mass, &
          WD_mass, carb_abund, sil_abund, 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, &
          inert_mass, ISM_over_H, &
          LC_gas_abs_birth_cloud, LC_dust_abs_birth_cloud, &
          LC_gas_abs_DISM, dust_col_dens_birth_cloud, &
          cloud_frac, ISM_elem_init_mass, ISM_abund, reserv_abund, &
          n_Z_neb, n_Q_neb, Z_neb_weight, Q_neb_weight_SFC)

!# Initialization:
     call initialization(reserv_abund, &
          ISM_abund, galaxy_mass(1), reserv_init_mass_scaled, ISM_mass(1), &
          BHNS_mass(1), WD_mass(1), inert_mass(1), carb_abund(1), &
          sil_abund(1), ISM_elem_init_mass, carb_init_mass, sil_init_mass, &
          abund_sol, elem_sol, Z_sol, dY_over_dZ, SF_warn_present, infall_warn_present, &
          outflow_warn_present, reserv_warn_present)

!# Convolution of the SSPs with the star formation rate:
     call evolution(dim_convol_time, convol_time, time_step, Z_SSP, dim_SSP, &
          ejec_rate_tot_SSP, ejec_rate_elem_SSP, &
          carb_dust_prod_rate_SSP, sil_dust_prod_rate_SSP, &
          WD_mass_prod_rate_SSP, BHNS_mass_prod_rate_SSP, ISM_mass, &
          carb_init_mass, sil_init_mass, &
          ISM_elem_init_mass, carb_abund, sil_abund, &
          inert_mass, galaxy_mass, BHNS_mass, WD_mass, &
          ISM_abund, &
          SF_live_d_mass, &
          SSP_Z_weight, &
          reserv_init_mass_scaled, &
          reserv_abund, &
          time_SSP, CCSN_rate_SSP, SNIa_rate_SSP, &
          CCSN_rate, SNIa_rate, ejec_rate_tot, SF_rate, infall_rate, &
          outflow_rate, &
          ejec_cumul_mass, SF_live_cumul_mass, infall_cumul_mass, &
          outflow_cumul_mass, SF_warn_present, SF_warn_age, &
          infall_warn_present, infall_warn_age, &
          outflow_warn_present, outflow_warn_age, dim_elem, ISM_over_H, &
          Z_neb, Z_neb_weight)
!# (Mixing of galactic outflows with the reservoir is not implemented.
!# `reserv_abund` should change if such a mixing occurs.)

     call 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)

     call 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)

!# Compute and output SEDs:
     t_for = fn_cosmic_time(form_redshift)

     do i_output_age = 1, dim_output_age
        if (verbosity >= 1) write(*, "(a,i0,a,i0)") "Age number ", &
             i_output_age, "/", dim_output_age
        time = output_age(i_output_age)

        cosmic_time = time + t_for
        redshift = fn_redshift(cosmic_time)

        if (verbosity >= 1) write(*, "(a,f8.2,a)") "Output time = ", time, " Myr"
        i_time = nint(time/time_step) + 1 !# Index of the nearest value of \
!#                                           `convol_time` to `output_age`.

        dust_abund = carb_abund(i_time) + sil_abund(i_time)
        if (dust_abund > 0) then
           species_weight_SFC(:) = (carb_frac_SFC(:)*carb_abund(i_time) + &
                sil_frac_SFC(:) * sil_abund(i_time))/ &
                dust_abund
           species_weight_DISM(:) = (carb_frac_DISM(:)*carb_abund(i_time) + &
                sil_frac_DISM(:) * sil_abund(i_time))/ &
                dust_abund
        else
           species_weight_SFC(:) = 0
           species_weight_DISM(:) = 0
        endif

        call SFC_DISM_properties(dim_species_SFC, grain_abs_SFC, LC_rate_SSP, &
             SF_live_d_mass, dust_abund, &
             species_weight_SFC, &
             LC_gas_abs_birth_cloud, LC_dust_abs_birth_cloud, &
             LC_gas_abs_DISM, &
             Lyman_cont_rate, Lyman_cont_dust_abs, Lyman_cont_gas_abs, &
             dust_col_dens_birth_cloud, cloud_frac, &
             M_dust_SFC, &
             ISM_over_H(i_time), i_time, convol_time, &
             n_Q_neb, Q_neb, Q_neb_weight_SFC, inner_radius, H_density, &
             n_Z_neb, Z_neb_weight, rel_volume_neb)
!# (Note: the composition of the ISM in the cloud is the same as in the diffuse
!# medium.)

        call compute_stellar_spectrum(convol_time = convol_time, &
             i_time = i_time, &
             dim_lambda_stel = dim_lambda_stel, &
             beta = beta, &
             inv_time = inv_time, &
             lum_SSP = lum_SSP, &
             SF_live_d_mass = SF_live_d_mass, &
             lum_stel_SFC_att_gas0 = lum_stel_SFC_att_gas0, &
             lum_stel_SFC_unatt0 = lum_stel_SFC_unatt0, &
             lum_stel_DISM_att_gas0 = lum_stel_DISM_att_gas0, &
             lum_stel_DISM_unatt0 = lum_stel_DISM_unatt0, &
             lum_stel_SFC0 = lum_stel_SFC0, &
             i_Lyman_break_stel = i_Lyman_break_stel, &
             LC_gas_abs_birth_cloud = LC_gas_abs_birth_cloud, &
             LC_gas_abs_DISM = LC_gas_abs_DISM, &
             dust_col_dens_birth_cloud = dust_col_dens_birth_cloud, &
             cloud_frac = cloud_frac, &
             dim_species = dim_species_SFC, &
             grain_abs = grain_abs_SFC, &
             species_weight = species_weight_SFC, &
             lambda_stel = lambda_stel)

        call compute_nebular_spectrum(convol_time = convol_time, &
             i_time = i_time, &
             dim_line = dim_line, &
             rel_L_line = rel_L_line, &
             dim_lambda_stel = dim_lambda_stel, &
             rel_lum_neb_cont = rel_lum_neb_cont, &
             SF_live_d_mass = SF_live_d_mass, &
             LC_gas_abs_birth_cloud = LC_gas_abs_birth_cloud, &
             LC_gas_abs_DISM = LC_gas_abs_DISM, &
             cloud_frac = cloud_frac, &
             i_Lyman_alpha = i_Lyman_alpha, &
             lum_neb_cont_SFC_unatt0 = lum_neb_cont_SFC_unatt0, &
             lum_neb_cont_SFC0 = lum_neb_cont_SFC0, &
             lum_neb_cont_DISM_unatt0 = lum_neb_cont_DISM_unatt0, &
             L_line_SFC_unatt = L_line_SFC_unatt, &
             L_line_SFC = L_line_SFC, &
             L_line_DISM_unatt = L_line_DISM_unatt, &
             M_dust_SFC = M_dust_SFC, &
             LC_power_SSP = LC_power_SSP, &
             n_Z_neb = n_Z_neb, n_Q_neb = n_Q_neb, &
             Z_neb_weight = Z_neb_weight, &
             Q_neb_weight_SFC = Q_neb_weight_SFC, &
             Q_neb_weight_DISM = Q_neb_weight_DISM)

!# Units: `lum_stel*0`, `lum_neb_cont*0` in erg.s-1.angstroem-1.L_sol-1.M_sys-1;
!# `L_line*` in erg.s-1.L_sol-1.M_sys-1.

!# Rebin to wavelengths `lambda_cont`:
        do i_cont = 1, dim_cont
           if (lambda_cont(i_cont) < lambda_stel(1)) then
!# No extrapolation of stellar spectra at short wavelengths.
              lum_stel_SFC_att_gas(i_cont) = 0
              lum_stel_SFC_unatt(i_cont) = 0
              lum_stel_SFC(i_cont) = 0
              lum_stel_DISM_att_gas(i_cont) = 0
              lum_stel_DISM_unatt(i_cont) = 0
              lum_neb_cont_SFC_unatt(i_cont) = 0
              lum_neb_cont_SFC(i_cont) = 0
              lum_neb_cont_DISM_unatt(i_cont) = 0
           else
              i_inf = i_bracket(i_cont)
              i_sup = i_inf+1
              lum_stel_SFC_att_gas(i_cont) = interp_log_log(lambda_stel(i_inf), &
                   lambda_stel(i_sup), lum_stel_SFC_att_gas0(i_inf), &
                   lum_stel_SFC_att_gas0(i_sup), lambda_cont(i_cont))
              lum_stel_SFC_unatt(i_cont) = interp_log_log(lambda_stel(i_inf), &
                   lambda_stel(i_sup), lum_stel_SFC_unatt0(i_inf), &
                   lum_stel_SFC_unatt0(i_sup), lambda_cont(i_cont))
              lum_stel_SFC(i_cont) = interp_log_log(lambda_stel(i_inf), &
                   lambda_stel(i_sup), lum_stel_SFC0(i_inf), &
                   lum_stel_SFC0(i_sup), lambda_cont(i_cont))
              lum_stel_DISM_att_gas(i_cont) = interp_log_log(lambda_stel(i_inf), &
                   lambda_stel(i_sup), lum_stel_DISM_att_gas0(i_inf), &
                   lum_stel_DISM_att_gas0(i_sup), lambda_cont(i_cont))
              lum_stel_DISM_unatt(i_cont) = interp_log_log(lambda_stel(i_inf), &
                   lambda_stel(i_sup), lum_stel_DISM_unatt0(i_inf), &
                   lum_stel_DISM_unatt0(i_sup), lambda_cont(i_cont))
              lum_neb_cont_SFC_unatt(i_cont) = interp_log_log(lambda_stel(i_inf), &
                   lambda_stel(i_sup), &
                   lum_neb_cont_SFC_unatt0(i_inf), lum_neb_cont_SFC_unatt0(i_sup), &
                   lambda_cont(i_cont))
              lum_neb_cont_SFC(i_cont) = interp_log_log(lambda_stel(i_inf), &
                   lambda_stel(i_sup), &
                   lum_neb_cont_SFC0(i_inf), lum_neb_cont_SFC0(i_sup), &
                   lambda_cont(i_cont))
              lum_neb_cont_DISM_unatt(i_cont) = interp_log_log(lambda_stel(i_inf), &
                   lambda_stel(i_sup), &
                   lum_neb_cont_DISM_unatt0(i_inf), lum_neb_cont_DISM_unatt0(i_sup), &
                   lambda_cont(i_cont))
!# Units: `lum_stel*`, `lum_neb_cont*` in erg.s-1.angstroem-1.L_sol-1.M_sys-1.

!# Following lines may be needed because the SEDs are extrapolated when `lambda_cont(i_cont) > lambda_stel(i_sup)`:
              lum_stel_SFC_att_gas(i_cont) = min(lum_stel_SFC_att_gas(i_cont), lum_stel_SFC_unatt(i_cont))
              lum_stel_SFC(i_cont) = min(lum_stel_SFC(i_cont), lum_stel_SFC_att_gas(i_cont))
              lum_neb_cont_SFC(i_cont) = min(lum_neb_cont_SFC(i_cont), lum_neb_cont_SFC_unatt(i_cont))
              lum_stel_DISM_att_gas(i_cont) = min(lum_stel_DISM_att_gas(i_cont), lum_stel_DISM_unatt(i_cont))
           endif
        enddo

!# Light emerging from star-forming clouds (if `_unatt`, attenuated only by gas in clouds;
!# otherwise, by gas and dust in clouds):
        lum_cont_unatt(:) = lum_stel_SFC_att_gas(:) + lum_neb_cont_SFC_unatt(:)
        lum_cont(:) = lum_stel_SFC(:) + lum_neb_cont_SFC(:)
        L_line_unatt(:) = L_line_SFC_unatt(:)
        L_line(:) = L_line_SFC(:)
!# (`lum_cont_unatt` <=> `lum_cont_SFC_att_gas`;
!# `lum_neb_cont_SFC_unatt` <=> `lum_neb_cont_SFC_att_gas`;
!# `lum_cont` <=> `lum_cont_SFC`;
!# `L_line_unatt` <=> `L_line_SFC_att_gas`.
!# Units: `lum_cont*` in erg.s-1.angstroem-1.L_sol-1.M_sys-1;
!# `L_line*` in erg.s-1.L_sol-1.M_sys-1.)


!# What about Lyman continuum photons emitted by the HII gas shortward
!# of the Lyman break???
!# Should they be reabsorbed? Here, we assumed they are not...

        tau_V = 0
        M_dust = 0
        L_dust = 0

!# Units: `lum_cont` and `lum_cont_unatt` now in erg.s-1.micrometer-1.M_sys-1:
        lum_cont(:) = lum_cont(:)*L_sol*per_A_to_per_um
        lum_cont_unatt(:) = lum_cont_unatt(:)*L_sol*per_A_to_per_um
!# Units: `L_line` and `L_line_unatt` now in erg.s-1.M_sys-1:
        L_line(:) = L_line(:)*L_sol
        L_line_unatt(:) = L_line_unatt(:)*L_sol

!# Compute dust opacity in star-forming clouds:
        call compute_opacity_SFC(dim_cont, lambda_cont, dim_line, lambda_line, &
             M_dust_SFC, carb_abund(i_time), sil_abund(i_time), &
             dim_species_SFC, grain_abs_SFC, species_weight_SFC, &
             kappa_abs_cont, kappa_abs_line, &
             lum_cont, lum_cont_unatt, L_line, L_line_unatt, &
             cont_trans_SFC, line_trans_SFC)
!# (The extinction by clouds has actually already been computed in subroutine
!# `compute_stellar_spectrum` of "mod_SFC_DISM.f90".)

!# Mean radiation field in star-forming clouds:
        if (CBR) then !# Add cosmic background radiation to the radiation field.
           call compute_RF_SFC(dim_cont, dim_line, dim_species_SFC, &
                grain_abs_SFC, &
                kappa_abs_cont, kappa_abs_line, &
                lambda_cont, lambda_line, M_dust_SFC, &
                lum_cont, lum_cont_unatt, &
                L_line, L_line_unatt, L_dust_SFC, &
                RF_SFC, RF_cont_SFC, RF_line_SFC, T_CBR0*(1+redshift))
        else
           call compute_RF_SFC(dim_cont, dim_line, dim_species_SFC, &
                grain_abs_SFC, &
                kappa_abs_cont, kappa_abs_line, &
                lambda_cont, lambda_line, M_dust_SFC, &
                lum_cont, lum_cont_unatt, &
                L_line, L_line_unatt, L_dust_SFC, &
                RF_SFC, RF_cont_SFC, RF_line_SFC)
        endif

!# Dust emission in star-forming clouds:
        if (dust_emission_SFC) then
           if (grain_output_SFC .and. time >= grain_output_min_age .and. &
                time <= grain_output_max_age) then
              do_output_grain = .true.
              if (grain_temp_output) &
                   write(unit_grain_temp, "(es10.4,tr1,a)") time, "!# Time."
              if (grain_SED_output) &
                   write(unit_grain_SED, "(es10.4,tr1,a)") time, "!# Time."
           else
              do_output_grain = .false.
           endif

           call compute_dust_emission(carb_abund(i_time), sil_abund(i_time), &
                dim_cont, lambda_cont, &
                grain_data_SFC, M_dust_SFC, &
                RF_SFC, lum_species_SFC, &
                dim_species_SFC, species_id_SFC, grain_density_SFC, grain_mean_atomic_mass_SFC, &
                species_weight_SFC, grain_abs_SFC, stoch_heating_SFC, &
                self_abs_power_SFC, cont_trans_SFC, cont_trans_SFC, &
                sublim_lum_species_SFC)
        else !# `dust_extinction_SFC = .false.`.
           lum_species_SFC(:,:) = 0
           sublim_lum_species_SFC(:,:) = 0
        endif

!# Add grain emission from star-forming clouds to `lum_cont`:
        lum_cont(1:dim_cont) = lum_cont(1:dim_cont) + &
             sum(lum_species_SFC(1:dim_species_SFC,1:dim_cont), dim = 1)

!# Add emission from stars and gas in the diffuse medium to `lum_cont` and `L_line`:
        lum_cont(:) = lum_cont(:) &
             + (lum_stel_DISM_att_gas(:) + lum_neb_cont_DISM_unatt(:))*L_sol*per_A_to_per_um
        L_line(:) = L_line(:) + L_line_DISM_unatt(:)*L_sol
!# (`lum_neb_cont_DISM_unatt` <=> `lum_neb_cont_DISM_att_gas`;
!# `L_line_DISM_unatt` <=> `L_line_DISM_att_gas`.
!# The (rare) Lyman continuum photons emitted by grains are neglected:
!# they are not absorbed by gas and do not contribute to nebular emission.)

!# Compute attenuation factors by dust in the diffuse medium:
        call compute_opacity_DISM(ISM_mass(i_time), dim_cont, &
             lambda_cont, dim_line, lambda_line, i_Lyman_alpha, M_dust, &
             carb_abund(i_time), sil_abund(i_time), &
             dim_species_DISM, grain_abs_DISM, &
             species_weight_DISM, &
             tau_V, King, slab, disk, bulge, &
             cont_trans_DISM_tot, cont_trans_DISM_incl, &
             line_trans_DISM_tot, line_trans_DISM_incl, &
             opt_depth_warn_present(i_output_age), &
             opt_depth_warn_min_lambda(i_output_age), &
             opt_depth_warn_max_lambda(i_output_age), &
             kappa_abs_cont, kappa_abs_line, ISM_over_H(i_time))

!# Compute radiation field in the diffuse medium:
        if (CBR) then !# Add cosmic background radiation to the radiation field.
           call compute_RF_DISM(dim_cont, dim_line, dim_species_DISM, &
                grain_abs_DISM, &
                kappa_abs_cont, kappa_abs_line, &
                lambda_cont, lambda_line, L_dust_SFC, M_dust, &
                lum_cont, L_line, cont_trans_DISM_tot, line_trans_DISM_tot, &
                L_dust_DISM, L_dust, RF_DISM, RF_cont_DISM, RF_line_DISM, &
                T_CBR0*(1+redshift))
        else
           call compute_RF_DISM(dim_cont, dim_line, dim_species_DISM, &
                grain_abs_DISM, &
                kappa_abs_cont, kappa_abs_line, &
                lambda_cont, lambda_line, L_dust_SFC, M_dust, &
                lum_cont, L_line, cont_trans_DISM_tot, line_trans_DISM_tot, &
                L_dust_DISM, L_dust, RF_DISM, RF_cont_DISM, RF_line_DISM)
        endif

!# Attenuation by dust in the diffuse medium:
        call attenuate_DISM( &
             lum_cont = lum_cont, &
             L_line = L_line, &
             cont_trans_DISM_incl = cont_trans_DISM_incl, &
             line_trans_DISM_incl = line_trans_DISM_incl, &
             lambda_cont = lambda_cont, &
             Lyman_cont_dust_abs = Lyman_cont_dust_abs, &
             cont_trans_DISM_tot = cont_trans_DISM_tot, &
             i_Lyman_break_cont = i_Lyman_break_cont)

!# Emission from dust in the diffuse medium:
        if (dust_emission_DISM) then
           if (grain_output_DISM .and. time >= grain_output_min_age .and. &
                time <= grain_output_max_age) then
              do_output_grain = .true.
              if (grain_temp_output) &
                   write(unit_grain_temp, "(es10.4,tr1,a)") time, "!# Time."
              if (grain_SED_output) &
                   write(unit_grain_SED, "(es10.4,tr1,a)") time, "!# Time."
           else
              do_output_grain = .false.
           endif

           call compute_dust_emission(carb_abund(i_time), sil_abund(i_time), &
                dim_cont, lambda_cont, &
                grain_data_DISM, M_dust, &
                RF_DISM, lum_species_DISM, &
                dim_species_DISM, species_id_DISM, &
                grain_density_DISM, grain_mean_atomic_mass_DISM, &
                species_weight_DISM, grain_abs_DISM, &
                stoch_heating_DISM, &
                self_abs_power_DISM, cont_trans_DISM_tot, cont_trans_DISM_incl, &
                sublim_lum_species_DISM)
        else
           lum_species_DISM(:,:) = 0
           sublim_lum_species_DISM(:,:) = 0
        endif

!# Add grain emission from the diffuse medium to `lum_cont`:
        do i_cont = 1, dim_cont
           lum_cont(i_cont) = lum_cont(i_cont) + &
                sum(lum_species_DISM(1:dim_species_DISM,i_cont))
        enddo

!# Convert back `lum_cont` to erg.s-1.angstroem-1.M_sys-1:
        lum_cont(:) = lum_cont(:) * per_um_to_per_A

        call compute_various(i_time, convol_time, beta, inv_time, L_bol_SSP, &
             SF_live_d_mass, SSP_Z_weight, L_dust, L_bol, dust_bol_ratio, &
             stel_Z_bol_avrg, stel_age_bol_avrg, &
             stellar_mass_SSP, live_stars_mass, &
             stel_Z_mass_avrg, stel_age_mass_avrg, &
             Lyman_cont_rate, Lyman_cont_gas_abs, Lyman_cont_dust_abs)

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

        call 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)
     end do

!# Warnings:
     call 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)

!# Close opened files:

     if (unit_spectra >= 0) then
        inquire(unit_spectra, opened = opened)
        if (opened) call close_file(unit_spectra)
     endif

     if (grain_temp_output) then
        inquire(unit_grain_temp, opened = opened)
        if (opened) call close_file(unit_grain_temp)
     endif

     if (grain_SED_output) then
        inquire(unit_grain_SED, opened = opened)
        if (opened) call close_file(unit_grain_SED)
     endif

!# Add the names of output files to the lists of output files of "spectra":

     inquire(file = path_file(spectra_dir, spectra_file), exist = exist)
     if (exist) then
        call open_file(unit, path_file(spectra_dir, spectra_list), &
             status = "old", position = "append", action = "write")
        write(unit, "(a)") trim(spectra_file)
        call close_file(unit)
     endif

     inquire(file = path_file(grain_temp_dir, grain_temp_file), exist = exist)
     if (exist) then
        call open_file(unit, path_file(grain_temp_dir, grain_temp_list), &
             status = "old", position = "append", action = "write")
        write(unit, "(a)") trim(grain_temp_file)
        call close_file(unit)
     endif

     inquire(file = path_file(grain_SED_dir, grain_SED_file), exist = exist)
     if (exist) then
        call open_file(unit, path_file(grain_SED_dir, grain_SED_list), &
             status = "old", position = "append", action = "write")
        write(unit, "(a)") trim(grain_SED_file)
        call close_file(unit)
     endif

!# Prepare things for the next scenario:

     first_scenario = .false.
     SSPs_set_prev = SSPs_set
     SSPs_set_changed = .false.
     stel_lib_set_prev = stel_lib_set
     stel_lib_set_changed = .false.
     grains_file_SFC_prev = grains_file_SFC
     grains_file_SFC_changed = .false.
     grains_file_DISM_prev = grains_file_DISM
     grains_file_DISM_changed = .false.
     ages_file_prev = ages_file
     close_bin_frac_prev = close_bin_frac
  end do !# End loop on scenarios.

  call close_file(unit_scenarios)
  call close_file(unit_log)

end program spectra
