Commit 4cc94b92383e48ceca76648fb3e19085724209a9

Authored by Jürgen Knödlseder
1 parent 169cd922

Implement background rate replacement by a power law above given energy threshold.

cscripts/csroot2caldb.par
... ... @@ -29,6 +29,7 @@ rebin, b, h, no,,, "Rebin"
29 29 eascale, r, h, 1.0,,, "Scaling factor for effective areas"
30 30 bgdscale, r, h, 1.0,,, "Scaling factor for background rates"
31 31 bgdoversample, i, h, 1,,, "Spatial oversampling factor for background template"
  32 +bgdethres, r, h, 1000.0,,, "Energy above which to replace background by power law (TeV)"
32 33 bgdinfill, b, h, no,,, "Infill of background template?"
33 34  
34 35 #
... ...
cscripts/csroot2caldb.py
... ... @@ -571,6 +571,11 @@ class csroot2caldb(ctools.cscript):
571 571 # Continue only if effective area HDU has been opened
572 572 if self._hdu_ea != None:
573 573  
  574 + # Write header
  575 + if self._logTerse():
  576 + self._log('\n')
  577 + self._log.header1('Generate effective area extension')
  578 +
574 579 # Get User parameters
575 580 norm1d = self['norm1d'].boolean()
576 581  
... ... @@ -642,8 +647,13 @@ class csroot2caldb(ctools.cscript):
642 647 # Continue only if point spread function HDU has been opened
643 648 if self._hdu_psf != None:
644 649  
  650 + # Write header
  651 + if self._logTerse():
  652 + self._log('\n')
  653 + self._log.header1('Generate point spread function extension')
  654 +
645 655 # King profile PSF
646   - if psftype == "King":
  656 + if psftype == 'King':
647 657 self._root2psf_king(file)
648 658 else:
649 659 self._root2psf_gauss(file)
... ... @@ -874,19 +884,24 @@ class csroot2caldb(ctools.cscript):
874 884 # Continue only if energy dispersion HDU has been opened
875 885 if self._hdu_edisp != None:
876 886  
  887 + # Write header
  888 + if self._logTerse():
  889 + self._log('\n')
  890 + self._log.header1('Generate energy dispersion extension')
  891 +
877 892 # Allocate ROOT 3D array
878 893 matrix = TH3F()
879   - file.GetObject("EestOverEtrue_offaxis", matrix)
  894 + file.GetObject('EestOverEtrue_offaxis', matrix)
880 895  
881 896 # Set boundaries
882   - bounds = self._make_3D_migra(matrix, self._hdu_edisp, None, "")
  897 + bounds = self._make_3D_migra(matrix, self._hdu_edisp, None, '')
883 898 for b in bounds:
884 899 self._edisp_bounds.append(b)
885 900 self._set_cif_keywords(self._hdu_edisp, self._edisp_name, \
886 901 self._edisp_bounds, self._edisp_desc)
887 902  
888 903 # MATRIX
889   - self._make_3D_migra(matrix, self._hdu_edisp, "MATRIX", "")
  904 + self._make_3D_migra(matrix, self._hdu_edisp, 'MATRIX', '')
890 905  
891 906 # Return
892 907 return
... ... @@ -907,22 +922,27 @@ class csroot2caldb(ctools.cscript):
907 922 # Continue only if background HDU has been opened
908 923 if self._hdu_bgd != None:
909 924  
  925 + # Write header
  926 + if self._logTerse():
  927 + self._log('\n')
  928 + self._log.header1('Generate 2D background extension')
  929 +
910 930 # Allocate ROOT 2D array
911 931 array = TH2F()
912   - file.GetObject("BGRatePerSqDeg_offaxis", array)
  932 + file.GetObject('BGRatePerSqDeg_offaxis', array)
913 933  
914 934 # Set boundaries
915   - bounds = self._make_2D(array, self._hdu_bgd, None, "deg")
  935 + bounds = self._make_2D(array, self._hdu_bgd, None, 'deg')
916 936 for b in bounds:
917 937 self._bgd_bounds.append(b)
918 938 self._set_cif_keywords(self._hdu_bgd, self._bgd_name, \
919 939 self._bgd_bounds, self._bgd_desc)
920 940  
921 941 # BGD
922   - self._make_2D(array, self._hdu_bgd, "BGD", "")
  942 + self._make_2D(array, self._hdu_bgd, 'BGD', '')
923 943  
924 944 # BGD_RECO
925   - self._make_2D(array, self._hdu_bgd, "BGD_RECO", "")
  945 + self._make_2D(array, self._hdu_bgd, 'BGD_RECO', '')
