Commit 74c93801ddd5758252381446308f64eb9040df15
1 parent
8c7309c7
(#2938) Adjust column name formatting in csspec fits files
This ensures the SED fits files are consistent with the current suggested data format
Showing
2 changed files
with
78 additions
and
58 deletions
cscripts/csspec.py
| ... | ... | @@ -645,6 +645,7 @@ class csspec(ctools.csobservation): |
| 645 | 645 | emin = self._ebounds.emin(i) |
| 646 | 646 | emax = self._ebounds.emax(i) |
| 647 | 647 | elogmean = self._ebounds.elogmean(i) |
| 648 | + e_scale = elogmean.MeV() * elogmean.MeV() * gammalib.MeV2erg | |
| 648 | 649 | |
| 649 | 650 | # Select observations for energy bin |
| 650 | 651 | obs = self._select_obs(emin, emax) |
| ... | ... | @@ -654,6 +655,7 @@ class csspec(ctools.csobservation): |
| 654 | 655 | 'energy_low': (elogmean - emin).TeV(), |
| 655 | 656 | 'energy_high': (emax - elogmean).TeV(), |
| 656 | 657 | 'flux': 0.0, |
| 658 | + 'e2dnde': 0.0, | |
| 657 | 659 | 'flux_err': 0.0, |
| 658 | 660 | 'TS': 0.0, |
| 659 | 661 | 'ulimit': 0.0, |
| ... | ... | @@ -736,8 +738,7 @@ class csspec(ctools.csobservation): |
| 736 | 738 | |
| 737 | 739 | # Compute upper limit |
| 738 | 740 | if ulimit_value > 0.0: |
| 739 | - result['ulimit'] = ulimit_value * elogmean.MeV() * \ | |
| 740 | - elogmean.MeV() * gammalib.MeV2erg | |
| 741 | + result['ulimit'] = ulimit_value | |
| 741 | 742 | |
| 742 | 743 | # Compute differential flux and flux error |
| 743 | 744 | fitted_flux = source.spectral().eval(elogmean) |
| ... | ... | @@ -758,14 +759,14 @@ class csspec(ctools.csobservation): |
| 758 | 759 | e_flux *= norm |
| 759 | 760 | |
| 760 | 761 | # Convert differential flux and flux error to nuFnu |
| 761 | - elogmean2 = elogmean.MeV() * elogmean.MeV() | |
| 762 | - result['flux'] = fitted_flux * elogmean2 * gammalib.MeV2erg | |
| 763 | - result['flux_err'] = e_flux * elogmean2 * gammalib.MeV2erg | |
| 762 | + result['flux'] = fitted_flux | |
| 763 | + result['e2dnde'] = fitted_flux * e_scale | |
| 764 | + result['flux_err'] = e_flux | |
| 764 | 765 | |
| 765 | 766 | # Log information |
| 766 | - value = '%e +/- %e' % (result['flux'], result['flux_err']) | |
| 767 | + value = '%e +/- %e' % (result['e2dnde'], result['flux_err']*e_scale) | |
| 767 | 768 | if self['calc_ulim'].boolean() and result['ulimit'] > 0.0: |
| 768 | - value += ' [< %e]' % (result['ulimit']) | |
| 769 | + value += ' [< %e]' % (result['ulimit']*e_scale) | |
| 769 | 770 | value += ' erg/cm2/s' |
| 770 | 771 | if self['calc_ts'].boolean() and result['TS'] > 0.0: |
| 771 | 772 | value += ' (TS = %.3f)' % (result['TS']) |
| ... | ... | @@ -866,6 +867,7 @@ class csspec(ctools.csobservation): |
| 866 | 867 | 'energy_low': (elogmean - emin).TeV(), |
| 867 | 868 | 'energy_high': (emax - elogmean).TeV(), |
| 868 | 869 | 'flux': 0.0, |
| 870 | + 'e2dnde': 0.0, | |
| 869 | 871 | 'flux_err': 0.0, |
| 870 | 872 | 'TS': 0.0, |
| 871 | 873 | 'ulimit': 0.0, |
| ... | ... | @@ -876,8 +878,9 @@ class csspec(ctools.csobservation): |
| 876 | 878 | |
| 877 | 879 | # Convert differential flux and flux error to nuFnu |
| 878 | 880 | norm = elogmean.MeV() * elogmean.MeV() * gammalib.MeV2erg |
| 879 | - result['flux'] = spectrum[i*2+1].value() * norm | |
| 880 | - result['flux_err'] = spectrum[i*2+1].error() * norm | |
| 881 | + result['flux'] = spectrum[i*2+1].value() | |
| 882 | + result['e2dnde'] = spectrum[i*2+1].value() * norm | |
| 883 | + result['flux_err'] = spectrum[i*2+1].error() | |
| 881 | 884 | |
| 882 | 885 | # Compute upper flux limit |
| 883 | 886 | ulimit_value = -1.0 |
| ... | ... | @@ -925,8 +928,7 @@ class csspec(ctools.csobservation): |
| 925 | 928 | |
| 926 | 929 | # Compute upper limit |
| 927 | 930 | if ulimit_value > 0.0: |
| 928 | - result['ulimit'] = ulimit_value * elogmean.MeV() * \ | |
| 929 | - elogmean.MeV() * gammalib.MeV2erg | |
| 931 | + result['ulimit'] = ulimit_value | |
| 930 | 932 | |
| 931 | 933 | # Compute TS |
| 932 | 934 | if self['calc_ts'].boolean(): |
| ... | ... | @@ -954,9 +956,9 @@ class csspec(ctools.csobservation): |
| 954 | 956 | result['TS'] = 2.0 * (logL1 - logL0) |
| 955 | 957 | |
| 956 | 958 | # Log information |
| 957 | - value = '%e +/- %e' % (result['flux'], result['flux_err']) | |
| 959 | + value = '%e +/- %e' % (result['e2dnde'], result['flux_err']*norm) | |
| 958 | 960 | if self['calc_ulim'].boolean() and result['ulimit'] > 0.0: |
| 959 | - value += ' [< %e]' % (result['ulimit']) | |
| 961 | + value += ' [< %e]' % (result['ulimit']*norm) | |
| 960 | 962 | value += ' erg/cm2/s' |
| 961 | 963 | if self['calc_ts'].boolean() and result['TS'] > 0.0: |
| 962 | 964 | value += ' (TS = %.3f)' % (result['TS']) |
| ... | ... | @@ -994,7 +996,6 @@ class csspec(ctools.csobservation): |
| 994 | 996 | dll_sigmax = self['dll_sigmax'].real() |
| 995 | 997 | dll_sigstep = self['dll_sigstep'].real() |
| 996 | 998 | sigsteps = int(2 * (dll_sigmax/dll_sigstep) + 1) |
| 997 | - scale = elogmean.MeV() * elogmean.MeV() * gammalib.MeV2erg | |
| 998 | 999 | |
| 999 | 1000 | # Setup the source model for fitting |
| 1000 | 1001 | source = like.obs().models()[self['srcname'].string()] |
| ... | ... | @@ -1016,6 +1017,9 @@ class csspec(ctools.csobservation): |
| 1016 | 1017 | # Store the resulting log-likelihood |
| 1017 | 1018 | norm_scan = [] |
| 1018 | 1019 | dloglike = [] |
| 1020 | + ref_norm = norm | |
| 1021 | + if self._method == 'SLICE': | |
| 1022 | + ref_norm = spectral.eval(elogmean) | |
| 1019 | 1023 | |
| 1020 | 1024 | # Compute the flux values to evaluate loglike at |
| 1021 | 1025 | log_norm = math.log10(norm) |
| ... | ... | @@ -1042,9 +1046,9 @@ class csspec(ctools.csobservation): |
| 1042 | 1046 | # Store dlikelihood & norm |
| 1043 | 1047 | dloglike.append(loglike - like.obs().logL()) |
| 1044 | 1048 | if self._method == 'SLICE': |
| 1045 | - norm_scan.append(spectral.eval(elogmean) * scale) | |
| 1049 | + norm_scan.append(spectral.eval(elogmean)) | |
| 1046 | 1050 | else: |
| 1047 | - norm_scan.append(new_norm * scale) | |
| 1051 | + norm_scan.append(new_norm) | |
| 1048 | 1052 | |
| 1049 | 1053 | return (norm_scan, dloglike, -loglike) |
| 1050 | 1054 | |
| ... | ... | @@ -1063,22 +1067,27 @@ class csspec(ctools.csobservation): |
| 1063 | 1067 | energy = gammalib.GFitsTableDoubleCol('e_ref', nrows) |
| 1064 | 1068 | energy_low = gammalib.GFitsTableDoubleCol('e_min', nrows) |
| 1065 | 1069 | energy_high = gammalib.GFitsTableDoubleCol('e_max', nrows) |
| 1066 | - flux = gammalib.GFitsTableDoubleCol('e2dnde', nrows) | |
| 1067 | - flux_err = gammalib.GFitsTableDoubleCol('e2dnde_err', nrows) | |
| 1070 | + norm = gammalib.GFitsTableDoubleCol('norm', nrows) | |
| 1071 | + norm_err = gammalib.GFitsTableDoubleCol('norm_err', nrows) | |
| 1072 | + ulim_values = gammalib.GFitsTableDoubleCol('norm_ul', nrows) | |
| 1073 | + e2dnde = gammalib.GFitsTableDoubleCol('ref_e2dnde', nrows) | |
| 1074 | + flux = gammalib.GFitsTableDoubleCol('ref_flux', nrows) | |
| 1075 | + Npred_values = gammalib.GFitsTableDoubleCol('ref_npred', nrows) | |
| 1068 | 1076 | TSvalues = gammalib.GFitsTableDoubleCol('ts', nrows) |
| 1069 | - ulim_values = gammalib.GFitsTableDoubleCol('e2dnde_ul', nrows) | |
| 1070 | - Npred_values = gammalib.GFitsTableDoubleCol('npred', nrows) | |
| 1071 | 1077 | loglike = gammalib.GFitsTableDoubleCol('loglike', nrows) |
| 1072 | 1078 | norm_scan = gammalib.GFitsTableDoubleCol('norm_scan', nrows, ncols) |
| 1073 | 1079 | dloglike_scan= gammalib.GFitsTableDoubleCol('dloglike_scan', nrows, ncols) |
| 1074 | 1080 | energy.unit('TeV') |
| 1075 | 1081 | energy_low.unit('TeV') |
| 1076 | 1082 | energy_high.unit('TeV') |
| 1077 | - flux.unit('erg/cm2/s') | |
| 1078 | - flux_err.unit('erg/cm2/s') | |
| 1079 | - ulim_values.unit('erg/cm2/s') | |
| 1083 | + norm.unit('') | |
| 1084 | + norm_err.unit('') | |
| 1085 | + ulim_values.unit('') | |
| 1086 | + e2dnde.unit('erg/cm2/s') | |
| 1087 | + flux.unit('counts/MeV/cm2/s') | |
| 1088 | + Npred_values.unit('counts') | |
| 1080 | 1089 | loglike.unit('') |
| 1081 | - norm_scan.unit('erg/cm2/s') | |
| 1090 | + norm_scan.unit('') | |
| 1082 | 1091 | dloglike_scan.unit('') |
| 1083 | 1092 | |
| 1084 | 1093 | # File FITS table columns |
| ... | ... | @@ -1086,17 +1095,19 @@ class csspec(ctools.csobservation): |
| 1086 | 1095 | energy[i] = result['energy'] |
| 1087 | 1096 | energy_low[i] = result['energy_low'] |
| 1088 | 1097 | energy_high[i] = result['energy_high'] |
| 1098 | + norm[i] = 1.0 | |
| 1099 | + norm_err[i] = result['flux_err'] / result['flux'] | |
| 1100 | + ulim_values[i] = result['ulimit'] / result['flux'] | |
| 1101 | + e2dnde[i] = result['e2dnde'] | |
| 1089 | 1102 | flux[i] = result['flux'] |
| 1090 | - flux_err[i] = result['flux_err'] | |
| 1091 | - TSvalues[i] = result['TS'] | |
| 1092 | - ulim_values[i] = result['ulimit'] | |
| 1093 | 1103 | Npred_values[i] = result['Npred'] |
| 1104 | + TSvalues[i] = result['TS'] | |
| 1094 | 1105 | loglike[i] = result['logL'] |
| 1095 | 1106 | |
| 1096 | 1107 | # Add likelihood scan values |
| 1097 | 1108 | for fbin in range(ncols): |
| 1098 | 1109 | dloglike_scan[i,fbin] = result['dloglike'][fbin] |
| 1099 | - norm_scan[i,fbin] = result['norm_scan'][fbin] | |
| 1110 | + norm_scan[i,fbin] = result['norm_scan'][fbin] / result['flux'] | |
| 1100 | 1111 | |
| 1101 | 1112 | # Initialise FITS Table with extension "SPECTRUM" |
| 1102 | 1113 | table = gammalib.GFitsBinTable(nrows) |
| ... | ... | @@ -1110,26 +1121,29 @@ class csspec(ctools.csobservation): |
| 1110 | 1121 | table.append(energy) |
| 1111 | 1122 | table.append(energy_low) |
| 1112 | 1123 | table.append(energy_high) |
| 1124 | + table.append(norm) | |
| 1125 | + table.append(norm_err) | |
| 1126 | + table.append(ulim_values) | |
| 1127 | + table.append(e2dnde) | |
| 1113 | 1128 | table.append(flux) |
| 1114 | - table.append(flux_err) | |
| 1115 | 1129 | table.append(TSvalues) |
| 1116 | - table.append(ulim_values) | |
| 1117 | 1130 | table.append(Npred_values) |
| 1118 | - | |
| 1131 | + | |
| 1119 | 1132 | # Define the SED type |
| 1120 | - sed_type = 'e2dnde' | |
| 1121 | - if ncols > 0: | |
| 1122 | - sed_type = 'likelihood' | |
| 1123 | - table.append(loglike) | |
| 1124 | - table.append(norm_scan) | |
| 1125 | - table.append(dloglike_scan) | |
| 1126 | - table.card('SED_TYPE', sed_type, 'SED type') | |
| 1133 | + table.card('SED_TYPE', 'norm,e2dnde,flux,npred', 'SED type') | |
| 1127 | 1134 | |
| 1128 | 1135 | # Define the upper limit confidence level |
| 1129 | 1136 | ulimit = ctools.ctulimit() |
| 1130 | 1137 | table.card('UL_CONF', ulimit['confidence'].real(), |
| 1131 | 1138 | 'Confidence level of upper limits') |
| 1132 | 1139 | |
| 1140 | + # Add the likelihood data | |
| 1141 | + if ncols > 0: | |
| 1142 | + table.card('SED_TYPE', 'likelihood,e2dnde,flux,npred', 'Likelihood scan data') | |
| 1143 | + table.append(loglike) | |
| 1144 | + table.append(norm_scan) | |
| 1145 | + table.append(dloglike_scan) | |
| 1146 | + | |
| 1133 | 1147 | # Create the FITS file now |
| 1134 | 1148 | self._fits = gammalib.GFits() |
| 1135 | 1149 | self._fits.append(table) | ... | ... |
examples/show_spectrum.py
| ... | ... | @@ -82,10 +82,11 @@ def get_spectrum_fits(fits): |
| 82 | 82 | c_energy = table['e_ref'] |
| 83 | 83 | c_ed = table['e_min'] |
| 84 | 84 | c_eu = table['e_max'] |
| 85 | - c_flux = table['e2dnde'] | |
| 86 | - c_eflux = table['e2dnde_err'] | |
| 85 | + c_flux = table['ref_e2dnde'] | |
| 86 | + c_norm = table['norm'] | |
| 87 | + c_eflux = table['norm_err'] | |
| 88 | + c_upper = table['norm_ul'] | |
| 87 | 89 | c_ts = table['ts'] |
| 88 | - c_upper = table['e2dnde_ul'] | |
| 89 | 90 | |
| 90 | 91 | # Initialise arrays to be filled |
| 91 | 92 | spec = { |
| ... | ... | @@ -101,18 +102,22 @@ def get_spectrum_fits(fits): |
| 101 | 102 | 'yerr' : [], |
| 102 | 103 | 'loglike' : [], |
| 103 | 104 | 'engs_scan' : [], |
| 104 | - 'norm_scan' : [], | |
| 105 | + 'e2dnde_scan' : [], | |
| 105 | 106 | 'dll_scan' : [] |
| 106 | 107 | } |
| 107 | 108 | |
| 108 | 109 | # Determine if we can load the delta-log-likelihood profiles |
| 110 | + has_sedtype = table.has_card('SED_TYPE') | |
| 109 | 111 | load_dll = False |
| 110 | - if (fits.table(1).has_card('SED_TYPE') and | |
| 111 | - fits.table(1).card('SED_TYPE').string() == 'likelihood'): | |
| 112 | - c_loglike = table['loglike'] | |
| 113 | - c_norm_scan = table['norm_scan'] | |
| 114 | - c_dll_scan = table['dloglike_scan'] | |
| 115 | - load_dll = True | |
| 112 | + if has_sedtype: | |
| 113 | + seds = table.card('SED_TYPE').string().split(',') | |
| 114 | + | |
| 115 | + # Load likelhood columns if present | |
| 116 | + if seds[0] == 'likelihood': | |
| 117 | + load_dll = True | |
| 118 | + c_loglike = table['loglike'] | |
| 119 | + c_norm_scan = table['norm_scan'] | |
| 120 | + c_dll_scan = table['dloglike_scan'] | |
| 116 | 121 | |
| 117 | 122 | # Loop over rows of the file |
| 118 | 123 | nrows = table.nrows() |
| ... | ... | @@ -120,36 +125,37 @@ def get_spectrum_fits(fits): |
| 120 | 125 | |
| 121 | 126 | # Get Test Statistic, flux and flux error |
| 122 | 127 | ts = c_ts.real(row) |
| 123 | - flx = c_flux.real(row) | |
| 124 | - e_flx = c_eflux.real(row) | |
| 128 | + norm = c_norm.real(row) | |
| 129 | + flx = norm * c_flux.real(row) | |
| 130 | + e_flx = flx * c_eflux.real(row) | |
| 125 | 131 | |
| 126 | 132 | # If Test Statistic is larger than 9 and flux error is smaller than |
| 127 | 133 | # flux then append flux plots ... |
| 128 | 134 | if ts > 9.0 and e_flx < flx: |
| 129 | 135 | spec['energies'].append(c_energy.real(row)) |
| 130 | - spec['flux'].append(c_flux.real(row)) | |
| 136 | + spec['flux'].append(flx) | |
| 131 | 137 | spec['ed_engs'].append(c_ed.real(row)) |
| 132 | 138 | spec['eu_engs'].append(c_eu.real(row)) |
| 133 | - spec['e_flux'].append(c_eflux.real(row)) | |
| 139 | + spec['e_flux'].append(e_flx) | |
| 134 | 140 | |
| 135 | 141 | # ... otherwise append upper limit |
| 136 | 142 | else: |
| 137 | 143 | spec['ul_energies'].append(c_energy.real(row)) |
| 138 | - spec['ul_flux'].append(c_upper.real(row)) | |
| 144 | + spec['ul_flux'].append(flx*c_upper.real(row)) | |
| 139 | 145 | spec['ul_ed_engs'].append(c_ed.real(row)) |
| 140 | 146 | spec['ul_eu_engs'].append(c_eu.real(row)) |
| 141 | 147 | |
| 142 | 148 | # Load the delta log-likelihood values |
| 143 | 149 | if load_dll: |
| 144 | - spec['norm_scan'] = np.zeros((nrows, c_norm_scan.elements(0))) | |
| 145 | - spec['dll_scan'] = np.zeros((nrows, c_norm_scan.elements(0))) | |
| 150 | + spec['e2dnde_scan'] = np.zeros((nrows, c_norm_scan.elements(0))) | |
| 151 | + spec['dll_scan'] = np.zeros((nrows, c_norm_scan.elements(0))) | |
| 146 | 152 | |
| 147 | 153 | for row in range(nrows): |
| 148 | 154 | loglike = c_loglike.real(row) |
| 149 | 155 | spec['loglike'].append(loglike) |
| 150 | 156 | spec['engs_scan'].append(c_energy.real(row) - c_ed.real(row)) |
| 151 | 157 | for col in range(c_norm_scan.elements(row)): |
| 152 | - spec['norm_scan'][row,col] = c_norm_scan.real(row,col) | |
| 158 | + spec['e2dnde_scan'][row,col] = c_norm_scan.real(row,col) * c_flux.real(row) | |
| 153 | 159 | spec['dll_scan'][row,col] = c_dll_scan.real(row,col) |
| 154 | 160 | spec['engs_scan'].append(c_energy.real(nrows-1)+c_eu.real(nrows-1)) |
| 155 | 161 | |
| ... | ... | @@ -183,10 +189,10 @@ def plot_dloglike(spec): |
| 183 | 189 | y_bounds = 10 ** y_bounds |
| 184 | 190 | |
| 185 | 191 | dll_hist = [] |
| 186 | - for ebin in range(len(spec['norm_scan'])): | |
| 192 | + for ebin in range(len(spec['e2dnde_scan'])): | |
| 187 | 193 | |
| 188 | 194 | # Interpolate the dlogLike values |
| 189 | - dll_interp = np.interp(fluxpnts, spec['norm_scan'][ebin], | |
| 195 | + dll_interp = np.interp(fluxpnts, spec['e2dnde_scan'][ebin], | |
| 190 | 196 | spec['dll_scan'][ebin]) |
| 191 | 197 | dll_hist.append(dll_interp) |
| 192 | 198 | ... | ... |