LCOV - code coverage report
Current view: top level - colors/private - synthetic.f90 (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 96 0
Test Date: 2025-10-14 06:41:40 Functions: 0.0 % 4 0

            Line data    Source code
       1              : ! ***********************************************************************
       2              : !
       3              : !   Copyright (C) 2025  Niall Miller & The MESA Team
       4              : !
       5              : !   This program is free software: you can redistribute it and/or modify
       6              : !   it under the terms of the GNU Lesser General Public License
       7              : !   as published by the Free Software Foundation,
       8              : !   either version 3 of the License, or (at your option) any later version.
       9              : !
      10              : !   This program is distributed in the hope that it will be useful,
      11              : !   but WITHOUT ANY WARRANTY; without even the implied warranty of
      12              : !   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
      13              : !   See the GNU Lesser General Public License for more details.
      14              : !
      15              : !   You should have received a copy of the GNU Lesser General Public License
      16              : !   along with this program. If not, see <https://www.gnu.org/licenses/>.
      17              : !
      18              : ! ***********************************************************************
      19              : 
      20              : module synthetic
      21              : 
      22              :    use const_def, only: dp
      23              :    use utils_lib, only: mkdir, folder_exists
      24              :    use colors_utils, only: remove_dat, romberg_integration, load_filter, load_vega_sed, load_lookup_table
      25              :    use knn_interp, only: interpolate_array
      26              : 
      27              :    implicit none
      28              : 
      29              :    private
      30              :    public :: calculate_synthetic
      31              : 
      32              : contains
      33              : 
      34              :    !****************************
      35              :    ! Calculate Synthetic Photometry Using SED and Filter
      36              :    !****************************
      37            0 :    real(dp) function calculate_synthetic(temperature, gravity, metallicity, ierr, &
      38            0 :                                          wavelengths, fluxes, filter_wavelengths, &
      39              :                                          filter_trans, &
      40              :                                          filter_filepath, vega_filepath, &
      41              :                                          filter_name, make_sed, colors_results_directory)
      42              :       ! Input arguments
      43              :       real(dp), intent(in) :: temperature, gravity, metallicity
      44              :       character(len=*), intent(in) :: filter_filepath, filter_name, vega_filepath, colors_results_directory
      45              :       integer, intent(out) :: ierr
      46              :       character(len=1000) :: line
      47              : 
      48              :       real(dp), dimension(:), intent(inout) :: wavelengths, fluxes
      49              :       real(dp), dimension(:), allocatable, intent(inout) :: filter_wavelengths, filter_trans
      50              :       logical, intent(in) :: make_sed
      51              : 
      52              :       ! Local variables
      53            0 :       real(dp), dimension(:), allocatable :: convolved_flux
      54              :       character(len=100) :: csv_file
      55            0 :       real(dp) :: synthetic_flux, vega_flux
      56              :       integer :: max_size, i
      57            0 :       real(dp) :: wv, fl, cf, fwv, ftr
      58              : 
      59            0 :       if (.not. folder_exists(trim(colors_results_directory))) call mkdir(trim(colors_results_directory))
      60            0 :       csv_file = trim(colors_results_directory)//'/'//trim(remove_dat(filter_name))//'_SED.csv'
      61            0 :       ierr = 0
      62              : 
      63              :       ! Load filter data
      64            0 :       call load_filter(filter_filepath, filter_wavelengths, filter_trans)
      65              : 
      66              : 
      67              :       ! Perform SED convolution
      68            0 :       allocate (convolved_flux(size(wavelengths)))
      69            0 :       call convolve_sed(wavelengths, fluxes, filter_wavelengths, filter_trans, convolved_flux)
      70              : 
      71              :       ! Write SED to CSV if requested
      72            0 :       if (make_sed) then
      73              :          ! Determine the maximum size among all arrays
      74              :          max_size = max(size(wavelengths), size(filter_wavelengths), &
      75            0 :                         size(fluxes), size(convolved_flux), size(filter_trans))
      76              : 
      77              :          ! Open the CSV file for writing
      78            0 :          open (unit=10, file=csv_file, status='REPLACE', action='write', iostat=ierr)
      79            0 :          if (ierr /= 0) then
      80            0 :             print *, "Error opening file for writing"
      81              :             return
      82              :          end if
      83              : 
      84              :          ! Write headers to the CSV file
      85            0 :          write (10, '(A)') "wavelengths,fluxes,convolved_flux,filter_wavelengths,filter_trans"
      86              : 
      87              :          ! Loop through data and safely write values, ensuring no out-of-bounds errors
      88            0 :          do i = 1, max_size
      89              :             ! Initialize values to zero in case they are out of bounds
      90            0 :             wv = 0.0_dp
      91            0 :             fl = 0.0_dp
      92            0 :             cf = 0.0_dp
      93            0 :             fwv = 0.0_dp
      94            0 :             ftr = 0.0_dp
      95              : 
      96              :             ! Assign actual values only if within valid indices
      97            0 :             if (i <= size(wavelengths)) wv = wavelengths(i)
      98            0 :             if (i <= size(fluxes)) fl = fluxes(i)
      99            0 :             if (i <= size(convolved_flux)) cf = convolved_flux(i)
     100            0 :             if (i <= size(filter_wavelengths)) fwv = filter_wavelengths(i)
     101            0 :             if (i <= size(filter_trans)) ftr = filter_trans(i)
     102              : 
     103              :             ! Write the formatted output
     104              :             write (line, '(ES14.6, ",", ES14.6, ",", ES14.6, ",", ES14.6, ",", ES14.6)') &
     105            0 :                wv, fl, cf, fwv, ftr
     106            0 :             write (10, '(A)') trim(line)
     107              :          end do
     108              : 
     109              :          ! Close the file
     110            0 :          close (10)
     111              :       end if
     112              : 
     113              :       ! Calculate Vega flux for zero point calibration
     114              :       vega_flux = calculate_vega_flux(vega_filepath, filter_wavelengths, filter_trans, &
     115            0 :                                       filter_name, make_sed, colors_results_directory)
     116              : 
     117              :       ! Calculate synthetic flux
     118              :       call calculate_synthetic_flux(wavelengths, convolved_flux, synthetic_flux, &
     119            0 :                                     filter_wavelengths, filter_trans)
     120              : 
     121              :       ! Calculate magnitude using Vega zero point
     122            0 :       if (vega_flux > 0.0_dp) then
     123            0 :          calculate_synthetic = -2.5d0*log10(synthetic_flux/vega_flux)
     124              :       else
     125            0 :          print *, "Error: Vega flux is zero, magnitude calculation is invalid."
     126            0 :          calculate_synthetic = huge(1.0_dp)
     127              :       end if
     128              : 
     129              :       ! Clean up
     130            0 :       deallocate (convolved_flux)
     131            0 :    end function calculate_synthetic
     132              : 
     133              :    !-----------------------------------------------------------------------
     134              :    ! Internal functions for synthetic photometry
     135              :    !-----------------------------------------------------------------------
     136              : 
     137              :    !****************************
     138              :    ! Convolve SED With Filter
     139              :    !****************************
     140            0 :    subroutine convolve_sed(wavelengths, fluxes, filter_wavelengths, filter_trans, convolved_flux)
     141              :       real(dp), dimension(:), intent(inout) :: wavelengths, fluxes
     142              :       real(dp), dimension(:), intent(inout) :: filter_wavelengths, filter_trans
     143              :       real(dp), dimension(:), allocatable, intent(out) :: convolved_flux
     144              :       real(dp), dimension(:), allocatable :: interpolated_filter
     145              :       integer :: n
     146              : 
     147            0 :       n = size(wavelengths)
     148              : 
     149              :       ! Allocate arrays
     150            0 :       allocate (interpolated_filter(n))
     151              : 
     152              :       ! Interpolate the filter transmission onto the wavelengths array
     153            0 :       call interpolate_array(filter_wavelengths, filter_trans, wavelengths, interpolated_filter)
     154              : 
     155              :       ! Perform convolution (element-wise multiplication)
     156            0 :       convolved_flux = fluxes*interpolated_filter
     157              : 
     158              :       ! Deallocate temporary arrays
     159            0 :       deallocate (interpolated_filter)
     160            0 :    end subroutine convolve_sed
     161              : 
     162              :    !****************************
     163              :    ! Calculate Synthetic Flux
     164              :    !****************************
     165            0 :    subroutine calculate_synthetic_flux(wavelengths, fluxes, synthetic_flux, &
     166            0 :                                        filter_wavelengths, filter_trans)
     167              : 
     168              :       real(dp), dimension(:), intent(in) :: wavelengths, fluxes
     169              :       real(dp), dimension(:), intent(inout) :: filter_wavelengths, filter_trans
     170              :       real(dp), intent(out) :: synthetic_flux
     171              :       integer :: i
     172            0 :       real(dp) :: integrated_flux, integrated_filter
     173              : 
     174              :       ! Validate inputs
     175            0 :       do i = 1, size(wavelengths) - 1
     176            0 :          if (wavelengths(i) <= 0.0_dp .or. fluxes(i) < 0.0_dp) then
     177            0 :             print *, "synthetic Invalid input at index", i, ":", wavelengths(i), fluxes(i)
     178            0 :             stop
     179              :          end if
     180              :       end do
     181              : 
     182            0 :       call romberg_integration(wavelengths, fluxes*wavelengths, integrated_flux)
     183              :       call romberg_integration(filter_wavelengths, &
     184            0 :                                filter_trans*filter_wavelengths, integrated_filter)
     185              :       ! Store the total flux
     186            0 :       if (integrated_filter > 0.0_dp) then
     187            0 :          synthetic_flux = integrated_flux/integrated_filter
     188              :       else
     189            0 :          print *, "Error: Integrated filter transmission is zero."
     190            0 :          synthetic_flux = -1.0_dp
     191            0 :          return
     192              :       end if
     193              :    end subroutine calculate_synthetic_flux
     194              : 
     195              :    !****************************
     196              :    ! Calculate Vega Flux for Zero Point
     197              :    !****************************
     198            0 :    function calculate_vega_flux(vega_filepath, filt_wave, filt_trans, &
     199            0 :                                 filter_name, make_sed, colors_results_directory) result(vega_flux)
     200              :       character(len=*), intent(in) :: vega_filepath, filter_name, colors_results_directory
     201              :       character(len=100) :: output_csv
     202              :       real(dp), dimension(:), intent(inout) :: filt_wave, filt_trans
     203              :       real(dp) :: vega_flux
     204            0 :       real(dp) :: int_flux, int_filter
     205            0 :       real(dp), allocatable :: vega_wave(:), vega_flux_arr(:), conv_flux(:)
     206              :       logical, intent(in) :: make_sed
     207              :       integer :: i, unit, max_size
     208            0 :       real(dp) :: wv, fl, cf, fwv, ftr
     209              :       integer:: ierr
     210              :       character(len=1000) :: line
     211              : 
     212              :       ! Load the Vega SED
     213              :       call load_vega_sed(vega_filepath, vega_wave, vega_flux_arr)
     214              : 
     215              :       ! Convolve the Vega SED with the filter transmission
     216            0 :       allocate (conv_flux(size(vega_wave)))
     217            0 :       call convolve_sed(vega_wave, vega_flux_arr, filt_wave, filt_trans, conv_flux)
     218              : 
     219              :       ! Integrate the convolved Vega SED and the filter transmission
     220            0 :       call romberg_integration(vega_wave, vega_wave*conv_flux, int_flux)
     221            0 :       call romberg_integration(filt_wave, filt_wave*filt_trans, int_filter)
     222              : 
     223            0 :       if (int_filter > 0.0_dp) then
     224            0 :          vega_flux = int_flux/int_filter
     225              :       else
     226              :          vega_flux = -1.0_dp
     227              :       end if
     228              : 
     229              :       ! Write Vega SED to CSV if requested
     230            0 :       if (make_sed) then
     231              :          ! Determine the maximum size among all arrays
     232              :          max_size = max(size(vega_wave), size(vega_flux_arr), size(conv_flux), &
     233            0 :                         size(filt_wave), size(filt_trans))
     234              : 
     235            0 :          if (.not. folder_exists(trim(colors_results_directory))) call mkdir(trim(colors_results_directory))
     236            0 :          output_csv = trim(colors_results_directory)//'/VEGA_'//trim(remove_dat(filter_name))//'_SED.csv'
     237              : 
     238              :          ! Open the CSV file for writing
     239            0 :          open (unit=10, file=output_csv, status='REPLACE', action='write', iostat=ierr)
     240            0 :          if (ierr /= 0) then
     241            0 :             print *, "Error opening file for writing"
     242            0 :             return
     243              :          end if
     244              : 
     245            0 :          write (10, '(A)') "wavelengths,fluxes,convolved_flux,filter_wavelengths,filter_trans"
     246              : 
     247              :          ! Loop through data and safely write values, ensuring no out-of-bounds errors
     248            0 :          do i = 1, max_size
     249              :             ! Initialize values to zero in case they are out of bounds
     250            0 :             wv = 0.0_dp
     251            0 :             fl = 0.0_dp
     252            0 :             cf = 0.0_dp
     253            0 :             fwv = 0.0_dp
     254            0 :             ftr = 0.0_dp
     255              : 
     256              :             ! Assign actual values only if within valid indices
     257            0 :             if (i <= size(vega_wave)) wv = vega_wave(i)
     258            0 :             if (i <= size(vega_flux_arr)) fl = vega_flux_arr(i)
     259            0 :             if (i <= size(conv_flux)) cf = conv_flux(i)
     260            0 :             if (i <= size(filt_wave)) fwv = filt_wave(i)
     261            0 :             if (i <= size(filt_trans)) ftr = filt_trans(i)
     262              : 
     263              :             ! Write the formatted output
     264              :             write (line, '(ES14.6, ",", ES14.6, ",", ES14.6, ",", ES14.6, ",", ES14.6)') &
     265            0 :                wv, fl, cf, fwv, ftr
     266            0 :             write (10, '(A)') trim(line)
     267              :          end do
     268              : 
     269              :          ! Close the file
     270            0 :          close (10)
     271              :       end if
     272              : 
     273              :       ! Clean up
     274            0 :       deallocate (conv_flux, vega_wave, vega_flux_arr)
     275            0 :    end function calculate_vega_flux
     276              : 
     277              : end module synthetic
        

Generated by: LCOV version 2.0-1