926 946  
927 947 # Return
928 948 return
... ... @@ -942,14 +962,28 @@ class csroot2caldb(ctools.cscript):
942 962 # Continue only if background HDU has been opened
943 963 if self._hdu_bgd != None:
944 964  
  965 + # Write header
  966 + if self._logTerse():
  967 + self._log('\n')
  968 + self._log.header1('Generate 3D background extension')
  969 +
945 970 # Get User parameters
946   - norm1d = self['norm1d'].boolean()
  971 + norm1d = self['norm1d'].boolean()
  972 + bgdethres = self['bgdethres'].real()
  973 + bgdinfill = self['bgdinfill'].boolean()
947 974  
948 975 # Allocate ROOT 2D array
949 976 array = TH2F()
950 977 file.GetObject('BGRatePerSqDeg_offaxis', array)
951 978  
952   - # Normalize 2D histogram on on-axis 1D histogram
  979 + # Replace 2D histogram values by power law extrapolation
  980 + self._plaw_replace(array, bgdethres)
  981 +
  982 + # Optionally fill in empty values in 2D histogram
  983 + if bgdinfill:
  984 + self._infill_bgd(array)
  985 +
  986 + # Optionally normalize 2D histogram on on-axis 1D histogram
953 987 if norm1d:
954 988 array_1D = TH1F()
955 989 file.GetObject('BGRatePerSqDeg', array_1D)
... ... @@ -1033,14 +1067,17 @@ class csroot2caldb(ctools.cscript):
1033 1067 if neng != hist1D.GetXaxis().GetNbins():
1034 1068 # Write header
1035 1069 if self._logTerse():
1036   - self._log('Difference in energy binning between 2D histogram'
1037   - ' "%s" and 1D histogram "%s".\n' %
1038   - (hist2D.GetName(), hist1D.GetName()))
  1070 + self._log(gammalib.parformat('On-axis normalisation'))
  1071 + self._log('Impossible since energy binning of 1D histogram '
  1072 + '"%s" does not match that of 2D histogram "%s".\n' %
  1073 + (hist1D.GetName(), hist2D.GetName()))
1039 1074 else:
1040 1075 # Write header
1041 1076 if self._logTerse():
1042   - self._log('Renormalize 2D histogram "%s" on 1D histogram "%s".\n' %
1043   - (hist2D.GetName(), hist1D.GetName()))
  1077 + self._log(gammalib.parformat('On-axis normalisation'))
  1078 + self._log('On-axis values of 1D histogram "%s" imposed on '
  1079 + '2D histogram "%s".\n' %
  1080 + (hist1D.GetName(), hist2D.GetName()))
