26 from cscripts
import mputils
36 This class implements the generation of a Spectral Energy Distribution
37 (SED) from gamma-ray observations.
46 self._init_csobservation(self.__class__.__name__, ctools.__version__, argv)
69 Extend ctools.csobservation getstate method to include some members
77 state = {
'base' : ctools.csobservation.__getstate__(self),
90 Extend ctools.csobservation setstate method to include some members
97 ctools.csobservation.__setstate__(self, state[
'base'])
99 self.
_fits = state[
'fits']
112 Get parameters from parfile and setup the observation
115 if self.obs().is_empty():
116 self._require_inobs(
'csspec::get_parameters()')
117 self.obs(self._get_observations())
120 self._set_obs_statistic(gammalib.toupper(self[
'statistic'].string()))
123 if self.obs().
models().is_empty():
124 self.obs().
models(self[
'inmodel'].filename())
127 self[
'srcname'].string()
130 self.
_method = self[
'method'].string()
137 for obs
in self.obs():
138 if obs.classname() ==
'GCTAObservation':
139 if obs.eventtype() ==
'CountsCube':
143 elif obs.classname() ==
'GCTAOnOffObservation':
145 n_cta = n_unbinned + n_binned + n_onoff
146 n_other = self.obs().size() - n_cta
154 if n_unbinned == 0
and n_binned != 0
and n_onoff == 0:
157 elif n_unbinned == 0
and n_binned == 0
and n_onoff != 0:
160 elif n_unbinned == 0
and n_binned != 0
and n_onoff != 0:
161 msg =
'Mix of binned and On/Off CTA observations found ' \
162 'in observation container. csscript does not support ' \
164 raise RuntimeError(msg)
165 elif n_unbinned != 0
and (n_binned != 0
or n_onoff != 0):
166 msg =
'Mix of unbinned and binned or On/Off CTA observations ' \
167 'found in observation container. csscript does not ' \
169 raise RuntimeError(msg)
170 elif n_unbinned != 0:
177 self[
'edisp'].boolean()
178 self[
'calc_ts'].boolean()
179 self[
'calc_ulim'].boolean()
180 self[
'confidence'].real()
181 self[
'fix_bkg'].boolean()
182 self[
'fix_srcs'].boolean()
183 self[
'bingamma'].real()
186 self[
'dll_sigstep'].real()
187 self[
'dll_sigmax'].real()
188 self[
'dll_freenodes'].boolean()
191 if self._read_ahead():
192 self[
'outfile'].filename()
195 self._log_parameters(gammalib.TERSE)
201 self._log_header1(gammalib.TERSE,
'Spectrum method')
202 self._log_value(gammalib.TERSE,
'Unbinned CTA observations', n_unbinned)
203 self._log_value(gammalib.TERSE,
'Binned CTA observations', n_binned)
204 self._log_value(gammalib.TERSE,
'On/off CTA observations', n_onoff)
205 self._log_value(gammalib.TERSE,
'Other observations', n_other)
211 if n_cta > 0
and n_other > 0
and self.
_method ==
'SLICE':
218 msg =
'Selected "SLICE" method but none of the observations ' \
219 'is a CTA observation. Please select "AUTO", "NODES" ' \
220 'or "BINS" if no CTA observation is provided.'
221 raise RuntimeError(msg)
226 self._log_value(gammalib.TERSE,
'Selected spectrum method', self.
_method)
230 self._log_string(gammalib.TERSE,
' WARNING: Only CTA observation '
231 'can be handled with the "SLICE" method, all '
232 'non-CTA observation will be ignored.')
239 Set energy boundaries
249 ebounds = self._create_ebounds()
257 cube_ebounds = self.obs()[0].events().ebounds()
259 cube_ebounds = self.obs()[0].rmf().emeasured()
263 for i
in range(ebounds.size()):
267 emin = ebounds.emin(i).TeV() * 0.999
268 emax = ebounds.emax(i).TeV() * 1.001
276 for k
in range(cube_ebounds.size()):
277 if cube_ebounds.emin(k).TeV() >= emin
and \
278 cube_ebounds.emax(k).TeV() <= emax:
279 emin_value = cube_ebounds.emin(k).TeV()
285 for k
in range(cube_ebounds.size()):
286 if cube_ebounds.emin(k).TeV() >= emin
and \
287 cube_ebounds.emax(k).TeV() <= emax:
288 emax_value = cube_ebounds.emax(k).TeV()
294 self._ebounds.append(gammalib.GEnergy(emin_value,
'TeV'),
295 gammalib.GEnergy(emax_value,
'TeV'))
299 msg =
'Energy range ['+str(cube_ebounds.emin())+ \
300 ', '+str(cube_ebounds.emax())+
'] of counts '+ \
301 'cube does not overlap with specified energy '+ \
303 str(ebounds.emin())+
', '+str(ebounds.emax())+
'].'+ \
304 ' Specify overlapping energy range.'
305 raise RuntimeError(msg)
309 self.
_ebounds = self._create_ebounds()
319 self._log_header1(gammalib.TERSE,
'Spectral binning')
323 cube_ebounds = self.obs()[0].events().ebounds()
324 value =
'%s - %s' % (str(cube_ebounds.emin()),
325 str(cube_ebounds.emax()))
326 self._log_value(gammalib.TERSE,
'Counts cube energy range', value)
330 etrue = self.obs()[0].rmf().etrue()
331 ereco = self.obs()[0].rmf().emeasured()
332 vtrue =
'%s - %s' % (str(etrue.emin()), str(etrue.emax()))
333 vreco =
'%s - %s' % (str(ereco.emin()), str(ereco.emax()))
334 self._log_value(gammalib.TERSE,
'RMF true energy range', vtrue)
335 self._log_value(gammalib.TERSE,
'RMF measured energy range', vreco)
338 for i
in range(self._ebounds.size()):
339 value =
'%s - %s' % (str(self._ebounds.emin(i)),
340 str(self._ebounds.emax(i)))
341 self._log_value(gammalib.TERSE,
'Bin %d' % (i+1), value)
348 Adjust model parameters
351 self._log_header1(gammalib.TERSE,
'Adjust model parameters')
354 for model
in self.obs().
models():
360 self._log_header3(gammalib.EXPLICIT, model.name())
363 if model.name() == self[
'srcname'].string():
368 self._log_string(gammalib.EXPLICIT,
369 ' Fixing "'+par.name()+
'"')
375 nbins = self._ebounds.size()
377 energies = gammalib.GEnergies(num, self._ebounds.emin(0),
378 self._ebounds.emax(nbins-1))
379 spectral = gammalib.GModelSpectralFunc(model.spectral(), energies)
380 model.spectral(spectral)
381 self._log_string(gammalib.EXPLICIT,
' Converting spectral model '
382 'into file function')
386 normpar = model.spectral()[0]
387 if normpar.is_fixed():
388 self._log_string(gammalib.EXPLICIT,
389 ' Freeing "'+normpar.name()+
'"')
393 if self[
'calc_ts'].boolean():
397 elif self[
'fix_bkg'].boolean()
and \
398 not model.classname() ==
'GModelSky':
401 self._log_string(gammalib.EXPLICIT,
402 ' Fixing "'+par.name()+
'"')
406 elif self[
'fix_srcs'].boolean()
and \
407 model.classname() ==
'GModelSky':
410 self._log_string(gammalib.EXPLICIT,
411 ' Fixing "'+par.name()+
'"')
419 Replace source spectrum by node function
422 models = gammalib.GModels()
425 for model
in self.obs().
models():
429 if model.name() == self[
'srcname'].string():
432 energies = gammalib.GEnergies()
433 for i
in range(self._ebounds.size()):
434 energies.append(self._ebounds.elogmean(i))
437 spectrum = gammalib.GModelSpectralNodes(model.spectral(), energies)
442 for i
in range(spectrum.nodes()):
443 par = spectrum[i*2+1]
446 minimum = 1.0e-20 * value
455 model.spectral(spectrum)
476 Replace source spectrum by bin function
479 models = gammalib.GModels()
482 for model
in self.obs().
models():
486 if model.name() == self[
'srcname'].string():
489 bingamma = self[
'bingamma'].real()
492 spectrum = gammalib.GModelSpectralBins(model.spectral(),
499 for i
in range(spectrum.bins()):
500 name =
'Intensity%d' % i
504 minimum = 1.0e-20 * value
513 model.spectral(spectrum)
534 Select an energy interval from one CTA On/Off observation
538 obs : `~gammalib.GCTAOnOffObservation`
540 emin : `~gammalib.GEnergy()`
542 emax : `~gammalib.GEnergy()`
547 obs : `~gammalib.GCTAOnOffObservation`
548 CTA On/Off observation
553 etrue = obs.rmf().etrue()
554 ereco = gammalib.GEbounds()
555 itrue = [i
for i
in range(obs.rmf().etrue().size())]
557 for i
in range(obs.rmf().emeasured().size()):
558 ereco_bin_min = obs.rmf().emeasured().emin(i)
559 ereco_bin_max = obs.rmf().emeasured().emax(i)
560 if ereco_bin_min * 1.001 >= emin
and ereco_bin_max * 0.999 <= emax:
561 ereco.append(ereco_bin_min, ereco_bin_max)
565 pha_on = gammalib.GPha(ereco)
566 pha_off = gammalib.GPha(ereco)
567 pha_on.exposure(obs.on_spec().exposure())
568 pha_off.exposure(obs.on_spec().exposure())
569 for idst, isrc
in enumerate(ireco):
571 pha_on[idst] = obs.on_spec()[isrc]
572 pha_on.areascal(idst, obs.on_spec().areascal(isrc))
573 pha_on.backscal(idst, obs.on_spec().backscal(isrc))
575 pha_off[idst] = obs.off_spec()[isrc]
576 pha_off.areascal(idst, obs.off_spec().areascal(isrc))
577 pha_off.backscal(idst, obs.off_spec().backscal(isrc))
580 pha_backresp = obs.off_spec()[
'BACKRESP']
582 for idst, isrc
in enumerate(ireco):
583 backresp.append(pha_backresp[isrc])
584 pha_off.append(
'BACKRESP', backresp)
587 arf = gammalib.GArf(etrue)
588 for idst, isrc
in enumerate(itrue):
589 arf[idst] = obs.arf()[isrc]
592 rmf = gammalib.GRmf(etrue, ereco)
593 for idst_true, isrc_true
in enumerate(itrue):
594 for idst_reco, isrc_reco
in enumerate(ireco):
595 rmf[idst_true, idst_reco] = obs.rmf()[isrc_true, isrc_reco]
599 statistic = obs.statistic()
600 instrument = obs.instrument()
601 obs = gammalib.GCTAOnOffObservation(pha_on, pha_off, arf, rmf)
603 obs.statistic(statistic)
604 obs.instrument(instrument)
611 Select observations for energy interval
615 emin : `~gammalib.GEnergy()`
617 emax : `~gammalib.GEnergy()`
622 obs : `~gammalib.GObservations`
623 Observation container
629 self._log_header3(gammalib.EXPLICIT,
'Filtering cube')
632 cubemask = ctools.ctcubemask(self.obs())
633 cubemask[
'regfile'] =
'NONE'
634 cubemask[
'ra'] =
'UNDEFINED'
635 cubemask[
'dec'] =
'UNDEFINED'
636 cubemask[
'rad'] =
'UNDEFINED'
637 cubemask[
'emin'] = emin.TeV()
638 cubemask[
'emax'] = emax.TeV()
642 if self._logVerbose()
and self._logDebug():
643 cubemask[
'debug'] =
True
649 obs = cubemask.obs().copy()
655 self._log_header3(gammalib.EXPLICIT,
'Filtering PHA, ARF and RMF')
658 obs = gammalib.GObservations()
662 for run
in self.obs():
663 if run.classname() ==
'GCTAOnOffObservation':
667 obs.models(self.obs().
models())
673 self._log_header3(gammalib.EXPLICIT,
'Selecting events')
676 select = ctools.ctselect(self.obs())
677 select[
'ra'] =
'UNDEFINED'
678 select[
'dec'] =
'UNDEFINED'
679 select[
'rad'] =
'UNDEFINED'
680 select[
'emin'] = emin.TeV()
681 select[
'emax'] = emax.TeV()
682 select[
'tmin'] =
'UNDEFINED'
683 select[
'tmax'] =
'UNDEFINED'
687 if self._logVerbose()
and self._logDebug():
688 select[
'debug'] =
True
694 obs = select.obs().copy()
701 Fit data for one energy bin
711 Dictionary with fit results
715 self._log_header2(gammalib.EXPLICIT,
'Energy bin ' + str(i + 1))
718 emin = self._ebounds.emin(i)
719 emax = self._ebounds.emax(i)
720 elogmean = self._ebounds.elogmean(i)
721 e_scale = elogmean.MeV() * elogmean.MeV() * gammalib.MeV2erg
727 result = {
'energy': elogmean.TeV(),
728 'energy_low': (elogmean - emin).TeV(),
729 'energy_high': (emax - elogmean).TeV(),
741 self._log_header3(gammalib.EXPLICIT,
'Performing fit in energy bin')
744 like = ctools.ctlike(obs)
745 like[
'edisp'] = self[
'edisp'].boolean()
750 if self._logVerbose()
and self._logDebug():
757 self._log_string(gammalib.EXPLICIT, str(like.obs().
models()))
760 if like.obs().logL() != 0.0:
763 fitted_models = like.obs().
models()
764 source = fitted_models[self[
'srcname'].string()]
767 if self[
'dll_sigstep'].real() > 0.0:
770 parname = source.spectral()[0].name()
771 (norm_scan, dlogL, loglike) = self.
_profile_logL(like.copy(), parname, elogmean)
772 result[
'norm_scan'] = norm_scan
773 result[
'dloglike'] = dlogL
774 result[
'logL'] = loglike
777 if self[
'calc_ts'].boolean():
778 result[
'TS'] = source.ts()
782 for observation
in like.obs():
783 result[
'Npred'] += observation.npred(source)
787 if self[
'calc_ulim'].boolean():
790 self._log_header3(gammalib.EXPLICIT,
791 'Computing upper limit for energy bin')
794 ulimit = ctools.ctulimit(like.obs())
795 ulimit[
'srcname'] = self[
'srcname'].string()
796 ulimit[
'eref'] = elogmean.TeV()
797 ulimit[
'confidence'] = self[
'confidence'].real()
801 if self._logVerbose()
and self._logDebug():
802 ulimit[
'debug'] =
True
807 ulimit_value = ulimit.diff_ulimit()
809 self._log_string(gammalib.EXPLICIT,
'Upper limit '
810 'calculation failed.')
814 if ulimit_value > 0.0:
815 result[
'ulimit'] = ulimit_value
818 fitted_flux = source.spectral().eval(elogmean)
819 parvalue = source.spectral()[0].value()
821 rel_error = source.spectral()[0].error() / parvalue
822 e_flux = fitted_flux * rel_error
828 if source.spatial().classname() ==
'GModelSpatialDiffuseCube':
829 region = gammalib.GSkyRegionCircle(0.0, 0.0, 180.0)
830 source.spatial().mc_cone(region)
831 norm = source.spatial().
spectrum().eval(elogmean)
836 result[
'flux'] = fitted_flux
837 result[
'e2dnde'] = fitted_flux * e_scale
838 result[
'flux_err'] = e_flux
841 value =
'%e +/- %e' % (result[
'e2dnde'], result[
'flux_err']*e_scale)
842 if self[
'calc_ulim'].boolean()
and result[
'ulimit'] > 0.0:
843 value +=
' [< %e]' % (result[
'ulimit']*e_scale)
844 value +=
' erg/cm2/s'
845 if self[
'calc_ts'].boolean()
and result[
'TS'] > 0.0:
846 value +=
' (TS = %.3f)' % (result[
'TS'])
847 self._log_value(gammalib.TERSE,
'Bin '+str(i+1), value)
852 value =
'Likelihood is zero. Bin is skipped.'
853 self._log_value(gammalib.TERSE,
'Bin '+str(i+1), value)
860 Fit model to energy bins
864 results : list of dict
865 List of dictionaries with fit results
868 self._log_header1(gammalib.TERSE,
'Generate spectrum')
869 self._log_string(gammalib.TERSE, str(self.
_ebounds))
878 args = [(self,
'_fit_energy_bin', i)
879 for i
in range(self._ebounds.size())]
880 poolresults = mputils.process(self.
_nthreads, mputils.mpfunc, args)
883 for i
in range(self._ebounds.size()):
884 results.append(poolresults[i][0])
885 self._log_string(gammalib.TERSE, poolresults[i][1][
'log'],
False)
889 for i
in range(self._ebounds.size()):
895 results.append(result)
902 Fit model to observations
906 results : list of dict
907 List of dictionaries with fit results
910 self._log_header1(gammalib.TERSE,
'Generate spectrum')
913 self._log_header3(gammalib.EXPLICIT,
'Performing model fit')
916 like = ctools.ctlike(self.obs())
917 like[
'edisp'] = self[
'edisp'].boolean()
924 model = like.obs().
models()[self[
'srcname'].string()]
925 spectrum = model.spectral()
926 logL0 = like.obs().logL()
929 self._log_string(gammalib.EXPLICIT, str(like.obs().
models()))
932 for i
in range(self._ebounds.size()):
935 emin = self._ebounds.emin(i)
936 emax = self._ebounds.emax(i)
937 elogmean = self._ebounds.elogmean(i)
940 result = {
'energy': elogmean.TeV(),
941 'energy_low': (elogmean - emin).TeV(),
942 'energy_high': (emax - elogmean).TeV(),
954 norm = elogmean.MeV() * elogmean.MeV() * gammalib.MeV2erg
955 result[
'flux'] = spectrum.intensity(i)
956 result[
'e2dnde'] = spectrum.intensity(i) * norm
957 result[
'flux_err'] = spectrum.error(i)
961 parname =
'Intensity%d' % i
964 if self[
'dll_sigstep'].real() > 0.0:
967 (norm_scan, dlogL, loglike) = self.
_profile_logL(like.copy(),
970 result[
'norm_scan'] = norm_scan
971 result[
'dloglike'] = dlogL
972 result[
'logL'] = loglike
975 if self[
'calc_ulim'].boolean():
978 self._log_header3(gammalib.EXPLICIT,
979 'Computing upper limit for node energy %f TeV' %
983 obs = like.obs().copy()
986 spectral = obs.models()[self[
'srcname'].string()].spectral()
991 ulimit = ctools.ctulimit(obs)
992 ulimit[
'srcname'] = self[
'srcname'].string()
993 ulimit[
'parname'] = parname
994 ulimit[
'eref'] = elogmean.TeV()
995 ulimit[
'tol'] = 1.0e-3
1000 ulimit_value = ulimit.diff_ulimit()
1002 self._log_string(gammalib.EXPLICIT,
'Upper limit '
1003 'calculation failed.')
1007 if ulimit_value > 0.0:
1008 result[
'ulimit'] = ulimit_value
1011 if self[
'calc_ts'].boolean():
1014 obs = like.obs().copy()
1018 par = obs.models()[self[
'srcname'].string()].spectral()[parname]
1020 par.factor_min(1.0e-8)
1021 par.factor_value(1.0e-8)
1026 tslike = ctools.ctlike(obs)
1027 tslike[
'edisp'] = self[
'edisp'].boolean()
1031 model = tslike.obs().
models()[self[
'srcname'].string()]
1032 logL1 = tslike.obs().logL()
1033 result[
'TS'] = 2.0 * (logL1 - logL0)
1036 value =
'%e +/- %e' % (result[
'e2dnde'], result[
'flux_err']*norm)
1037 if self[
'calc_ulim'].boolean()
and result[
'ulimit'] > 0.0:
1038 value +=
' [< %e]' % (result[
'ulimit']*norm)
1039 value +=
' erg/cm2/s'
1040 if self[
'calc_ts'].boolean()
and result[
'TS'] > 0.0:
1041 value +=
' (TS = %.3f)' % (result[
'TS'])
1042 self._log_value(gammalib.TERSE,
'Bin '+str(i+1), value)
1045 results.append(result)
1052 Computes the delta log-likelihood profile in a single energy bin
1056 like : `~ctools.ctlike()`
1057 ctlike fitter containing prefit model
1059 Name of the spectral parameter to be fit
1060 elogmean : `~gammalib.GEnergy()`
1061 Energy at which the model is to be evaluated
1066 Normalization values
1067 dloglike_scan : list
1068 Computed loglikelihood values
1070 Computed reference log-likelihood for dloglike_scan
1073 dll_sigmax = self[
'dll_sigmax'].real()
1074 dll_sigstep = self[
'dll_sigstep'].real()
1075 sigsteps = int(2 * (dll_sigmax/dll_sigstep) + 1)
1078 source = like.obs().
models()[self[
'srcname'].string()]
1079 spectral = source.spectral()
1080 flux_par = spectral[parname]
1081 norm = flux_par.value()
1082 norm_err = flux_par.error()
1083 source.tscalc(
False)
1087 (
not self[
'dll_freenodes'].boolean()):
1088 for par
in spectral:
1093 loglike = like.obs().logL()
1100 ref_norm = spectral.eval(elogmean)
1103 log_norm = math.log10(norm)
1104 if (norm_err > 0.0)
and (norm > norm_err):
1105 log_step = log_norm - math.log10(norm-norm_err)
1106 log_start = log_norm - (sigsteps/2) * log_step
1110 log_start = log_norm
1111 if log_start > -24.0:
1113 log_step = (-14.0 - log_start) / (sigsteps-1)
1114 norm_vals = [10.0 ** (log_start + i*log_step)
for i
in range(sigsteps)]
1117 flux_par.factor_min(0.0)
1118 flux_par.factor_max(1e30)
1119 for new_norm
in norm_vals:
1120 flux_par.value(new_norm)
1126 dloglike.append(loglike - like.obs().logL())
1128 norm_scan.append(spectral.eval(elogmean))
1130 norm_scan.append(new_norm)
1133 return (norm_scan, dloglike, -loglike)
1141 results : list of dict
1142 List of result dictionaries
1145 nrows = self._ebounds.size()
1146 ncols = len(results[0][
'norm_scan'])
1147 energy = gammalib.GFitsTableDoubleCol(
'e_ref', nrows)
1148 energy_low = gammalib.GFitsTableDoubleCol(
'e_min', nrows)
1149 energy_high = gammalib.GFitsTableDoubleCol(
'e_max', nrows)
1150 norm = gammalib.GFitsTableDoubleCol(
'norm', nrows)
1151 norm_err = gammalib.GFitsTableDoubleCol(
'norm_err', nrows)
1152 norm_ul = gammalib.GFitsTableDoubleCol(
'norm_ul', nrows)
1153 e2dnde = gammalib.GFitsTableDoubleCol(
'ref_e2dnde', nrows)
1154 dnde = gammalib.GFitsTableDoubleCol(
'ref_dnde', nrows)
1155 Npred_values = gammalib.GFitsTableDoubleCol(
'ref_npred', nrows)
1156 TSvalues = gammalib.GFitsTableDoubleCol(
'ts', nrows)
1157 loglike = gammalib.GFitsTableDoubleCol(
'loglike', nrows)
1158 norm_scan = gammalib.GFitsTableDoubleCol(
'norm_scan', nrows, ncols)
1159 dloglike_scan= gammalib.GFitsTableDoubleCol(
'dloglike_scan', nrows, ncols)
1161 energy_low.unit(
'TeV')
1162 energy_high.unit(
'TeV')
1166 e2dnde.unit(
'erg/cm2/s')
1167 dnde.unit(
'counts/MeV/cm2/s')
1168 Npred_values.unit(
'counts')
1171 dloglike_scan.unit(
'')
1174 for i, result
in enumerate(results):
1175 energy[i] = result[
'energy']
1176 energy_low[i] = result[
'energy_low']
1177 energy_high[i] = result[
'energy_high']
1179 if result[
'flux'] != 0.0:
1180 norm_err[i] = result[
'flux_err'] / result[
'flux']
1181 norm_ul[i] = result[
'ulimit'] / result[
'flux']
1185 e2dnde[i] = result[
'e2dnde']
1186 dnde[i] = result[
'flux']
1187 Npred_values[i] = result[
'Npred']
1188 TSvalues[i] = result[
'TS']
1189 loglike[i] = result[
'logL']
1192 for fbin
in range(ncols):
1193 dloglike_scan[i,fbin] = result[
'dloglike'][fbin]
1194 if result[
'flux'] != 0.0:
1195 norm_scan[i,fbin] = result[
'norm_scan'][fbin] / result[
'flux']
1197 norm_scan[i,fbin] = 0.0
1200 table = gammalib.GFitsBinTable(nrows)
1201 table.extname(
'SPECTRUM')
1204 table.card(
'INSTRUME',
'CTA',
'Name of Instrument')
1205 table.card(
'TELESCOP',
'CTA',
'Name of Telescope')
1208 table.append(energy)
1209 table.append(energy_low)
1210 table.append(energy_high)
1212 table.append(norm_err)
1213 table.append(norm_ul)
1214 table.append(e2dnde)
1216 table.append(TSvalues)
1217 table.append(Npred_values)
1220 table.card(
'SED_TYPE',
'norm,e2dnde,dnde,npred',
'SED type')
1223 ulimit = ctools.ctulimit()
1224 table.card(
'UL_CONF', ulimit[
'confidence'].real(),
1225 'Confidence level of upper limits')
1229 table.card(
'SED_TYPE').value(
'likelihood,e2dnde,dnde,npred')
1230 table.append(loglike)
1231 table.append(norm_scan)
1232 table.append(dloglike_scan)
1238 table.card(
'OBJECT', self[
'srcname'].string(),
'Source name')
1239 table.card(
'METHOD', self.
_method,
'Spectrum generation method')
1241 table.card(
'BINGAMMA', self[
'bingamma'].real(),
'Spectral index for BINS method')
1242 table.card(
'STAT', self[
'statistic'].string(),
'Optimization statistic')
1243 table.card(
'EDISP', self[
'edisp'].boolean(),
'Use energy dispersion?')
1244 table.card(
'CALCULIM', self[
'calc_ulim'].boolean(),
'Upper limits computation')
1245 table.card(
'CALCTS', self[
'calc_ts'].boolean(),
'Test statistic computation')
1246 table.card(
'FIXBKG', self[
'fix_bkg'].boolean(),
'Fix background parameters')
1247 table.card(
'FIXSRCS', self[
'fix_srcs'].boolean(),
'Fix other source parameters')
1250 self.
_fits = gammalib.GFits()
1251 self._fits.append(table)
1266 self._log_observations(gammalib.NORMAL, self.obs(),
'Input observation')
1269 self._log_models(gammalib.VERBOSE, self.obs().
models(),
'Input model')
1290 self._log_models(gammalib.VERBOSE, self.obs().
models(),
1291 '"NODES" spectrum generation model')
1303 self._log_models(gammalib.VERBOSE, self.obs().
models(),
1304 '"BINS" spectrum generation model')
1313 if self[
'publish'].boolean():
1324 self._log_header1(gammalib.TERSE,
'Save spectrum')
1327 if self.
_fits !=
None:
1330 outfile = self[
'outfile'].filename()
1333 self._log_value(gammalib.NORMAL,
'Spectrum file', outfile.url())
1336 self._fits.saveto(outfile, self[
'clobber'].boolean())
1347 name : str, optional
1351 self._log_header1(gammalib.TERSE,
'Publish spectrum')
1354 if self.
_fits !=
None:
1357 if self._fits.contains(
'SPECTRUM'):
1361 user_name = self._name()
1366 self._log_value(gammalib.NORMAL,
'Spectrum name', user_name)
1369 self._fits.publish(
'SPECTRUM', user_name)
1376 Return spectrum FITS file
1379 FITS file containing spectrum
1389 self.obs().
models(models.clone())
1398 if __name__ ==
'__main__':
def _set_replace_src_spectrum_by_bins
def _set_replace_src_spectrum_by_nodes
def _log_spectral_binning