def _compute_ulimit(self, obs): """ Computes upper limit Parameters ---------- obs : `~gammalib.GObservations` Observation container Returns ------- ul_diff, ul_flux, ul_eflux : tuple of float Upper limits, -1.0 of not computed """ # Initialise upper flux limit ul_diff = -1.0 ul_flux = -1.0 ul_eflux = -1.0 # Perform computation only if requested if self['calc_ulim'].boolean(): # Write header in logger self._log_header3(gammalib.EXPLICIT, 'Computing upper limit') # Create copy of observations cpy_obs = obs.copy() # Fix parameters of source of interest in copy of observations. # This assures that the original spectral parameters and position # are used in the upper limit search. The ctulimit tool makes sure # that the flux parameter is freed when needed. source = cpy_obs.models()[self._srcname] for par in source: if par.is_free(): par.fix() # Create instance of upper limit tool ulimit = ctools.ctulimit(cpy_obs) ulimit['srcname'] = self._srcname ulimit['eref'] = 1.0 ulimit['emin'] = self['emin'].real() ulimit['emax'] = self['emax'].real() ulimit['sigma_min'] = 0.0 ulimit['sigma_max'] = 3.0 # Try to run upper limit tool and catch any exceptions try: ulimit.run() ul_diff = ulimit.diff_ulimit() ul_flux = ulimit.flux_ulimit() ul_eflux = ulimit.eflux_ulimit() except: self._log_string(gammalib.TERSE, 'Upper limit calculation failed.') ul_diff = -1.0 ul_flux = -1.0 ul_eflux = -1.0 # Return upper limit tuple return ul_diff, ul_flux, ul_eflux
def test_functional(self): """ Test cttsmap functionnality. """ # Set-up cttsmap ulimit = ctools.ctulimit() ulimit["inobs"].filename(self.events_name) ulimit["inmodel"].filename(self.model_name) ulimit["srcname"].string("Crab") ulimit["caldb"].string(self.caldb) ulimit["irf"].string(self.irf) # Run tool self.test_try("Run ctulimit") try: ulimit.run() self.test_try_success() except: self.test_try_failure("Exception occured in ctulimit.") # Save results self.test_try("Save results") try: ulimit.save() self.test_try_success() except: self.test_try_failure("Exception occured in saving results.") # Return return
def _test_python(self): """ Test ctulimit from Python """ # Set-up ctulimit ulimit = ctools.ctulimit() ulimit['inobs'] = self._events ulimit['inmodel'] = self._model ulimit['srcname'] = 'Crab' ulimit['caldb'] = self._caldb ulimit['irf'] = self._irf ulimit['logfile'] = 'ctulimit_py1.log' ulimit['chatter'] = 2 # Run ctulimit tool ulimit.logFileOpen() # Make sure we get a log file ulimit.run() ulimit.save() # Check result file self._check_result_file('ctulimit_py1.log') # Return return
def _compute_ulimit(self, obs): """ Computes upper flux limit. Args: obs: Observation container. Returns: Upper flux limit (-1 of not computed). """ # Initialise upper flux limit ulimit_value = -1.0 # Perform computation only if requested if self["calc_ulim"].boolean(): # Write header in logger if self._logExplicit(): self._log.header3("Computing upper flux limit") # Create upper limit object ulimit = ctools.ctulimit(obs) ulimit["srcname"] = self._srcname ulimit["eref"] = 1.0 # Try to run upper limit and catch exceptions try: ulimit.run() ulimit_value = ulimit.flux_ulimit() except: if self._logTerse(): self._log("Upper limit flux calculation failed.s\n") ulimit_value = -1.0 # Return upper limit return ulimit_value
def _fit_model(self): """ Fit model to observations Returns ------- results : list of dict List of dictionaries with fit results """ # Write header self._log_header1(gammalib.TERSE, 'Generate spectrum') # Write header for fitting self._log_header3(gammalib.EXPLICIT, 'Performing model fit') # Perform maximum likelihood fit like = ctools.ctlike(self.obs()) like['edisp'] = self['edisp'].boolean() like.run() # Initialise fit results results = [] # Extract fit results model = like.obs().models()[self['srcname'].string()] spectrum = model.spectral() logL0 = like.obs().logL() # Write model results for explicit chatter level self._log_string(gammalib.EXPLICIT, str(like.obs().models())) # Loop over all nodes for i in range(spectrum.nodes()): # Get energy boundaries emin = self._ebounds.emin(i) emax = self._ebounds.emax(i) elogmean = self._ebounds.elogmean(i) # Initialise dictionary result = { 'energy': elogmean.TeV(), 'energy_low': (elogmean - emin).TeV(), 'energy_high': (emax - elogmean).TeV(), 'flux': 0.0, 'flux_err': 0.0, 'TS': 0.0, 'ulimit': 0.0, 'Npred': 0.0 } # Convert differential flux and flux error to nuFnu norm = elogmean.MeV() * elogmean.MeV() * gammalib.MeV2erg result['flux'] = spectrum[i * 2 + 1].value() * norm result['flux_err'] = spectrum[i * 2 + 1].error() * norm # Compute upper flux limit ulimit_value = -1.0 if self['calc_ulim'].boolean(): # Logging information self._log_header3( gammalib.EXPLICIT, 'Computing upper limit for node energy %f TeV' % result['energy']) # Copy observation container obs = like.obs().copy() # Fix intensities of all nodes spectral = obs.models()[self['srcname'].string()].spectral() for par in spectral: par.fix() # Create upper limit object ulimit = ctools.ctulimit(obs) ulimit['srcname'] = self['srcname'].string() ulimit['parname'] = 'Intensity%d' % i ulimit['eref'] = elogmean.TeV() ulimit['tol'] = 1.0e-3 # Try to run upper limit and catch exceptions try: ulimit.run() ulimit_value = ulimit.diff_ulimit() except: self._log_string(gammalib.EXPLICIT, 'Upper limit ' 'calculation failed.') ulimit_value = -1.0 # Compute upper limit if ulimit_value > 0.0: result['ulimit'] = ulimit_value * elogmean.MeV() * \ elogmean.MeV() * gammalib.MeV2erg # Compute TS if self['calc_ts'].boolean(): # Copy observation container obs = like.obs().copy() # Set intensity of node to tiny value by scaling the value # by a factor 1e-8. par = obs.models()[self['srcname'].string()].spectral()[i * 2 + 1] par.autoscale() par.factor_min(1.0e-8) par.factor_value(1.0e-8) par.autoscale() par.fix() # Perform maximum likelihood fit tslike = ctools.ctlike(obs) tslike['edisp'] = self['edisp'].boolean() tslike.run() # Store Test Statistic model = tslike.obs().models()[self['srcname'].string()] logL1 = tslike.obs().logL() result['TS'] = 2.0 * (logL1 - logL0) # Log information value = '%e +/- %e' % (result['flux'], result['flux_err']) if self['calc_ulim'].boolean() and result['ulimit'] > 0.0: value += ' [< %e]' % (result['ulimit']) value += ' erg/cm2/s' if self['calc_ts'].boolean() and result['TS'] > 0.0: value += ' (TS = %.3f)' % (result['TS']) self._log_value(gammalib.TERSE, 'Bin ' + str(i + 1), value) # Append results results.append(result) # Return results return results
def _fit_energy_bin(self, i): """ Fit data for one energy bin Parameters ---------- i : int Energy bin index Returns ------- result : dict Dictionary with fit results """ # Write header for energy bin self._log_header2(gammalib.EXPLICIT, 'Energy bin ' + str(i + 1)) # Get energy boundaries emin = self._ebounds.emin(i) emax = self._ebounds.emax(i) elogmean = self._ebounds.elogmean(i) # Select observations for energy bin obs = self._select_obs(emin, emax) # Initialise dictionary result = { 'energy': elogmean.TeV(), 'energy_low': (elogmean - emin).TeV(), 'energy_high': (emax - elogmean).TeV(), 'flux': 0.0, 'flux_err': 0.0, 'TS': 0.0, 'ulimit': 0.0, 'Npred': 0.0 } # Write header for fitting self._log_header3(gammalib.EXPLICIT, 'Performing fit in energy bin') # Setup maximum likelihood fit like = ctools.ctlike(obs) like['edisp'] = self['edisp'].boolean() like['nthreads'] = 1 # Avoids OpenMP conflict # If chatter level is verbose and debugging is requested then # switch also on the debug model in ctlike if self._logVerbose() and self._logDebug(): like['debug'] = True # Perform maximum likelihood fit like.run() # Write model results for explicit chatter level self._log_string(gammalib.EXPLICIT, str(like.obs().models())) # Continue only if log-likelihood is non-zero if like.obs().logL() != 0.0: # Get results fitted_models = like.obs().models() source = fitted_models[self['srcname'].string()] # Extract Test Statistic value if self['calc_ts'].boolean(): result['TS'] = source.ts() # Compute Npred value (only works for unbinned analysis) if not self._binned_mode and not self._onoff_mode: for observation in like.obs(): result['Npred'] += observation.npred(source) # Compute upper flux limit ulimit_value = -1.0 if self['calc_ulim'].boolean(): # Logging information self._log_header3(gammalib.EXPLICIT, 'Computing upper limit for energy bin') # Create upper limit object ulimit = ctools.ctulimit(like.obs()) ulimit['srcname'] = self['srcname'].string() ulimit['eref'] = elogmean.TeV() # If chatter level is verbose and debugging is requested # then switch also on the debug model in ctulimit if self._logVerbose() and self._logDebug(): ulimit['debug'] = True # Try to run upper limit and catch exceptions try: ulimit.run() ulimit_value = ulimit.diff_ulimit() except: self._log_string(gammalib.EXPLICIT, 'Upper limit ' 'calculation failed.') ulimit_value = -1.0 # Compute upper limit if ulimit_value > 0.0: result['ulimit'] = ulimit_value * elogmean.MeV() * \ elogmean.MeV() * gammalib.MeV2erg # Compute differential flux and flux error fitted_flux = source.spectral().eval(elogmean) parvalue = source.spectral()[0].value() if parvalue != 0.0: rel_error = source.spectral()[0].error() / parvalue e_flux = fitted_flux * rel_error else: e_flux = 0.0 # If the source model is a cube then multiply-in the cube # spectrum if source.spatial().classname() == 'GModelSpatialDiffuseCube': dir = gammalib.GSkyDir() source.spatial().set_mc_cone(dir, 180.0) norm = source.spatial().spectrum().eval(elogmean) fitted_flux *= norm e_flux *= norm # Convert differential flux and flux error to nuFnu elogmean2 = elogmean.MeV() * elogmean.MeV() result['flux'] = fitted_flux * elogmean2 * gammalib.MeV2erg result['flux_err'] = e_flux * elogmean2 * gammalib.MeV2erg # Log information value = '%e +/- %e' % (result['flux'], result['flux_err']) if self['calc_ulim'].boolean() and result['ulimit'] > 0.0: value += ' [< %e]' % (result['ulimit']) value += ' erg/cm2/s' if self['calc_ts'].boolean() and result['TS'] > 0.0: value += ' (TS = %.3f)' % (result['TS']) self._log_value(gammalib.TERSE, 'Bin ' + str(i + 1), value) # ... otherwise if logL is zero then signal that bin is # skipped else: value = 'Likelihood is zero. Bin is skipped.' self._log_value(gammalib.TERSE, 'Bin ' + str(i + 1), value) # Return result return result
def _test_python(self): """ Test ctulimit from Python """ # Allocate ctulimit ulimit = ctools.ctulimit() # Check that empty ctulimit tool holds zero upper limits self.test_value(ulimit.diff_ulimit(), 0.0, 1.0e-21, 'Check differential upper limit') self.test_value(ulimit.flux_ulimit(), 0.0, 1.0e-16, 'Check upper limit on photon flux') self.test_value(ulimit.eflux_ulimit(), 0.0, 1.0e-16, 'Check upper limit on energy flux') # Check that saving does not nothing ulimit['logfile'] = 'ctulimit_py0.log' ulimit.logFileOpen() ulimit.save() # Check that clearing does not lead to an exception or segfault ulimit.clear() # Now set ctulimit parameters ulimit['inobs'] = self._events ulimit['inmodel'] = self._model ulimit['srcname'] = 'Crab' ulimit['caldb'] = self._caldb ulimit['irf'] = self._irf ulimit['tol'] = 0.1 ulimit['logfile'] = 'ctulimit_py1.log' ulimit['chatter'] = 2 # Run ctulimit tool ulimit.logFileOpen() # Make sure we get a log file ulimit.run() ulimit.save() # Set reference value ref_diff = 2.75359655874408e-17 ref_flux = 1.66942609234064e-11 ref_eflux = 6.4588860064421e-11 # Check results self.test_value(ulimit.diff_ulimit(), ref_diff, 1.0e-21, 'Check differential upper limit') self.test_value(ulimit.flux_ulimit(), ref_flux, 1.0e-16, 'Check upper limit on photon flux') self.test_value(ulimit.eflux_ulimit(), ref_eflux, 1.0e-16, 'Check upper limit on energy flux') # Check obs() method self.test_value(ulimit.obs().size(), 1, 'Check number of observations in container') # Check opt() method self.test_value(ulimit.opt().status(), 0, 'Check optimizer status') # Copy ctulimit tool cpy_ulimit = ulimit.copy() # Check results of copy self.test_value(cpy_ulimit.diff_ulimit(), ref_diff, 1.0e-21, 'Check differential upper limit') self.test_value(cpy_ulimit.flux_ulimit(), ref_flux, 1.0e-16, 'Check upper limit on photon flux') self.test_value(cpy_ulimit.eflux_ulimit(), ref_eflux, 1.0e-16, 'Check upper limit on energy flux') # Check results self.test_value(cpy_ulimit.diff_ulimit(), ref_diff, 1.0e-21, 'Check differential upper limit') self.test_value(cpy_ulimit.flux_ulimit(), ref_flux, 1.0e-16, 'Check upper limit on photon flux') self.test_value(cpy_ulimit.eflux_ulimit(), ref_eflux, 1.0e-16, 'Check upper limit on energy flux') # Now clear copy of ctulimit tool cpy_ulimit.clear() # Check that empty ctulimit tool holds zero upper limits self.test_value(cpy_ulimit.diff_ulimit(), 0.0, 1.0e-21, 'Check differential upper limit') self.test_value(cpy_ulimit.flux_ulimit(), 0.0, 1.0e-16, 'Check upper limit on photon flux') self.test_value(cpy_ulimit.eflux_ulimit(), 0.0, 1.0e-16, 'Check upper limit on energy flux') # Run ctlike to get an initial log-likelihood solution like = ctools.ctlike() like['inobs'] = self._events like['inmodel'] = self._model like['caldb'] = self._caldb like['irf'] = self._irf like.run() # Now set ctulimit tool using the observation container from the # previous run. This should avoid the necessity to recompute the # maximum likelihood ulimit = ctools.ctulimit(like.obs()) ulimit['srcname'] = 'Crab' ulimit['tol'] = 0.1 ulimit['logfile'] = 'ctulimit_py2.log' ulimit['chatter'] = 3 # Execute ctulimit tool ulimit.logFileOpen() # Needed to get a new log file ulimit.execute() # Check results self.test_value(ulimit.diff_ulimit(), ref_diff, 1.0e-21, 'Check differential upper limit') self.test_value(ulimit.flux_ulimit(), ref_flux, 1.0e-16, 'Check upper limit on photon flux') self.test_value(ulimit.eflux_ulimit(), ref_eflux, 1.0e-16, 'Check upper limit on energy flux') # Test invalid model name ulimit['srcname'] = 'Weihnachtsstern' ulimit['logfile'] = 'ctulimit_py3.log' ulimit.logFileOpen() self.test_try('Test invalid model name') try: ulimit.execute() self.test_try_failure('Exception not thrown') except ValueError: self.test_try_success() # Test specification of background model ulimit['srcname'] = 'Background' ulimit['logfile'] = 'ctulimit_py4.log' ulimit.logFileOpen() self.test_try('Test invalid model name') try: ulimit.execute() self.test_try_failure('Exception not thrown') except ValueError: self.test_try_success() # Test run with too few iterations ulimit['srcname'] = 'Crab' ulimit['max_iter'] = 1 ulimit['logfile'] = 'ctulimit_py5.log' ulimit.logFileOpen() self.test_try('Test ctulimit with too few iterations') try: ulimit.execute() self.test_try_failure('Exception not thrown') except ValueError: self.test_try_success() # Return return
'logs/enhist.png', ebins=60, title='Energy Histogram - %d Events' % nevents) printstat('pointing elevation minutes') runs = veripy.obs2runs(obs) db.plot_elev_bins(runs, 'logs/pelev.png', elmin=elmin - 0.5, elmax=elmax + 0.5, nelbins=80) printstat('starting ctulimit') eref = gammalib.GEnergy(10, 'TeV') t3 = time.time() ul = ctools.ctulimit(obs) ul['srcname'] = dmhalo ul['edisp'] = True ul['confidence'] = 0.68 ul['eref'] = eref.TeV() # reference energy for differential limit ul['emin'] = emin ul['emax'] = emax ul['tol'] = 1e-5 ul['max_iter'] = 50 ul['sigma_min'] = 0.0 ul['sigma_max'] = 10.0 ul['debug'] = True ul['chatter'] = 4 ul['logfile'] = 'logs/ctulimit.log' ul.logFileOpen() ul.run()
def run(self): """ Run the script. """ # Switch screen logging on in debug mode if self.logDebug(): self.log.cout(True) # Get parameters self.get_parameters() # Write input parameters into logger if self.logTerse(): self.log_parameters() self.log("\n") # Write observation into logger if self.logTerse(): self.log("\n") self.log.header1("Observation") self.log(str(self.obs)) self.log("\n") # Write header if self.logTerse(): self.log("\n") self.log.header1("Adjust model parameters") # Adjust model parameters dependent on input user parameters for model in self.obs.models(): # Set TS flag for all models to false. # Source of interest will be set to true later model.tscalc(False) # Log model name if self.logExplicit(): self.log.header3(model.name()) # Deal with the source of interest if model.name() == self.m_srcname: if self.m_calc_ts: model.tscalc(True) elif self.m_fix_bkg and not model.classname() == "GModelSky": for par in model: if par.is_free() and self.logExplicit(): self.log(" Fixing \""+par.name()+"\"\n") par.fix() elif self.m_fix_srcs and model.classname() == "GModelSky": for par in model: if par.is_free() and self.logExplicit(): self.log(" Fixing \""+par.name()+"\"\n") par.fix() # Write header if self.logTerse(): self.log("\n") self.log.header1("Generate lightcurve") # Initialise FITS Table with extension "LIGHTCURVE" table = gammalib.GFitsBinTable(self.m_tbins.size()) table.extname("LIGHTCURVE") # Add Header for compatibility with gammalib.GMWLSpectrum table.card("INSTRUME", "CTA", "Name of Instrument") table.card("TELESCOP", "CTA", "Name of Telescope") # Create FITS table columns MJD = gammalib.GFitsTableDoubleCol("MJD", self.m_tbins.size()) MJD.unit("days") e_MJD = gammalib.GFitsTableDoubleCol("e_MJD", self.m_tbins.size()) e_MJD.unit("days") # Create a FITS column for every free parameter columns = [] for par in self.obs.models()[self.m_srcname]: if par.is_free(): col = gammalib.GFitsTableDoubleCol(par.name(), self.m_tbins.size()) col.unit(par.unit()) columns.append(col) e_col = gammalib.GFitsTableDoubleCol("e_"+par.name(), self.m_tbins.size()) e_col.unit(par.unit()) columns.append(e_col) # Create TS and upper limit columns TSvalues = gammalib.GFitsTableDoubleCol("TS", self.m_tbins.size()) ulim_values = gammalib.GFitsTableDoubleCol("UpperLimit", self.m_tbins.size()) ulim_values.unit("ph/cm2/s") # Loop over energy bins for i in range(self.m_tbins.size()): # Log information if self.logTerse(): self.log("\n") self.log.header2("Time bin "+str(i)) # Get time boundaries tmin = self.m_tbins.tstart(i) tmax = self.m_tbins.tstop(i) # Compute time bin center and time width tmean = (tmin + tmax) tmean *= 0.5 twidth = (tmax - tmin) twidth *= 0.5 # Store time as MJD MJD[i] = tmean.mjd() e_MJD[i] = twidth.days() # Log information if self.logExplicit(): self.log.header3("Selecting events") # Select events select = ctools.ctselect(self.obs) select["emin"].real(self.m_emin) select["emax"].real(self.m_emax) select["tmin"].real(tmin.convert(select.time_reference())) select["tmax"].real(tmax.convert(select.time_reference())) select["rad"].value("UNDEFINED") select["ra"].value("UNDEFINED") select["dec"].value("UNDEFINED") select.run() # Retrieve observation obs = select.obs() # Binned analysis if self.m_binned: # Header if self.logTerse(): self.log.header3("Binning events") # Bin events bin = ctools.ctbin(select.obs()) bin["usepnt"].boolean(False) bin["ebinalg"].string("LOG") bin["xref"].real(self.m_xref) bin["yref"].real(self.m_yref) bin["binsz"].real(self.m_binsz) bin["nxpix"].integer(self.m_nxpix) bin["nypix"].integer(self.m_nypix) bin["enumbins"].integer(self.m_ebins) bin["emin"].real(self.m_emin) bin["emax"].real(self.m_emax) bin["coordsys"].string(self.m_coordsys) bin["proj"].string(self.m_proj) bin.run() # Header if self.logTerse(): self.log.header3("Creating exposure cube") # Create exposure cube expcube = ctools.ctexpcube(select.obs()) expcube["incube"].filename("NONE") expcube["usepnt"].boolean(False) expcube["ebinalg"].string("LOG") expcube["xref"].real(self.m_xref) expcube["yref"].real(self.m_yref) expcube["binsz"].real(self.m_binsz) expcube["nxpix"].integer(self.m_nxpix) expcube["nypix"].integer(self.m_nypix) expcube["enumbins"].integer(self.m_ebins) expcube["emin"].real(self.m_emin) expcube["emax"].real(self.m_emax) expcube["coordsys"].string(self.m_coordsys) expcube["proj"].string(self.m_proj) expcube.run() # Header if self.logTerse(): self.log.header3("Creating PSF cube") # Create psf cube psfcube = ctools.ctpsfcube(select.obs()) psfcube["incube"].filename("NONE") psfcube["usepnt"].boolean(False) psfcube["ebinalg"].string("LOG") psfcube["xref"].real(self.m_xref) psfcube["yref"].real(self.m_yref) psfcube["binsz"].real(self.m_binsz) psfcube["nxpix"].integer(self.m_nxpix) psfcube["nypix"].integer(self.m_nypix) psfcube["enumbins"].integer(self.m_ebins) psfcube["emin"].real(self.m_emin) psfcube["emax"].real(self.m_emax) psfcube["coordsys"].string(self.m_coordsys) psfcube["proj"].string(self.m_proj) psfcube.run() # Header if self.logTerse(): self.log.header3("Creating background cube") # Create background cube bkgcube = ctools.ctbkgcube(select.obs()) bkgcube["incube"].filename("NONE") bkgcube["usepnt"].boolean(False) bkgcube["ebinalg"].string("LOG") bkgcube["xref"].real(self.m_xref) bkgcube["yref"].real(self.m_yref) bkgcube["binsz"].real(self.m_binsz) bkgcube["nxpix"].integer(self.m_nxpix) bkgcube["nypix"].integer(self.m_nypix) bkgcube["enumbins"].integer(self.m_ebins) bkgcube["emin"].real(self.m_emin) bkgcube["emax"].real(self.m_emax) bkgcube["coordsys"].string(self.m_coordsys) bkgcube["proj"].string(self.m_proj) bkgcube.run() # Set new binned observation obs = bin.obs() # Set precomputed binned response obs[0].response(expcube.expcube(), psfcube.psfcube(), bkgcube.bkgcube()) # Get new models models = bkgcube.models() # Fix background models if required if self.m_fix_bkg: for model in models: if not model.classname() == "GModelSky": for par in model: par.fix() # Set new models to binned observation obs.models(models) # Header if self.logTerse(): self.log.header3("Performing fit") # Likelihood like = ctools.ctlike(obs) like.run() # Skip bin if no event was present if like.obs().logL() == 0.0: # Log information if self.logTerse(): self.log("No event in this time bin. Bin is skipped\n") # Set all values to 0 for col in columns: col[i] = 0.0 TSvalues[i] = 0.0 ulim_values[i] = 0.0 continue # Get results fitted_models = like.obs().models() source = fitted_models[self.m_srcname] # Calculate Upper Limit ulimit_value = -1.0 if self.m_calc_ulimit: # Logging information if self.logTerse(): self.log.header3("Computing upper limit") # Create upper limit object ulimit = ctools.ctulimit(like.obs()) ulimit["srcname"].string(self.m_srcname) ulimit["eref"].real(1.0) # Try to run upper limit and catch exceptions try: ulimit.run() ulimit_value = ulimit.flux_ulimit() except: if self.logTerse(): self.log("Upper limit calculation failed\n") ulimit_value = -1.0 # Get TS value TS = -1.0 if self.m_calc_ts: TS = source.ts() # Set values for storage TSvalues[i] = TS # Set FITS column values for col in columns: if "e_" == col.name()[:2]: col[i] = source.spectral()[col.name()[2:]].error() else: col[i] = source.spectral()[col.name()].value() # Store upper limit value if available if ulimit_value > 0.0: ulim_values[i] = ulimit_value # Log information if self.logExplicit(): self.log.header3("Results of bin "+str(i)+": MJD "+str(tmin.mjd())+"-"+str(tmax.mjd())) for col in columns: if "e_" == col.name()[:2]: continue value = source.spectral()[col.name()].value() error = source.spectral()[col.name()].error() unit = source.spectral()[col.name()].unit() self.log(" > "+col.name()+": "+str(value)+" +- "+str(error)+" "+unit+"\n") if self.m_calc_ts and TSvalues[i] > 0.0: self.log(" > TS = "+str(TS)+" \n") if self.m_calc_ulimit and ulim_values[i] > 0.0: self.log(" > UL = "+str(ulim_values[i])+" [ph/cm2/s]") self.log("\n") # Append filles columns to fits table table.append(MJD) table.append(e_MJD) for col in columns: table.append(col) table.append(TSvalues) table.append(ulim_values) # Create the FITS file now self.fits = gammalib.GFits() self.fits.append(table) # Return return
def _fit_model(self): """ Fit Model to DATA in the observation Return ------ Result , dictionary with relevant fit results """ # Set reference energy for calculations eref = gammalib.GEnergy(self['dmass'].real() / 2.0, 'TeV') # Get expected dmflux at reference energy # for the source of interest srcmodel = self.obs().models()[self['srcname'].string()] srcspec = srcmodel.spectral() theoflux = srcspec.eval(eref) # Header self._log_header1(gammalib.TERSE, 'Fitting DM Model') # So, at this moment interesting results to save are: # - Reference Energy # - Differential flux obtained in the fit # - Error # - TS # - Upper-limit on the differential flux # - Reference value of sigmav # - UL computed of sigmav # - Scale factor computed to obtain the UL on sigmav # This may be change when including Spectral class # for DM annihilation result = { 'energy': eref.TeV(), 'flux': 0.0, 'flux_err': 0.0, 'TS': 0.0, 'ulimit': 0.0, # 'sigma_ref' : self[ 'sigmav' ] , # 'sigma_lim' : 0.0 , 'sc_factor': 0.0 } # Header for ctlike instance :) self._log_header3(gammalib.EXPLICIT, 'ctlike instance') # Maximum likelihood fit via ctlike like = ctools.ctlike(self.obs()) like['edisp'] = self['edisp'].boolean() like['nthreads'] = 1 # Chatter if self._logVerbose() and self._logDebug(): like['debug'] = True like.run() # Extract fit results model = like.obs().models()[self['srcname'].string()] spectrum = model.spectral() logL0 = like.obs().logL() # Write models results self._log_string(gammalib.EXPLICIT, str(like.obs().models())) # Continue only if logL0 is different from zero if logL0 != 0.0: # Extract TS value result['TS'] = model.ts() # Calculation of upper-limit via ctulimit ulimit_value = -1.0 if self['calc_ulim'].boolean(): # Print to log self._log_header3(gammalib.EXPLICIT, 'Computing Upper Limit') # Instance for ctulimit ulimit = ctools.ctulimit(like.obs()) ulimit['srcname'] = self['srcname'].string() ulimit['eref'] = eref.TeV() # Set chatter if self._logVerbose() and self._logDebug(): ulimit['debug'] = True # Catching exceptions try: ulimit.run() ulimit_value = ulimit.diff_ulimit() except: self._log_string(gammalib.EXPLICIT, 'UL Calculation failed :(') ulimit_value = -1.0 # Compute quantities related to ulimit if ulimit_value > 0.0: result[ 'ulimit' ] = ulimit_value * eref.MeV() * \ eref.MeV() * gammalib.MeV2erg result['sc_factor'] = ulimit_value / theoflux # result[ 'sigma_lim' ] = ulimit_value / theoflux * \ # self[ 'sigmav' ] # Get flux and error fitted_flux = spectrum.eval(eref) parvalue = spectrum[0].value() if parvalue != 0.0: rel_error = spectrum[0].error() / parvalue e_flux = fitted_flux * rel_error else: e_flux = 0.0 # If a cube, then compute corresponding weight if model.spatial().classname() == 'GModelSpatialDiffuseCube': dir = gammalib.GSkyDir() model.spatial().set_mc_cone(dir, 180) norm = model.spatial().spectrum().eval(eref) fitted_flux *= norm e_flux *= norm # Convert to nuFnu eref2 = eref.MeV() * eref.MeV() result['flux'] = fitted_flux * eref2 * gammalib.MeV2erg result['flux_err'] = e_flux * eref2 * gammalib.MeV2erg # Logging value = '%e +/- %e' % (result['flux'], result['flux_err']) svmsg = '' if self['calc_ulim'].boolean() and result['ulimit'] > 0.0: value += ' [< %e]' % (result['ulimit']) svmsg += ' [%e]' % (result['sc_factor']) value += ' erg/cm**2/s' if self['calc_ts'].boolean() and result['TS'] > 0.0: value += ' (TS = %.3f)' % (result['TS']) self._log_value(gammalib.TERSE, 'Flux', value) if len(svmsg) > 0: self._log_value(gammalib.TERSE, 'ScaleFactor', svmsg) # If logL0 == 0, then failed :( # but, this does not raise any error else: value = 'Likelihood is zero. Something is weird. Check model' self._log_value(gammalib.TERSE, 'Warning: ', value) # Return # At this moment, only save for individual mass and channel # Later, include loop to compute over several masses and channels return result
model["Prefactor"].scale(1) model["Prefactor"].fix() model["Index"].value(p[1]) model["Index"].scale(-1) model["Index"].fix() model["PivotEnergy"].value(p[2]) model["PivotEnergy"].scale(1) model["PivotEnergy"].fix() ''' model = gammalib.GModelSky(spatial, spectral) model.name('DM') bkgmodel.append(model) bkgmodel.save("fitmodel.xml") ulimit = ctools.ctulimit() ulimit["inobs"] = cntcube ulimit["expcube"] = expcube ulimit["psfcube"] = psfcube ulimit["bkgcube"] = bkgcube ulimit["inmodel"] = "fitmodel.xml" ulimit["caldb"] = caldb_ ulimit["irf"] = irf_ ulimit["srcname"] = 'DM' ulimit["eref"] = eref ulimit["emin"] = emin ulimit["emax"] = emax ulimit["debug"] = True ulimit.execute() print >> f, mass, eref, ulimit.diff_ulimit(), ulimit.flux_ulimit(
def _fit_energy_bin(self, i ): """ Fit data for one energy bin Parameters ---------- i : int Energy bin index Returns ------- result : dict Dictionary with fit results """ # Write header for energy bin self._log_header2(gammalib.EXPLICIT, 'Energy bin ' + str(i + 1)) # Get energy boundaries emin = self._ebounds.emin(i) emax = self._ebounds.emax(i) elogmean = self._ebounds.elogmean(i) # Select observations for energy bin obs = self._select_obs(emin, emax) # Initialise dictionary result = {'energy': elogmean.TeV(), 'energy_low': (elogmean - emin).TeV(), 'energy_high': (emax - elogmean).TeV(), 'flux': 0.0, 'flux_err': 0.0, 'TS': 0.0, 'ulimit': 0.0, 'Npred': 0.0} # Write header for fitting self._log_header3(gammalib.EXPLICIT, 'Performing fit in energy bin') # Setup maximum likelihood fit like = ctools.ctlike(obs) like['edisp'] = self['edisp'].boolean() like['nthreads'] = 1 # Avoids OpenMP conflict # If chatter level is verbose and debugging is requested then # switch also on the debug model in ctlike if self._logVerbose() and self._logDebug(): like['debug'] = True # Perform maximum likelihood fit like.run() # plotfile = self[ 'srcname' ].string() + 'AfterLike{:d}.png'.format( i + 1 ) # ctamap( obs , 'CEL' , self[ 'srcname' ].string() , plotfile ) # Write model results for explicit chatter level self._log_string(gammalib.EXPLICIT, str(like.obs().models())) obs2 = obs.copy() obs2.models().remove( self[ 'srcname' ].string() ) likeNoSrc = ctools.ctlike( obs2 ) likeNoSrc[ 'edisp' ] = self[ 'edisp' ].boolean() likeNoSrc[ 'nthreads' ] = 1 likeNoSrc.run() logLNoSrc = likeNoSrc.obs().logL() # print( 'The LogL(No-Src) is {:.7e}'.format( logLNoSrc ) ) binNorm = obs.models()[ self[ 'srcname' ].string() ].spectral()[ 'Prefactor' ].value() scaleNorm = obs.models()[ self[ 'srcname' ].string() ].spectral()[ 'Prefactor' ].scale() binNorm = binNorm / scaleNorm logbinNorm = np.log10( binNorm ) lognorms = np.logspace( logbinNorm - 5 , logbinNorm + 5 , 10000 ) # Continue only if log-likelihood is non-zero logL0 = like.obs().logL() # print( 'The LogL(Src) is {:.7e}'.format( logL0 ) ) thisTS = [] NormsScan = [] if logL0 != 0.0: # Get results fitted_models = like.obs().models() source = fitted_models[self['srcname'].string()] # print( source ) # Extract Test Statistic value if self['calc_ts'].boolean(): result['TS'] = source.ts() for norm in lognorms : thisobs = like.obs().copy() pars = thisobs.models()[ self[ 'srcname' ].string() ].spectral()[ 'Prefactor' ] pars.factor_min( obs.models()[ self[ 'srcname' ].string() ].spectral()[ 'Prefactor' ].min() ) pars.factor_max( 1.0e+12 ) pars.factor_value( norm ) pars.fix() this_like = ctools.ctlike( thisobs ) this_like[ 'edisp' ] = self[ 'edisp' ].boolean() this_like.run() thismodel = this_like.obs().models()[ self[ 'srcname' ].string() ] thislogL = this_like.obs().logL() thisnorm = thismodel.spectral().eval( elogmean ) # print( thismodel.ts() ) # print( 'Norm {:.7e} with LogL {:.7e}'.format( thisnorm , thislogL ) ) ts = - ( source.ts() - thismodel.ts() ) if abs( ts ) <= 4.1 : thisTS.append( ts ) NormsScan.append( thisnorm * elogmean.MeV() * elogmean.MeV() * gammalib.MeV2erg ) data = np.array( ( NormsScan , thisTS ) ).transpose() filename = self[ 'outfile' ].filename().path() + self[ 'outfile' ].filename().file() filename = filename[ : -5 ] + 'ScanDataForBin{:d}.txt'.format( i + 1 ) np.savetxt( filename , data , fmt='%.5e\t%.5e' , header='Normalization\tTS' ) # Compute Npred value (only works for unbinned analysis) if not self._binned_mode and not self._onoff_mode: for observation in like.obs(): result['Npred'] += observation.npred(source) # Compute upper flux limit ulimit_value = -1.0 if self['calc_ulim'].boolean(): # Logging information self._log_header3(gammalib.EXPLICIT, 'Computing upper limit for energy bin') # Create upper limit object ulimit = ctools.ctulimit(like.obs()) ulimit['srcname'] = self['srcname'].string() ulimit['eref'] = elogmean.TeV() # If chatter level is verbose and debugging is requested # then switch also on the debug model in ctulimit if self._logVerbose() and self._logDebug(): ulimit['debug'] = True # Try to run upper limit and catch exceptions try: ulimit.run() ulimit_value = ulimit.diff_ulimit() except: self._log_string(gammalib.EXPLICIT, 'Upper limit ' 'calculation failed.') ulimit_value = -1.0 # Compute upper limit if ulimit_value > 0.0: result['ulimit'] = ulimit_value * elogmean.MeV() * \ elogmean.MeV() * gammalib.MeV2erg # Compute differential flux and flux error fitted_flux = source.spectral().eval(elogmean) parvalue = source.spectral()[0].value() if parvalue != 0.0: rel_error = source.spectral()[0].error() / parvalue e_flux = fitted_flux * rel_error else: e_flux = 0.0 # If the source model is a cube then multiply-in the cube # spectrum if source.spatial().classname() == 'GModelSpatialDiffuseCube': dir = gammalib.GSkyDir() source.spatial().set_mc_cone(dir, 180.0) norm = source.spatial().spectrum().eval(elogmean) fitted_flux *= norm e_flux *= norm # Convert differential flux and flux error to nuFnu elogmean2 = elogmean.MeV() * elogmean.MeV() result['flux'] = fitted_flux * elogmean2 * gammalib.MeV2erg result['flux_err'] = e_flux * elogmean2 * gammalib.MeV2erg # Log information value = '%e +/- %e' % (result['flux'], result['flux_err']) if self['calc_ulim'].boolean() and result['ulimit'] > 0.0: value += ' [< %e]' % (result['ulimit']) value += ' erg/cm2/s' if self['calc_ts'].boolean() and result['TS'] > 0.0: value += ' (TS = %.3f)' % (result['TS']) self._log_value(gammalib.TERSE, 'Bin '+str(i+1), value) # ... otherwise if logL is zero then signal that bin is # skipped else: value = 'Likelihood is zero. Bin is skipped.' self._log_value(gammalib.TERSE, 'Bin '+str(i+1), value) # Return result return result
def _fit_energy_bin(self, i): """ Fit data for one energy bin Parameters ---------- i : int Energy bin index Returns ------- result : dict Dictionary with fit results """ # Get energy boundaries emin = self._ebounds.emin(i) emax = self._ebounds.emax(i) elogmean = self._ebounds.elogmean(i) # Select observations for energy bin obs = self._select_obs(emin, emax) # Initialise dictionary result = {'energy': elogmean.TeV(), 'energy_low': (elogmean - emin).TeV(), 'energy_high': (emax - elogmean).TeV(), 'flux': 0.0, 'flux_err': 0.0, 'TS': 0.0, 'ulimit': 0.0, 'Npred': 0.0} # Write header for fitting self._log_header3(gammalib.EXPLICIT, 'Performing fit') # Perform maximum likelihood fit like = ctools.ctlike(obs) like['edisp'] = self['edisp'].boolean() like.run() # Continue only if log-likelihood is non-zero if like.obs().logL() != 0.0: # Get results fitted_models = like.obs().models() source = fitted_models[self['srcname'].string()] # Extract Test Statistic value if self['calc_ts'].boolean(): result['TS'] = source.ts() # Compute Npred value (only works for unbinned analysis) if not self._binned_mode and not self._onoff_mode: for observation in like.obs(): result['Npred'] += observation.npred(source) # Compute upper flux limit ulimit_value = -1.0 if self['calc_ulim'].boolean(): # Logging information self._log_header3(gammalib.EXPLICIT, 'Computing upper limit') # Create upper limit object ulimit = ctools.ctulimit(like.obs()) ulimit['srcname'] = self['srcname'].string() ulimit['eref'] = elogmean.TeV() # Try to run upper limit and catch exceptions try: ulimit.run() ulimit_value = ulimit.diff_ulimit() except: self._log_string(gammalib.EXPLICIT, 'Upper limit ' 'calculation failed.') ulimit_value = -1.0 # Compute upper limit if ulimit_value > 0.0: result['ulimit'] = ulimit_value * elogmean.MeV() * \ elogmean.MeV() * gammalib.MeV2erg # Compute differential flux and flux error fitted_flux = source.spectral().eval(elogmean) parvalue = source.spectral()[0].value() if parvalue != 0.0: rel_error = source.spectral()[0].error() / parvalue e_flux = fitted_flux * rel_error else: e_flux = 0.0 # Convert differential flux and flux error to nuFnu elogmean2 = elogmean.MeV() * elogmean.MeV() result['flux'] = fitted_flux * elogmean2 * gammalib.MeV2erg result['flux_err'] = e_flux * elogmean2 * gammalib.MeV2erg # Log information value = '%e +/- %e' % (result['flux'], result['flux_err']) if self['calc_ulim'].boolean() and result['ulimit'] > 0.0: value += ' [< %e]' % (result['ulimit']) value += ' erg/cm2/s' if self['calc_ts'].boolean() and result['TS'] > 0.0: value += ' (TS = %.3f)' % (result['TS']) self._log_value(gammalib.TERSE, 'Bin '+str(i+1), value) # ... otherwise if logL is zero then signal that bin is # skipped else: value = 'No event in this bin. Likelihood is zero. Bin is skipped.' self._log_value(gammalib.TERSE, 'Bin '+str(i+1), value) # Return result return result
def run(self): """ Run the script. """ # Switch screen logging on in debug mode if self.logDebug(): self.log.cout(True) # Get parameters self.get_parameters() # Write input parameters into logger if self.logTerse(): self.log_parameters() self.log("\n") # Write spectral binning into header if self.logTerse(): self.log("\n") self.log.header1("Spectral binning") if self.m_binned_mode: cube_ebounds = self.obs[0].events().ebounds() self.log.parformat("Counts cube energy range") self.log(str(cube_ebounds.emin())) self.log(" - ") self.log(str(cube_ebounds.emax())) self.log("\n") for i in range(self.m_ebounds.size()): self.log.parformat("Bin "+str(i+1)) self.log(str(self.m_ebounds.emin(i))) self.log(" - ") self.log(str(self.m_ebounds.emax(i))) self.log("\n") # Write observation into logger if self.logTerse(): self.log("\n") self.log.header1("Observation") self.log(str(self.obs)) self.log("\n") # Write header if self.logTerse(): self.log("\n") self.log.header1("Adjust model parameters") # Adjust model parameters dependent on input user parameters for model in self.obs.models(): # Set TS flag for all models to false. # Source of interest will be set to true later model.tscalc(False) # Log model name if self.logExplicit(): self.log.header3(model.name()) # Deal with the source of interest if model.name() == self.m_srcname: for par in model: if par.is_free() and self.logExplicit(): self.log(" Fixing \""+par.name()+"\"\n") par.fix() normpar = model.spectral()[0] if normpar.is_fixed() and self.logExplicit(): self.log(" Freeing \""+normpar.name()+"\"\n") normpar.free() if self.m_calc_ts: model.tscalc(True) elif self.m_fix_bkg and not model.classname() == "GModelSky": for par in model: if par.is_free() and self.logExplicit(): self.log(" Fixing \""+par.name()+"\"\n") par.fix() elif self.m_fix_srcs and model.classname() == "GModelSky": for par in model: if par.is_free() and self.logExplicit(): self.log(" Fixing \""+par.name()+"\"\n") par.fix() # Write header if self.logTerse(): self.log("\n") self.log.header1("Generate spectrum") self.log(str(self.m_ebounds)) # Initialise FITS Table with extension "SPECTRUM" table = gammalib.GFitsBinTable(self.m_ebounds.size()) table.extname("SPECTRUM") # Add Header for compatibility with gammalib.GMWLSpectrum table.card("INSTRUME", "CTA", "Name of Instrument") table.card("TELESCOP", "CTA", "Name of Telescope") # Create FITS table columns energy = gammalib.GFitsTableDoubleCol("Energy", self.m_ebounds.size()) energy_low = gammalib.GFitsTableDoubleCol("ed_Energy", self.m_ebounds.size()) energy_high = gammalib.GFitsTableDoubleCol("eu_Energy", self.m_ebounds.size()) flux = gammalib.GFitsTableDoubleCol("Flux", self.m_ebounds.size()) flux_err = gammalib.GFitsTableDoubleCol("e_Flux", self.m_ebounds.size()) TSvalues = gammalib.GFitsTableDoubleCol("TS", self.m_ebounds.size()) ulim_values = gammalib.GFitsTableDoubleCol("UpperLimit", self.m_ebounds.size()) Npred_values = gammalib.GFitsTableDoubleCol("Npred", self.m_ebounds.size()) energy.unit("TeV") energy_low.unit("TeV") energy_high.unit("TeV") flux.unit("erg/cm2/s") flux_err.unit("erg/cm2/s") ulim_values.unit("erg/cm2/s") # Loop over energy bins for i in range(self.m_ebounds.size()): # Log information if self.logExplicit(): self.log("\n") self.log.header2("Energy bin "+str(i+1)) # Get energy boundaries emin = self.m_ebounds.emin(i) emax = self.m_ebounds.emax(i) elogmean = self.m_ebounds.elogmean(i) elogmean2 = elogmean.MeV() * elogmean.MeV() # Store energy as TeV energy[i] = elogmean.TeV() # Store energy errors energy_low[i] = (elogmean - emin).TeV() energy_high[i] = (emax - elogmean).TeV() # use ctselect for unbinned analysis if not self.m_binned_mode: # Log information if self.logExplicit(): self.log.header3("Selecting events") # Select events select = ctools.ctselect(self.obs) select["emin"] = emin.TeV() select["emax"] = emax.TeV() select["tmin"] = "UNDEFINED" select["tmax"] = "UNDEFINED" select["rad"] = "UNDEFINED" select["ra"] = "UNDEFINED" select["dec"] = "UNDEFINED" select.run() # Retrieve observation obs = select.obs() # use ctcubemask for binned analysis else: # Header if self.logExplicit(): self.log.header3("Filtering cube") # Select layers cubemask = ctools.ctcubemask(self.obs) cubemask["regfile"] = "NONE" cubemask["ra"] = "UNDEFINED" cubemask["dec"] = "UNDEFINED" cubemask["rad"] = "UNDEFINED" cubemask["emin"] = emin.TeV() cubemask["emax"] = emax.TeV() cubemask.run() # Set new binned observation obs = cubemask.obs() # Header if self.logExplicit(): self.log.header3("Performing fit") # Likelihood like = ctools.ctlike(obs) like["edisp"] = self.m_edisp like.run() # Skip bin if no event was present if like.obs().logL() == 0.0: # Log information if self.logExplicit(): self.log("No event in this bin. ") self.log("Likelihood is zero. ") self.log("Bin is skipped.") # Set all values to 0 flux[i] = 0.0 flux_err[i] = 0.0 TSvalues[i] = 0.0 ulim_values[i] = 0.0 Npred_values[i] = 0.0 continue # Get results fitted_models = like.obs().models() source = fitted_models[self.m_srcname] # Calculate Upper Limit ulimit_value = -1.0 if self.m_calc_ulimit: # Logging information if self.logExplicit(): self.log.header3("Computing upper limit") # Create upper limit object ulimit = ctools.ctulimit(like.obs()) ulimit["srcname"] = self.m_srcname ulimit["eref"] = elogmean.TeV() # Try to run upper limit and catch exceptions try: ulimit.run() ulimit_value = ulimit.diff_ulimit() except: if self.logExplicit(): self.log("Upper limit calculation failed.") ulimit_value = -1.0 # Get TS value TS = -1.0 if self.m_calc_ts: TS = source.ts() # Compute Npred value (only works for unbinned analysis) Npred = 0.0 if not self.m_binned_mode: for observation in like.obs(): Npred += observation.npred(source) # Get differential flux fitted_flux = source.spectral().eval(elogmean,gammalib.GTime()) # Compute flux error parvalue = source.spectral()[0].value() rel_error = source.spectral()[0].error() / parvalue e_flux = fitted_flux * rel_error # Set values for storage TSvalues[i] = TS # Set npred values Npred_values[i] = Npred # Convert fluxes to nuFnu flux[i] = fitted_flux * elogmean2 * gammalib.MeV2erg flux_err[i] = e_flux * elogmean2 * gammalib.MeV2erg if ulimit_value > 0.0: ulim_values[i] = ulimit_value * elogmean2 * gammalib.MeV2erg # Log information if self.logTerse(): self.log("\n") self.log.parformat("Bin "+str(i+1)) self.log(str(flux[i])) self.log(" +/- ") self.log(str(flux_err[i])) if self.m_calc_ulimit and ulim_values[i] > 0.0: self.log(" [< "+str(ulim_values[i])+"]") self.log(" erg/cm2/s") if self.m_calc_ts and TSvalues[i] > 0.0: self.log(" (TS = "+str(TS)+")") # Append filled columns to fits table table.append(energy) table.append(energy_low) table.append(energy_high) table.append(flux) table.append(flux_err) table.append(TSvalues) table.append(ulim_values) table.append(Npred_values) # Create the FITS file now self.fits = gammalib.GFits() self.fits.append(table) # Return return
def run(self): """ Run the script. """ # Switch screen logging on in debug mode if self._logDebug(): self._log.cout(True) # Get parameters self._get_parameters() # Write spectral binning into header if self._logTerse(): self._log("\n") self._log.header1("Spectral binning") if self._binned_mode: cube_ebounds = self._obs[0].events().ebounds() self._log.parformat("Counts cube energy range") self._log(str(cube_ebounds.emin())) self._log(" - ") self._log(str(cube_ebounds.emax())) self._log("\n") for i in range(self._ebounds.size()): self._log.parformat("Bin " + str(i + 1)) self._log(str(self._ebounds.emin(i))) self._log(" - ") self._log(str(self._ebounds.emax(i))) self._log("\n") # Write observation into logger if self._logTerse(): self._log("\n") self._log.header1("Observation") self._log(str(self._obs)) self._log("\n") # Write header if self._logTerse(): self._log("\n") self._log.header1("Adjust model parameters") # Adjust model parameters dependent on input user parameters for model in self._obs.models(): # Set TS flag for all models to false. # Source of interest will be set to true later model.tscalc(False) # Log model name if self._logExplicit(): self._log.header3(model.name()) # Deal with the source of interest if model.name() == self._srcname: for par in model: if par.is_free() and self._logExplicit(): self._log(" Fixing \"" + par.name() + "\"\n") par.fix() normpar = model.spectral()[0] if normpar.is_fixed() and self._logExplicit(): self._log(" Freeing \"" + normpar.name() + "\"\n") normpar.free() if self._calc_ts: model.tscalc(True) elif self._fix_bkg and not model.classname() == "GModelSky": for par in model: if par.is_free() and self._logExplicit(): self._log(" Fixing \"" + par.name() + "\"\n") par.fix() elif self._fix_srcs and model.classname() == "GModelSky": for par in model: if par.is_free() and self._logExplicit(): self._log(" Fixing \"" + par.name() + "\"\n") par.fix() # Write header if self._logTerse(): self._log("\n") self._log.header1("Generate spectrum") self._log(str(self._ebounds)) # Initialise FITS Table with extension "SPECTRUM" table = gammalib.GFitsBinTable(self._ebounds.size()) table.extname("SPECTRUM") # Add Header for compatibility with gammalib.GMWLSpectrum table.card("INSTRUME", "CTA", "Name of Instrument") table.card("TELESCOP", "CTA", "Name of Telescope") # Create FITS table columns nrows = self._ebounds.size() energy = gammalib.GFitsTableDoubleCol("Energy", nrows) energy_low = gammalib.GFitsTableDoubleCol("ed_Energy", nrows) energy_high = gammalib.GFitsTableDoubleCol("eu_Energy", nrows) flux = gammalib.GFitsTableDoubleCol("Flux", nrows) flux_err = gammalib.GFitsTableDoubleCol("e_Flux", nrows) TSvalues = gammalib.GFitsTableDoubleCol("TS", nrows) ulim_values = gammalib.GFitsTableDoubleCol("UpperLimit", nrows) Npred_values = gammalib.GFitsTableDoubleCol("Npred", nrows) energy.unit("TeV") energy_low.unit("TeV") energy_high.unit("TeV") flux.unit("erg/cm2/s") flux_err.unit("erg/cm2/s") ulim_values.unit("erg/cm2/s") # Loop over energy bins for i in range(nrows): # Log information if self._logExplicit(): self._log("\n") self._log.header2("Energy bin " + str(i + 1)) # Get energy boundaries emin = self._ebounds.emin(i) emax = self._ebounds.emax(i) elogmean = self._ebounds.elogmean(i) elogmean2 = elogmean.MeV() * elogmean.MeV() # Store energy as TeV energy[i] = elogmean.TeV() # Store energy errors energy_low[i] = (elogmean - emin).TeV() energy_high[i] = (emax - elogmean).TeV() # Use ctselect for unbinned analysis if not self._binned_mode: # Log information if self._logExplicit(): self._log.header3("Selecting events") # Select events select = ctools.ctselect(self._obs) select["emin"] = emin.TeV() select["emax"] = emax.TeV() select["tmin"] = "UNDEFINED" select["tmax"] = "UNDEFINED" select["rad"] = "UNDEFINED" select["ra"] = "UNDEFINED" select["dec"] = "UNDEFINED" select.run() # Retrieve observation obs = select.obs() # Use ctcubemask for binned analysis else: # Header if self._logExplicit(): self._log.header3("Filtering cube") # Select layers cubemask = ctools.ctcubemask(self._obs) cubemask["regfile"] = "NONE" cubemask["ra"] = "UNDEFINED" cubemask["dec"] = "UNDEFINED" cubemask["rad"] = "UNDEFINED" cubemask["emin"] = emin.TeV() cubemask["emax"] = emax.TeV() cubemask.run() # Set new binned observation obs = cubemask.obs() # Header if self._logExplicit(): self._log.header3("Performing fit") # Likelihood like = ctools.ctlike(obs) like["edisp"] = self._edisp like.run() # Skip bin if no event was present if like.obs().logL() == 0.0: # Log information if self._logExplicit(): self._log("No event in this bin. ") self._log("Likelihood is zero. ") self._log("Bin is skipped.") # Set all values to 0 flux[i] = 0.0 flux_err[i] = 0.0 TSvalues[i] = 0.0 ulim_values[i] = 0.0 Npred_values[i] = 0.0 continue # Get results fitted_models = like.obs().models() source = fitted_models[self._srcname] # Calculate Upper Limit ulimit_value = -1.0 if self._calc_ulimit: # Logging information if self._logExplicit(): self._log.header3("Computing upper limit") # Create upper limit object ulimit = ctools.ctulimit(like.obs()) ulimit["srcname"] = self._srcname ulimit["eref"] = elogmean.TeV() # Try to run upper limit and catch exceptions try: ulimit.run() ulimit_value = ulimit.diff_ulimit() except: if self._logExplicit(): self._log("Upper limit calculation failed.") ulimit_value = -1.0 # Get TS value TS = -1.0 if self._calc_ts: TS = source.ts() # Compute Npred value (only works for unbinned analysis) Npred = 0.0 if not self._binned_mode: for observation in like.obs(): Npred += observation.npred(source) # Get differential flux fitted_flux = source.spectral().eval(elogmean) # Compute flux error parvalue = source.spectral()[0].value() rel_error = source.spectral()[0].error() / parvalue e_flux = fitted_flux * rel_error # Set values for storage TSvalues[i] = TS # Set npred values Npred_values[i] = Npred # Convert fluxes to nuFnu flux[i] = fitted_flux * elogmean2 * gammalib.MeV2erg flux_err[i] = e_flux * elogmean2 * gammalib.MeV2erg if ulimit_value > 0.0: ulim_values[i] = ulimit_value * elogmean2 * gammalib.MeV2erg # Log information if self._logTerse(): self._log("\n") self._log.parformat("Bin " + str(i + 1)) self._log(str(flux[i])) self._log(" +/- ") self._log(str(flux_err[i])) if self._calc_ulimit and ulim_values[i] > 0.0: self._log(" [< " + str(ulim_values[i]) + "]") self._log(" erg/cm2/s") if self._calc_ts and TSvalues[i] > 0.0: self._log(" (TS = " + str(TS) + ")") # Append filled columns to fits table table.append(energy) table.append(energy_low) table.append(energy_high) table.append(flux) table.append(flux_err) table.append(TSvalues) table.append(ulim_values) table.append(Npred_values) # Create the FITS file now self._fits = gammalib.GFits() self._fits.append(table) # Return return
def run(self): """ Run the script. """ # Switch screen logging on in debug mode if self.logDebug(): self.log.cout(True) # Get parameters self.get_parameters() # Write input parameters into logger if self.logTerse(): self.log_parameters() self.log("\n") # Write observation into logger if self.logTerse(): self.log("\n") self.log.header1("Observation") self.log(str(self.obs)) self.log("\n") # Write header if self.logTerse(): self.log("\n") self.log.header1("Adjust model parameters") # Adjust model parameters dependent on input user parameters for model in self.obs.models(): # Set TS flag for all models to false. # Source of interest will be set to true later model.tscalc(False) # Log model name if self.logExplicit(): self.log.header3(model.name()) # Deal with the source of interest if model.name() == self.m_srcname: for par in model: if par.is_free() and self.logExplicit(): self.log(" Fixing \""+par.name()+"\"\n") par.fix() if par.is_fixed() and self.logExplicit(): self.log(" Freeing \""+par.name()+"\"\n") model.spectral()[0].free() if self.m_calc_ts: model.tscalc(True) elif self.m_fix_bkg and not model.classname() == "GModelSky": for par in model: if par.is_free() and self.logExplicit(): self.log(" Fixing \""+par.name()+"\"\n") par.fix() elif self.m_fix_srcs and model.classname() == "GModelSky": for par in model: if par.is_free() and self.logExplicit(): self.log(" Fixing \""+par.name()+"\"\n") par.fix() # Write header if self.logTerse(): self.log("\n") self.log.header1("Generate spectrum") # Initialise FITS Table with extension "SPECTRUM" table = gammalib.GFitsBinTable(self.m_ebounds.size()) table.extname("SPECTRUM") # Add Header for compatibility with gammalib.GMWLSpectrum table.card("INSTRUME", "CTA", "Name of Instrument") table.card("TELESCOP", "CTA", "Name of Telescope") # Create FITS table columns energy = gammalib.GFitsTableDoubleCol("Energy", self.m_ebounds.size()) energy.unit("TeV") energy_low = gammalib.GFitsTableDoubleCol("ed_Energy", self.m_ebounds.size()) energy_low.unit("TeV") energy_high = gammalib.GFitsTableDoubleCol("eu_Energy", self.m_ebounds.size()) energy_high.unit("TeV") flux = gammalib.GFitsTableDoubleCol("Flux", self.m_ebounds.size()) flux.unit("erg/cm2/s") flux_err = gammalib.GFitsTableDoubleCol("e_Flux", self.m_ebounds.size()) flux_err.unit("erg/cm2/s") TSvalues = gammalib.GFitsTableDoubleCol("TS", self.m_ebounds.size()) ulim_values = gammalib.GFitsTableDoubleCol("UpperLimit", self.m_ebounds.size()) ulim_values.unit("erg/cm2/s") # Loop over energy bins for i in range(self.m_ebounds.size()): # Log information if self.logTerse(): self.log("\n") self.log.header2("Energy bin "+str(i)) # Get energy boundaries emin = self.m_ebounds.emin(i) emax = self.m_ebounds.emax(i) elogmean = self.m_ebounds.elogmean(i) elogmean2 = elogmean.MeV() * elogmean.MeV() # Store energy as TeV energy[i] = elogmean.TeV() # Store energy errors energy_low[i] = (elogmean - emin).TeV() energy_high[i] = (emax - elogmean).TeV() # Log information if self.logExplicit(): self.log.header3("Selecting events") # Select events select = ctools.ctselect(self.obs) select["emin"].real(emin.TeV()) select["emax"].real(emax.TeV()) select["tmin"].value("UNDEFINED") select["tmax"].value("UNDEFINED") select["rad"].value("UNDEFINED") select["ra"].value("UNDEFINED") select["dec"].value("UNDEFINED") select.run() # Retrieve observation obs = select.obs() # Binned analysis if self.m_binned: # Header if self.logTerse(): self.log.header3("Binning events") # Bin events bin = ctools.ctbin(select.obs()) bin["usepnt"].boolean(False) bin["ebinalg"].string("LOG") bin["xref"].real(self.m_xref) bin["yref"].real(self.m_yref) bin["binsz"].real(self.m_binsz) bin["nxpix"].integer(self.m_nxpix) bin["nypix"].integer(self.m_nypix) bin["enumbins"].integer(self.m_ebins) bin["emin"].real(emin.TeV()) bin["emax"].real(emax.TeV()) bin["coordsys"].string(self.m_coordsys) bin["proj"].string(self.m_proj) bin.run() # Header if self.logTerse(): self.log.header3("Creating exposure cube") # Create exposure cube expcube = ctools.ctexpcube(select.obs()) expcube["incube"].filename("NONE") expcube["usepnt"].boolean(False) expcube["ebinalg"].string("LOG") expcube["xref"].real(self.m_xref) expcube["yref"].real(self.m_yref) expcube["binsz"].real(self.m_binsz) expcube["nxpix"].integer(self.m_nxpix) expcube["nypix"].integer(self.m_nypix) expcube["enumbins"].integer(self.m_ebins) expcube["emin"].real(emin.TeV()) expcube["emax"].real(emax.TeV()) expcube["coordsys"].string(self.m_coordsys) expcube["proj"].string(self.m_proj) expcube.run() # Header if self.logTerse(): self.log.header3("Creating PSF cube") # Create psf cube psfcube = ctools.ctpsfcube(select.obs()) psfcube["incube"].filename("NONE") psfcube["usepnt"].boolean(False) psfcube["ebinalg"].string("LOG") psfcube["xref"].real(self.m_xref) psfcube["yref"].real(self.m_yref) psfcube["binsz"].real(self.m_binsz) psfcube["nxpix"].integer(self.m_nxpix) psfcube["nypix"].integer(self.m_nypix) psfcube["enumbins"].integer(self.m_ebins) psfcube["emin"].real(emin.TeV()) psfcube["emax"].real(emax.TeV()) psfcube["coordsys"].string(self.m_coordsys) psfcube["proj"].string(self.m_proj) psfcube.run() # Header if self.logTerse(): self.log.header3("Creating background cube") # Create background cube bkgcube = ctools.ctbkgcube(select.obs()) bkgcube["incube"].filename("NONE") bkgcube["usepnt"].boolean(False) bkgcube["ebinalg"].string("LOG") bkgcube["xref"].real(self.m_xref) bkgcube["yref"].real(self.m_yref) bkgcube["binsz"].real(self.m_binsz) bkgcube["nxpix"].integer(self.m_nxpix) bkgcube["nypix"].integer(self.m_nypix) bkgcube["enumbins"].integer(self.m_ebins) bkgcube["emin"].real(emin.TeV()) bkgcube["emax"].real(emax.TeV()) bkgcube["coordsys"].string(self.m_coordsys) bkgcube["proj"].string(self.m_proj) bkgcube.run() # Set new binned observation obs = bin.obs() # Set precomputed binned response obs[0].response(expcube.expcube(), psfcube.psfcube(), bkgcube.bkgcube()) # Get new models models = bkgcube.models() # Fix background models if required if self.m_fix_bkg: for model in models: if not model.classname() == "GModelSky": for par in model: par.fix() # Set new models to binned observation obs.models(models) # Header if self.logTerse(): self.log.header3("Performing fit") # Likelihood like = ctools.ctlike(obs) like.run() # Skip bin if no event was present if like.obs().logL() == 0.0: # Log information if self.logTerse(): self.log("No event in this bin. Bin is skipped\n") # Set all values to 0 flux[i] = 0.0 flux_err[i] = 0.0 TSvalues[i] = 0.0 ulim_values[i] = 0.0 continue # Get results fitted_models = like.obs().models() source = fitted_models[self.m_srcname] # Calculate Upper Limit ulimit_value = -1.0 if self.m_calc_ulimit: # Logging information if self.logTerse(): self.log.header3("Computing upper limit") # Create upper limit object ulimit = ctools.ctulimit(like.obs()) ulimit["srcname"].string(self.m_srcname) ulimit["eref"].real(elogmean.TeV()) # Try to run upper limit and catch exceptions try: ulimit.run() ulimit_value = ulimit.diff_ulimit() except: if self.logTerse(): self.log("Upper limit calculation failed\n") ulimit_value = -1.0 # Get TS value TS = -1.0 if self.m_calc_ts: TS = source.ts() # Get differential flux fitted_flux = source.spectral().eval(elogmean,gammalib.GTime()) # Compute flux error parvalue = source.spectral()[0].value() rel_error = source.spectral()[0].error()/parvalue e_flux = fitted_flux*rel_error # Set values for storage TSvalues[i] = TS # Convert fluxes to nuFnu flux[i] = fitted_flux * elogmean2 * gammalib.MeV2erg flux_err[i] = e_flux * elogmean2 * gammalib.MeV2erg if ulimit_value > 0.0: ulim_values[i] = ulimit_value * elogmean2 * gammalib.MeV2erg # Log information if self.logExplicit(): self.log("Bin "+str(i)+" ["+str(emin.TeV())+"-"+str(emax.TeV())+"] TeV: ") self.log("Flux = "+str(flux[i])) self.log(" +- "+str(flux_err[i])+" [erg/cm2/s]") if self.m_calc_ts and TSvalues[i] > 0.0: self.log(", TS = "+str(TS)) if self.m_calc_ulimit and ulim_values[i] > 0.0: self.log(", UL = "+str(ulim_values[i])+" [erg/cm2/s]") self.log("\n") # Append filles columns to fits table table.append(energy) table.append(energy_low) table.append(energy_high) table.append(flux) table.append(flux_err) table.append(TSvalues) table.append(ulim_values) # Create the FITS file now self.fits = gammalib.GFits() self.fits.append(table) # Return return
def _fit_mass_point(self, i) : """ Fit Model to DATA in the observation for a specific value of dark matter mass Return ------ Result , dictionary with relevant fit results """ # Get value of mass (already in GeV) dmmass = self._masses[i] self._log_header2(gammalib.EXPLICIT, 'Mass point ' + str(i + 1)) # Set reference energy and energy range for calculations geref = gammalib.GEnergy(dmmass / 2., 'GeV') gemin = gammalib.GEnergy(self['emin'].real(), 'GeV') gemax = gammalib.GEnergy(dmmass, 'GeV') # Create file with flux according to process # Well, at this moment, just annihilation :P self._log_header1( gammalib.TERSE , 'Compute DM model' ) if self['process'].string() == 'ANNA' : thisdmmodel = self._gen_model_anna(i) # elif self['process'].string() == 'DEC' : # print( 'Not Implemented' ) # sys.exit() # Then create GModel containers for source and bkg # thisbkgmodel = self._gen_bkgmodel() # GModels source and append dm and bkg models # mymodels = gammalib.GModels() # mymodels.append(thisdmmodel) # mymodels.append(thisbkgmodel) # Show mymodels in logfile, just to check that everything is Ok! self._log_string(gammalib.EXPLICIT , str(self.obs().models())) self._log_header1(gammalib.TERSE, 'Set or replace by Dark matter model') for model in self.obs().models() : if model.classname() != 'GCTAModelIrfBackground' : self.obs().models().remove(model.name()) self.obs().models().append(thisdmmodel) obssim = self.obs().copy() # Now, all the analysis is the same as in csspec script # Get expected dmflux between emin and emax # for the source of interest # srcname = self['srcname'].string() # srcmodel = obssim.models()[srcname] # srcspec = srcmodel.spectral() theoflux = thisdmmodel.spectral().flux(gemin, gemax) # theoflux *= thisdmmodel.spectral()['Normalization'].value() sigmav = math.pow(10., self['logsigmav'].real()) # Header self._log_header1(gammalib.TERSE, 'Fitting DM Model') # So, at this moment interesting results to save are: # - Min and Max Energy to compute integrated fluxes # - Mass of dark matter candidate # - Differential flux obtained in the fit # - Error # - TS # - Upper-limit on the differential flux # - Reference value of sigmav # - UL computed of sigmav # - Scale factor computed to obtain the UL on sigmav # This may be change when including Spectral class # for DM annihilation result = {'e_min' : gemin.TeV(), 'e_max' : gemax.TeV(), 'mass' : dmmass, 'flux' : 0.0, 'flux_err' : 0.0, 'logL' : 0.0, 'TS' : 0.0, 'ulimit' : 0.0, 'sigma_ref' : sigmav, 'sigma_lim' : 0.0 , 'sc_factor' : 0.0} # Header for ctlike instance :) self._log_header3(gammalib.EXPLICIT, 'Performing likelihood fit') # Maximum likelihood fit via ctlike like = ctools.ctlike(obssim) like['edisp'] = self['edisp'].boolean() like['nthreads'] = 1 # Chatter if self._logVerbose() and self._logDebug() : like['debug'] = True like.run() # Extract fit results model = like.obs().models()[self['srcname'].string()] spectrum = model.spectral() logL0 = like.obs().logL() result['logL'] = logL0 # Write models results self._log_string(gammalib.EXPLICIT, str(like.obs().models())) # Continue only if logL0 is different from zero if logL0 != 0.0 : # Extract TS value result['TS'] = model.ts() # Calculation of upper-limit via ctulimit ulimit_value = -1.0 if self['calc_ulim'].boolean() : # Print to log self._log_header3(gammalib.EXPLICIT, 'Computing Upper Limit') # Instance for ctulimit ulimit = ctools.ctulimit(like.obs()) ulimit['srcname'] = self['srcname'].string() ulimit['eref'] = geref.TeV() ulimit['emin'] = gemin.TeV() ulimit['emax'] = gemax.TeV() # Set chatter if self._logVerbose() and self._logDebug() : ulimit['debug'] = True ulimit.run() ulimit_value = ulimit.diff_ulimit() # Catching exceptions # try : # ulimit.run() # ulimit_value = ulimit.diff_ulimit() # except : # self._log_string(gammalib.EXPLICIT, 'UL Calculation failed') # ulimit_value = -1.0 # Compute quantities related to ulimit if ulimit_value > 0.0 : flimit_value = ulimit.flux_ulimit() result[ 'ulimit' ] = flimit_value scfactor = flimit_value / theoflux result[ 'sc_factor' ] = scfactor result[ 'sigma_lim' ] = scfactor * sigmav # Get flux and error fitted_flux = spectrum.eval(geref) parvalue = spectrum[0].value() if parvalue != 0.0 : rel_error = spectrum[0].error() / parvalue e_flux = fitted_flux * rel_error else : e_flux = 0.0 # If a cube, then compute corresponding weight if model.spatial().classname() == 'GModelSpatialDiffuseCube' : dir = gammalib.GSkyDir() model.spatial().set_mc_cone( dir , 180 ) norm = model.spatial().spectrum().eval( geref ) fitted_flux *= norm e_flux *= norm # Convert to nuFnu eref2 = geref.MeV() * geref.MeV() result['flux'] = fitted_flux * eref2 * gammalib.MeV2erg result['flux_err'] = e_flux * eref2 * gammalib.MeV2erg # Logging value = '%e +/- %e' % (fitted_flux, e_flux) svmsg = '' if self[ 'calc_ulim' ].boolean() and result[ 'ulimit' ] > 0.0 : value += ' [< %e]' % (result['ulimit']) svmsg += ' [%e]' % (result['sc_factor']) value += ' 1/cm**2/s' if self['calc_ts'].boolean() and result['TS'] > 0.0: value += ' (TS = %.3f)' % (result['TS']) self._log_value(gammalib.TERSE, 'Flux', value) if len( svmsg ) > 0 : self._log_value(gammalib.TERSE, 'ScaleFactor', svmsg) # If logL0 == 0, then failed :( # but, this does not raise any error else : value = 'Likelihood is zero. Something is weird. Check model' self._log_value(gammalib.TERSE, 'Warning: ', value) # Return return result