1044 1081  
1045 1082 # Get a copy of 2D histogram
1046 1083 hist2D_copy = hist2D.Clone()
... ... @@ -1087,6 +1124,218 @@ class csroot2caldb(ctools.cscript):
1087 1124 # Return
1088 1125 return
1089 1126  
  1127 + def _plaw_replace(self, hist2D, ethres):
  1128 + """
  1129 + Replace background rate values by power law values
  1130 +
  1131 + Parameters
  1132 + ----------
  1133 + hist2D : `~ROOT.TH2F`
  1134 + ROOT 2D histogram.
  1135 + ethres : float
  1136 + Energy threshold.
  1137 + """
  1138 + # Extract energy and offset angle vectors
  1139 + energies = hist2D.GetXaxis()
  1140 + neng = energies.GetNbins()
  1141 + offsets = hist2D.GetYaxis()
  1142 + noffset = offsets.GetNbins()
  1143 +
  1144 + # Continue only if threshold is low enough
  1145 + if ethres < math.pow(10.0, energies.GetBinUpEdge(neng)):
  1146 +
  1147 + # Determine mean energy values
  1148 + engs = []
  1149 + for ieng in range(neng):
  1150 + energy = 0.5 * (math.pow(10.0, energies.GetBinLowEdge(ieng+1)) +
  1151 + math.pow(10.0, energies.GetBinUpEdge(ieng+1)))
  1152 + engs.append(energy)
  1153 +
  1154 + # Loop over all offaxis angles
  1155 + for ioff in range(noffset):
  1156 +
  1157 + # Determine offset angle (for logging)
  1158 + offset = 0.5 * (offsets.GetBinLowEdge(ioff+1) +
  1159 + offsets.GetBinUpEdge(ioff+1))
  1160 +
  1161 + # Initialise vectors
  1162 + axis = []
  1163 + rates = []
  1164 +
  1165 + # Extract values
  1166 + for ieng in range(neng):
  1167 + energy = engs[ieng]
  1168 + rate = hist2D.GetBinContent(ieng+1,ioff+1)
  1169 + axis.append(energy)
  1170 + rates.append(rate)
  1171 +
  1172 + # Determine energy range
  1173 + emin, emax = self._plaw_energy_range(axis, rates)
  1174 +
  1175 + # Get power law coefficients
  1176 + coeff = self._plaw_coefficients(axis, rates, emin, ethres)
  1177 +
  1178 + # Replace histogram values by power law values
  1179 + for ieng in range(neng):
  1180 + energy = engs[ieng]
  1181 + if energy > ethres:
  1182 + value = self._plaw_value(coeff, energy)
  1183 + hist2D.SetBinContent(ieng+1,ioff+1,value)
  1184 + if self._logTerse():
  1185 + self._log(gammalib.parformat('Power law replacement'))
  1186 + self._log('%e cts/s/deg2 (E=%8.4f TeV (%d) '
  1187 + 'Off=%.2f deg (%d)\n' %
  1188 + (value,engs[ieng],ieng,offset,ioff))
  1189 +
  1190 + # Return
  1191 + return
  1192 +
  1193 + def _plaw_energy_range(self, energies, rates):
  1194 + """
  1195 + Determine the energy range over which the power law is valid
  1196 +
  1197 + Parameters
  1198 + ----------
  1199 + energies : list of float
  1200 + Energy values (TeV)
  1201 + rates : list of float
  1202 + Background rate values
  1203 + """
  1204 + # Find energy at which background rate takes a maximum
  1205 + min_energy = 0.0
  1206 + max_rate = 0.0
  1207 + for ieng, energy in enumerate(energies):
  1208 + if rates[ieng] > max_rate:
  1209 + max_rate = rates[ieng]
  1210 + min_energy = energy
  1211 +
  1212 + # Find last data point
  1213 + max_energy = 0.0
  1214 + for ieng in range(len(energies)-1,-1,-1):
  1215 + if rates[ieng] > 0.0:
  1216 + max_energy = energies[ieng]
  1217 + break
  1218 +
  1219 + # Return
  1220 + return (min_energy, max_energy)
  1221 +
  1222 + def _plaw_coefficients(self, energies, rates, emin, emax):
  1223 + """
  1224 + Determines the power law coefficients
  1225 +
  1226 + Parameters
  1227 + ----------
  1228 + energies : list of float
  1229 + Energy values (TeV)
  1230 + rates : list of float
  1231 + Background rate values
  1232 + emin : float
  1233 + Minimum energy (TeV)
  1234 + emax : float
  1235 + Maximum energy (TeV)
  1236 + """
  1237 + # Determine nearest energy indices
  1238 + i_emin = 0
  1239 + i_emax = 0
  1240 + delta_emin = 1.0e30
  1241 + delta_emax = 1.0e30
  1242 + for ieng, energy in enumerate(energies):
  1243 + if abs(energy-emin) < delta_emin:
  1244 + delta_emin = abs(energy-emin)
  1245 + i_emin = ieng
  1246 + if abs(energy-emax) < delta_emax:
  1247 + delta_emax = abs(energy-emax)
  1248 + i_emax = ieng
  1249 +
  1250 + # Determine coefficients of power law
  1251 + x1 = math.log10(energies[i_emin])
  1252 + x2 = math.log10(energies[i_emax])
  1253 + y1 = math.log10(rates[i_emin])
  1254 + y2 = math.log10(rates[i_emax])
  1255 + m = (y2-y1)/(x2-x1)
  1256 + t = y1 - m * x1
  1257 +
  1258 + # Return coefficients
  1259 + return {'m': m, 't': t}
  1260 +
  1261 + def _plaw_value(self, coeff, energy):
  1262 + """
  1263 + Determine the power law value
  1264 +
  1265 + Parameters
  1266 + ----------
  1267 + coeff : dict
  1268 + Dictionary of coefficients
  1269 + energy : float
  1270 + Energy value (TeV)
  1271 + """
  1272 + # Compute value
  1273 + value = math.pow(10.0, coeff['m'] * math.log10(energy) + coeff['t'])
  1274 +
  1275 + # Return value
  1276 + return value
  1277 +
  1278 + def _infill_bgd(self, hist2D):
  1279 + """
  1280 + Fill all empty bins in 2D histogram by preceeding values in energy
  1281 +
  1282 + Parameters
  1283 + ----------
  1284 + hist2D : `~ROOT.TH2F`
  1285 + ROOT 2D histogram.
  1286 + """
  1287 + # Extract energy and offset angle vectors
  1288 + energies = hist2D.GetXaxis()
  1289 + neng = energies.GetNbins()
  1290 + offsets = hist2D.GetYaxis()
  1291 + noffset = offsets.GetNbins()
  1292 +
  1293 + # Determine mean energy values (for logging)
  1294 + engs = []
  1295 + for ieng in range(neng):
  1296 + energy = 0.5 * (math.pow(10.0, energies.GetBinLowEdge(ieng+1)) +
  1297 + math.pow(10.0, energies.GetBinUpEdge(ieng+1)))
  1298 + engs.append(energy)
  1299 +
  1300 + # Loop over all offaxis angles
  1301 + for ioff in range(noffset):
  1302 +
  1303 + # Determine offset angle (for logging)
  1304 + offset = 0.5 * (offsets.GetBinLowEdge(ioff+1) +
  1305 + offsets.GetBinUpEdge(ioff+1))
  1306 +
  1307 + # Initialise indices and value
  1308 + i_start = -1
  1309 + i_stop = -1
  1310 + value = 0.0
  1311 +
  1312 + # First start and stop indices for infill
  1313 + for ieng in range(neng):
  1314 + if hist2D.GetBinContent(ieng+1,ioff+1) > 0.0:
  1315 + i_start = ieng
  1316 + value = hist2D.GetBinContent(ieng+1,ioff+1)
  1317 + break
  1318 + for ieng in range(neng-1, -1, -1):
  1319 + if hist2D.GetBinContent(ieng+1,ioff+1) > 0.0:
  1320 + i_stop = ieng
  1321 + break
  1322 +
  1323 + # If indices are valid then fill in background rates
  1324 + if i_start > -1 and i_stop > -1:
  1325 + for ieng in range(i_start, i_stop+1):
  1326 + if hist2D.GetBinContent(ieng+1,ioff+1) == 0.0:
  1327 + hist2D.SetBinContent(ieng+1,ioff+1,value)
  1328 + if self._logTerse():
  1329 + self._log(gammalib.parformat('Background infill'))
  1330 + self._log('%e cts/s/deg2 (E=%8.4f TeV (%d) '
  1331 + 'Off=%.2f deg (%d)\n' %
  1332 + (value,engs[ieng],ieng,offset,ioff))
  1333 + else:
  1334 + value = hist2D.GetBinContent(ieng+1,ioff+1)
  1335 +
  1336 + # Return
  1337 + return
  1338 +
1090 1339 def _make_2D(self, array, hdu, name, unit, scale=1.0):
1091 1340 """
1092 1341 Make 2D matrix as function of energy and offset angle from a
... ... @@ -1162,7 +1411,6 @@ class csroot2caldb(ctools.cscript):
1162 1411 # Get User parameters
1163 1412 scale = self['bgdscale'].real()
1164 1413 oversample = self['bgdoversample'].integer()
1165   - infill = self['bgdinfill'].boolean()
1166 1414  
1167 1415 # Set constants
1168 1416 deg2sr = 0.01745329*0.01745329
... ... @@ -1178,32 +1426,6 @@ class csroot2caldb(ctools.cscript):
1178 1426 ewidth.append(pow(10.0, energies.GetBinUpEdge(ieng+1) +6.0) -
1179 1427 pow(10.0, energies.GetBinLowEdge(ieng+1)+6.0))
1180 1428  
1181   - # Optionally fill all empty bins by the preceding value that occur
1182   - # for all offset angles between the first and the last non zero
1183   - # bin
1184   - if infill:
1185   - for ioff in range(noffset):
1186   - i_start = -1
1187   - i_stop = -1
1188   - value = 0.0
1189   - for ieng in range(neng):
1190   - if array.GetBinContent(ieng+1,ioff+1) > 0.0:
1191   - i_start = ieng
1192   - value = array.GetBinContent(ieng+1,ioff+1)
1193   - break
1194   - for ieng in range(neng-1, -1, -1):
1195   - if array.GetBinContent(ieng+1,ioff+1) > 0.0:
1196   - i_stop = ieng
1197   - break
1198   - if i_start > -1 and i_stop > -1:
1199   - for ieng in range(i_start, i_stop+1):
1200   - if array.GetBinContent(ieng+1,ioff+1) == 0.0:
1201   - array.SetBinContent(ieng+1,ioff+1,value)
1202   - print('Background infill ieng=%d ioff=%d value=%f' %
1203   - (ieng,ioff,value))
1204   - else:
1205   - value = array.GetBinContent(ieng+1,ioff+1)
1206   -
1207 1429 # Build DETX and DETY axes
1208 1430 ndet = array.GetYaxis().GetNbins()
1209 1431 ndets = 2*ndet*oversample
... ...