def _test_sim_stacked_edisp(self): """ Test sim() function in stacked mode with energy dispersion """ # Simulate binned observations res = obsutils.sim(self._setup_sim(two=True), nbins=5, edisp=True, log=True) # Check simulation results self.test_value(res.size(), 1, 'Check number of observations') self.test_value(res.models().size(), 2, 'Check number of models') self.test_value(res.nobserved(), 20, 'Check number of observed events') self.test_value(res.npred(), 0.0, 'Check number of predicted events') self.test_value(res[0].eventtype(), 'CountsCube', 'Check event type') self.test_value(res[0].events().ebounds().emin().TeV(), 1.0, 'Check minimum energy') self.test_value(res[0].events().ebounds().emax().TeV(), 10.0, 'Check minimum energy') self.test_value(res[0].events().ebounds().size(), 5, 'Check number of energy bins') self.test_value(res[0].events().number(), 20, 'Check number of events in cube') # Check energy dispersion flag self.test_assert(not (res[0].response().use_edisp()), 'Check energy dispersion usage') res[0].response().apply_edisp(True) self.test_assert(res[0].response().use_edisp(), 'Check energy dispersion usage') # Return return
def _test_sim_onoff(self): """ Test sim() function in On/Off mode """ # Set-up observation container pnt = gammalib.GSkyDir() pnt.radec_deg(83.63, 22.51) obs = gammalib.GObservations() run = obsutils.set_obs(pnt, duration=100.0, emin=1.0, emax=10.0, obsid='0') obs.append(run) pnt.radec_deg(83.63, 21.51) run = obsutils.set_obs(pnt, duration=100.0, emin=1.0, emax=10.0, obsid='1') obs.append(run) obs.models(gammalib.GModels(self._model)) # Simulate stacked observations res = obsutils.sim(obs, onsrc='Crab', nbins=5) # Check simulation results self.test_value(res.size(), 2, 'Check number of observations') self.test_value(res.models().size(), 2, 'Check number of models') self.test_value(res.nobserved(), 46, 'Check number of observed events') self.test_value(res.npred(), 0.0, 'Check number of predicted events') self.test_value(res[0].on_spec().ebounds().emin().TeV(), 1.0, 'Check minimum energy of On spectrum') self.test_value(res[0].on_spec().ebounds().emax().TeV(), 10.0, 'Check minimum energy of On spectrum') self.test_value(res[0].on_spec().ebounds().size(), 5, 'Check number of energy bins of On spectrum') self.test_value(res[0].on_spec().counts(), 24, 'Check number of events in of On spectrum') self.test_value(res[0].off_spec().ebounds().emin().TeV(), 1.0, 'Check minimum energy of Off spectrum') self.test_value(res[0].off_spec().ebounds().emax().TeV(), 10.0, 'Check minimum energy of Off spectrum') self.test_value(res[0].off_spec().ebounds().size(), 5, 'Check number of energy bins of Off spectrum') self.test_value(res[0].off_spec().counts(), 2, 'Check number of events in of Off spectrum') self.test_value(res[0].arf().ebounds().emin().TeV(), 0.5, 'Check minimum energy of ARF') self.test_value(res[0].arf().ebounds().emax().TeV(), 12.0, 'Check minimum energy of ARF') self.test_value(res[0].arf().ebounds().size(), 42, 'Check number of energy bins of ARF') self.test_value(res[0].rmf().etrue().emin().TeV(), 0.5, 'Check minimum true energy of RMF') self.test_value(res[0].rmf().etrue().emax().TeV(), 12.0, 'Check minimum true energy of RMF') self.test_value(res[0].rmf().etrue().size(), 42, 'Check number of true energy bins of RMF') self.test_value(res[0].rmf().emeasured().emin().TeV(), 1.0, 'Check minimum reconstructed energy of RMF') self.test_value(res[0].rmf().emeasured().emax().TeV(), 10.0, 'Check minimum reconstructed energy of RMF') self.test_value(res[0].rmf().emeasured().size(), 5, 'Check number of reconstructed energy bins of RMF') # Return return
def _test_sim_log(self): """ Test sim() function with logging switched on """ # Simulate unbinned observations res = obsutils.sim(self._setup_sim(), log=True) # Check simulation results self.test_value(res.size(), 1, 'Check number of observations') self.test_value(res.models().size(), 2, 'Check number of models') self.test_value(res.nobserved(), 4, 'Check number of observed events') self.test_value(res.npred(), 0.0, 'Check number of predicted events') self.test_value(res[0].eventtype(), 'EventList', 'Check event type') self.test_value(res[0].events().ebounds().emin().TeV(), 1.0, 'Check minimum energy') self.test_value(res[0].events().ebounds().emax().TeV(), 10.0, 'Check maximum energy') # Check energy dispersion flag self.test_assert(not (res[0].response().use_edisp()), 'Check energy dispersion usage') res[0].response().apply_edisp(True) self.test_assert(res[0].response().use_edisp(), 'Check energy dispersion usage') # Return return
def _test_sim_stacked_edisp(self): """ Test sim() function in stacked mode with energy dispersion """ # Simulate binned observations res = obsutils.sim(self._setup_sim(two=True), nbins=5, edisp=True, log=True) # Check simulation results self.test_value(res.size(), 1, 'Check number of observations') self.test_value(res.models().size(), 2, 'Check number of models') self.test_value(res.nobserved(), 20, 'Check number of observed events') self.test_value(res.npred(), 0.0, 'Check number of predicted events') self.test_value(res[0].eventtype(), 'CountsCube', 'Check event type') self.test_value(res[0].events().ebounds().emin().TeV(), 1.0, 'Check minimum energy') self.test_value(res[0].events().ebounds().emax().TeV(), 10.0, 'Check maximum energy') self.test_value(res[0].events().ebounds().size(), 5, 'Check number of energy bins') self.test_value(res[0].events().number(), 20, 'Check number of events in cube') # Check energy dispersion flag self.test_assert(not (res[0].response().use_edisp()), 'Check energy dispersion usage') res[0].response().apply_edisp(True) self.test_assert(res[0].response().use_edisp(), 'Check energy dispersion usage') # Return return
def _test_sim_log(self): """ Test sim() function with logging switched on """ # Simulate unbinned observations res = obsutils.sim(self._setup_sim(), log=True) # Check simulation results self.test_value(res.size(), 1, 'Check number of observations') self.test_value(res.models().size(), 2, 'Check number of models') self.test_value(res.nobserved(), 4, 'Check number of observed events') self.test_value(res.npred(), 0.0, 'Check number of predicted events') self.test_value(res[0].eventtype(), 'EventList', 'Check event type') self.test_value(res[0].events().ebounds().emin().TeV(), 1.0, 'Check minimum energy') self.test_value(res[0].events().ebounds().emax().TeV(), 10.0, 'Check minimum energy') # Check energy dispersion flag self.test_assert(not (res[0].response().use_edisp()), 'Check energy dispersion usage') res[0].response().apply_edisp(True) self.test_assert(res[0].response().use_edisp(), 'Check energy dispersion usage') # Return return
def _sim(self, seed): """ Return a simulated observation container Parameters ---------- seed : int Random number generator seed Returns ------- sim : `~gammalib.GObservations` Simulated observation container """ # If observation is a counts cube then simulate events from the counts # cube model ... if self.obs().size() == 1 and self.obs()[0].eventtype() == 'CountsCube': # If no counts cube model exists then compute it now if self._model == None: model = ctools.ctmodel(self.obs()) model['debug'] = self['debug'].boolean() model['chatter'] = self['chatter'].integer() model.run() self._model = model.cube().copy() # Save copy for persistence # Allocate random number generator ran = gammalib.GRan() # Get copy of model map counts = self._model.counts().copy() # Randomize counts for i in range(counts.npix()): counts[i] = ran.poisson(counts[i]) # Copy observations sim = self.obs().copy() # Set counts map sim[0].events().counts(counts) # ... otherwise simuate events from the observation container (works # only for event lists else: sim = obsutils.sim(self.obs(), seed = seed, log = self._log_clients, debug = self['debug'].boolean(), nthreads = 1) # Return simulated observation return sim
def _sim(self, seed): """ Return a simulated observation container Parameters ---------- seed : int Random number generator seed Returns ------- sim : `~gammalib.GObservations` Simulated observation container """ # If observation is a counts cube then simulate events from the counts # cube model ... if self.obs().size() == 1 and self.obs()[0].eventtype() == 'CountsCube': # If no counts cube model exists then compute it now if self._model == None: model = ctools.ctmodel(self.obs()) model['debug'] = self['debug'].boolean() model['chatter'] = self['chatter'].integer() model.run() self._model = model.cube().copy() # Save copy for persistence # Allocate random number generator ran = gammalib.GRan() # Get copy of model map counts = self._model.counts().copy() # Randomize counts for i in range(counts.npix()): counts[i] = ran.poisson(counts[i]) # Copy observations sim = self.obs().copy() # Set counts map sim[0].events().counts(counts) # ... otherwise simuate events from the observation container (works # only for event lists else: sim = obsutils.sim(self.obs(), seed = seed, log = self._log_clients, debug = self['debug'].boolean()) # Return simulated observation return sim
def _trial(self, seed, stacked): """ Compute the pull for a single trial Parameters ---------- seed : int Random number generator seed stacked : bool Use stacked analysis Returns ------- result : dict Dictionary of results """ # Write header if self._logNormal(): self._log.header2('Trial '+str(seed-self._seed+1)) # If we have a binned obeservation then specify the lower and # upper energy limit if self._enumbins > 0: emin = self['emin'].real() emax = self['emax'].real() else: emin = None emax = None # Simulate events obs = obsutils.sim(self._obs, emin=emin, emax=emax, nbins=self._enumbins, seed=seed, binsz=self._binsz, npix=self._npix, proj=self._proj, coord=self._coordsys, edisp=self._edisp, log=self._log_clients, debug=self._logDebug(), chatter=self._chatter) # Set response for a stacked observation if stacked: if self._edisp: obs[0].response(self._exposure, self._psfcube, self._edispcube, self._bckcube) else: obs[0].response(self._exposure, self._psfcube, self._bckcube) obs.models(self._stackmodels) # Determine number of events in simulation nevents = 0.0 for run in obs: nevents += run.events().number() # Write simulation results if self._logNormal(): self._log.header3('Simulation') for run in self._obs: self._log.parformat('Input observation %s' % (run.id())) self._log(self._obs_string(run)) self._log('\n') for run in obs: self._log.parformat('Output observation %s' % (run.id())) self._log(self._obs_string(run)) self._log('\n') self._log.parformat('Number of simulated events') self._log(nevents) self._log('\n') # Fit model if self['profile'].boolean(): models = self._obs.models() for i in range(models.size()): model_name = models[i].name() like = obsutils.cterror(self._obs, model_name, log=self._log_clients, debug=self._logDebug(), chatter=self._chatter) else: like = obsutils.fit(obs, edisp=self._edisp, log=self._log_clients, debug=self._logDebug(), chatter=self._chatter) # Store results logL = like.opt().value() npred = like.obs().npred() models = like.obs().models() # Write result header if self._logNormal(): self._log.header3('Pulls') # Gather results colnames = [] values = {} colnames.append('LogL') colnames.append('Sim_Events') colnames.append('Npred_Events') values['LogL'] = logL values['Sim_Events'] = nevents values['Npred_Events'] = npred for i in range(models.size()): model = models[i] model_name = model.name() for k in range(model.size()): par = model[k] if par.is_free(): # Set parameter name name = model_name+'_'+par.name() # Append parameter, Pull_parameter and Unc_parameter colnames.append(name) colnames.append('Pull_'+name) colnames.append('Unc_'+name) # Compute pull fitted_value = par.value() real_value = self._obs.models()[i][k].value() error = par.error() if error != 0.0: pull = (fitted_value - real_value) / error else: pull = 99.0 # Store results values[name] = fitted_value values['Pull_'+name] = pull values['Unc_'+name] = error # Write result if self._logNormal(): self._log.parformat(name) self._log(pull) self._log(' (') self._log(fitted_value) self._log(' +/- ') self._log(error) self._log(')\n') # Bundle together results result = {'colnames': colnames, 'values': values} # Return return result
def trial(self, seed, full_model, bkg_model): """ Create the TS for a single trial. Parameters: seed - Random number generator seed """ # Write header if self.logExplicit(): self.log.header2("Trial "+str(seed+1)) # Simulate events sim = obsutils.sim(self.obs, nbins=self.m_enumbins, seed=seed, binsz=self.m_binsz, npix=self.m_npix, log=self.m_log, debug=self.m_debug) # Determine number of events in simulation nevents = 0.0 for run in sim: nevents += run.events().number() # Write simulation results if self.logExplicit(): self.log.header3("Simulation") self.log.parformat("Number of simulated events") self.log(nevents) self.log("\n") # Fit background only sim.models(bkg_model) like_bgm = obsutils.fit(sim, log=self.m_log, debug=self.m_debug) result_bgm = like_bgm.obs().models().copy() LogL_bgm = like_bgm.opt().value() npred_bgm = like_bgm.obs().npred() # Write background fit results if self.logExplicit(): self.log.header3("Background model fit") self.log.parformat("log likelihood") self.log(LogL_bgm) self.log("\n") self.log.parformat("Number of predicted events") self.log(npred_bgm) self.log("\n") for model in result_bgm: self.log.parformat("Model") self.log(model.name()) self.log("\n") for par in model: self.log(str(par)+"\n") # Fit background and test source sim.models(full_model) like_all = obsutils.fit(sim, log=self.m_log, debug=self.m_debug) result_all = like_all.obs().models().copy() LogL_all = like_all.opt().value() npred_all = like_all.obs().npred() ts = 2.0*(LogL_bgm-LogL_all) # Write background and test source fit results if self.logExplicit(): self.log.header3("Background and test source model fit") self.log.parformat("Test statistics") self.log(ts) self.log("\n") self.log.parformat("log likelihood") self.log(LogL_all) self.log("\n") self.log.parformat("Number of predicted events") self.log(npred_all) self.log("\n") for model in result_all: self.log.parformat("Model") self.log(model.name()) self.log("\n") for par in model: self.log(str(par)+"\n") # Write result elif self.logTerse(): self.log.parformat("Trial "+str(seed)) self.log("TS=") self.log(ts) self.log(" Prefactor=") self.log(result_all[self.m_srcname]["Prefactor"].value()) self.log("+/-") self.log(result_all[self.m_srcname]["Prefactor"].error()) self.log("\n") # Initialise results colnames = [] values = {} # Set TS value colnames.append("TS") values["TS"] = ts # Set logL for background fit colnames.append("LogL_bgm") values["LogL_bgm"] = LogL_bgm # Set logL for full fit colnames.append("LogL_all") values["LogL_all"] = LogL_all # Set Nevents colnames.append("Nevents") values["Nevents"] = nevents # Set Npred for background fit colnames.append("Npred_bkg") values["Npred_bkg"] = npred_bgm # Set Npred for full fit colnames.append("Npred_all") values["Npred_all"] = npred_all # Gather free full fit parameters for i in range(result_all.size()): model = result_all[i] model_name = model.name() for k in range(model.size()): par = model[k] if par.is_free(): # Set parameter name name = model_name+"_"+par.name() # Append value colnames.append(name) values[name] = par.value() # Append error name = "Unc_"+name colnames.append(name) values[name] = par.error() # Bundle together results result = {'colnames': colnames, 'values': values} # Return return result
def get_sensitivity(self, obs, emin, emax, bkg_model, full_model): """ Determine sensitivity for given observations. Parameters: obs - Observation container emin - Minimum energy for fitting and flux computation emax - Maximum energy for fitting and flux computation bkg_model - Background model full_model - Source model """ # Set TeV->erg conversion factor tev2erg = 1.6021764 # Set energy boundaries self.set_obs_ebounds(emin, emax) # Determine energy boundaries from first observation in the container loge = math.log10(math.sqrt(emin.TeV()*emax.TeV())) e_mean = math.pow(10.0, loge) erg_mean = e_mean * tev2erg # Compute Crab unit (this is the factor with which the Prefactor needs # to be multiplied to get 1 Crab crab_flux = self.get_crab_flux(emin, emax) src_flux = full_model[self.m_srcname].spectral().flux(emin, emax) crab_unit = crab_flux/src_flux # Write header if self.logTerse(): self.log("\n") self.log.header2("Energies: "+str(emin)+" - "+str(emax)) self.log.parformat("Crab flux") self.log(crab_flux) self.log(" ph/cm2/s\n") self.log.parformat("Source model flux") self.log(src_flux) self.log(" ph/cm2/s\n") self.log.parformat("Crab unit factor") self.log(crab_unit) self.log("\n") # Initialise loop crab_flux_value = [] photon_flux_value = [] energy_flux_value = [] sensitivity_value = [] iter = 0 test_crab_flux = 0.1 # Initial test flux in Crab units (100 mCrab) # Loop until we break while True: # Update iteration counter iter += 1 # Write header if self.logExplicit(): self.log.header2("Iteration "+str(iter)) # Set source model. crab_prefactor is the Prefactor that # corresponds to 1 Crab src_model = full_model.copy() crab_prefactor = src_model[self.m_srcname]['Prefactor'].value() * crab_unit src_model[self.m_srcname]['Prefactor'].value(crab_prefactor * test_crab_flux) obs.models(src_model) # Simulate events sim = obsutils.sim(obs, nbins=self.m_enumbins, seed=iter, binsz=self.m_binsz, npix=self.m_npix, log=self.m_log, debug=self.m_debug, edisp=self.m_edisp) # Determine number of events in simulation nevents = 0.0 for run in sim: nevents += run.events().number() # Write simulation results if self.logExplicit(): self.log.header3("Simulation") self.log.parformat("Number of simulated events") self.log(nevents) self.log("\n") # Fit background only sim.models(bkg_model) like = obsutils.fit(sim, log=self.m_log, debug=self.m_debug, edisp=self.m_edisp) result_bgm = like.obs().models().copy() LogL_bgm = like.opt().value() npred_bgm = like.obs().npred() # Assess quality based on a comparison between Npred and Nevents quality_bgm = npred_bgm-nevents # Write background fit results if self.logExplicit(): self.log.header3("Background model fit") self.log.parformat("log likelihood") self.log(LogL_bgm) self.log("\n") self.log.parformat("Number of predicted events") self.log(npred_bgm) self.log("\n") self.log.parformat("Fit quality") self.log(quality_bgm) self.log("\n") # Start over if the fit quality was bad #if abs(quality_bgm) > 3.0: # if self.logExplicit(): # self.log("Fit quality outside required range. Start over.\n") # continue # Write model fit results if self.logExplicit(): for model in result_bgm: self.log.parformat("Model") self.log(model.name()) self.log("\n") for par in model: self.log(str(par)+"\n") # Fit background and test source sim.models(src_model) like = obsutils.fit(sim, log=self.m_log, debug=self.m_debug, edisp=self.m_edisp) result_all = like.obs().models().copy() LogL_all = like.opt().value() npred_all = like.obs().npred() ts = 2.0*(LogL_bgm-LogL_all) # Assess quality based on a comparison between Npred and Nevents quality_all = npred_all-nevents # Write background and test source fit results if self.logExplicit(): self.log.header3("Background and test source model fit") self.log.parformat("Test statistics") self.log(ts) self.log("\n") self.log.parformat("log likelihood") self.log(LogL_all) self.log("\n") self.log.parformat("Number of predicted events") self.log(npred_all) self.log("\n") self.log.parformat("Fit quality") self.log(quality_all) self.log("\n") # for model in result_all: self.log.parformat("Model") self.log(model.name()) self.log("\n") for par in model: self.log(str(par)+"\n") # Start over if the fit quality was bad #if abs(quality_all) > 3.0: # if self.logExplicit(): # self.log("Fit quality outside required range. Start over.\n") # continue # Start over if TS was non-positive if ts <= 0.0: if self.logExplicit(): self.log("Non positive TS. Start over.\n") continue # Get fitted Crab, photon and energy fluxes crab_flux = result_all[self.m_srcname]['Prefactor'].value() / crab_prefactor crab_flux_err = result_all[self.m_srcname]['Prefactor'].error() / crab_prefactor photon_flux = result_all[self.m_srcname].spectral().flux(emin, emax) energy_flux = result_all[self.m_srcname].spectral().eflux(emin, emax) # Compute differential sensitivity in unit erg/cm2/s energy = gammalib.GEnergy(e_mean, "TeV") time = gammalib.GTime() sensitivity = result_all[self.m_srcname].spectral().eval(energy, time) * \ e_mean*erg_mean * 1.0e6 # Compute flux correction factor based on average TS correct = 1.0 if ts > 0: correct = math.sqrt(self.m_ts_thres/ts) # Compute extrapolated fluxes crab_flux = correct * crab_flux photon_flux = correct * photon_flux energy_flux = correct * energy_flux sensitivity = correct * sensitivity crab_flux_value.append(crab_flux) photon_flux_value.append(photon_flux) energy_flux_value.append(energy_flux) sensitivity_value.append(sensitivity) # Write background and test source fit results if self.logExplicit(): self.log.parformat("Photon flux") self.log(photon_flux) self.log(" ph/cm2/s\n") self.log.parformat("Energy flux") self.log(energy_flux) self.log(" erg/cm2/s\n") self.log.parformat("Crab flux") self.log(crab_flux*1000.0) self.log(" mCrab\n") self.log.parformat("Differential sensitivity") self.log(sensitivity) self.log(" erg/cm2/s\n") for model in result_all: self.log.parformat("Model") self.log(model.name()) self.log("\n") for par in model: self.log(str(par)+"\n") elif self.logTerse(): self.log.parformat("Iteration "+str(iter)) self.log("TS=") self.log(ts) self.log(" ") self.log("corr=") self.log(correct) self.log(" ") self.log(photon_flux) self.log(" ph/cm2/s = ") self.log(energy_flux) self.log(" erg/cm2/s = ") self.log(crab_flux*1000.0) self.log(" mCrab = ") self.log(sensitivity) self.log(" erg/cm2/s\n") # Compute sliding average of extrapolated fitted prefactor, # photon and energy flux. This damps out fluctuations and # improves convergence crab_flux = 0.0 photon_flux = 0.0 energy_flux = 0.0 sensitivity = 0.0 num = 0.0 for k in range(self.m_num_avg): inx = len(crab_flux_value) - k - 1 if inx >= 0: crab_flux += crab_flux_value[inx] photon_flux += photon_flux_value[inx] energy_flux += energy_flux_value[inx] sensitivity += sensitivity_value[inx] num += 1.0 crab_flux /= num photon_flux /= num energy_flux /= num sensitivity /= num # Compare average flux to last average if iter > self.m_num_avg: if test_crab_flux > 0: ratio = crab_flux/test_crab_flux # We have 2 convergence criteria: # 1. The average flux does not change # 2. The flux correction factor is small if ratio >= 0.99 and ratio <= 1.01 and \ correct >= 0.9 and correct <= 1.1: if self.logTerse(): self.log(" Converged ("+str(ratio)+")\n") break else: if self.logTerse(): self.log(" Flux is zero.\n") break # Use average for next iteration test_crab_flux = crab_flux # Exit loop if number of trials exhausted if (iter >= self.m_max_iter): if self.logTerse(): self.log(" Test ended after "+str(self.m_max_iter)+" iterations.\n") break # Write fit results if self.logTerse(): self.log.header3("Fit results") self.log.parformat("Test statistics") self.log(ts) self.log("\n") self.log.parformat("Photon flux") self.log(photon_flux) self.log(" ph/cm2/s\n") self.log.parformat("Energy flux") self.log(energy_flux) self.log(" erg/cm2/s\n") self.log.parformat("Crab flux") self.log(crab_flux*1000.0) self.log(" mCrab\n") self.log.parformat("Differential sensitivity") self.log(sensitivity) self.log(" erg/cm2/s\n") self.log.parformat("Number of simulated events") self.log(nevents) self.log("\n") self.log.header3("Background and test source model fitting") self.log.parformat("log likelihood") self.log(LogL_all) self.log("\n") self.log.parformat("Number of predicted events") self.log(npred_all) self.log("\n") for model in result_all: self.log.parformat("Model") self.log(model.name()) self.log("\n") for par in model: self.log(str(par)+"\n") self.log.header3("Background model fit") self.log.parformat("log likelihood") self.log(LogL_bgm) self.log("\n") self.log.parformat("Number of predicted events") self.log(npred_bgm) self.log("\n") for model in result_bgm: self.log.parformat("Model") self.log(model.name()) self.log("\n") for par in model: self.log(str(par)+"\n") # Store result result = {'loge': loge, 'emin': emin.TeV(), 'emax': emax.TeV(), \ 'crab_flux': crab_flux, 'photon_flux': photon_flux, \ 'energy_flux': energy_flux, \ 'sensitivity': sensitivity} # Return result return result
def _get_sensitivity(self, emin, emax, test_model): """ Determine sensitivity for given observations Parameters ---------- emin : `~gammalib.GEnergy` Minimum energy for fitting and flux computation emax : `~gammalib.GEnergy` Maximum energy for fitting and flux computation test_model : `~gammalib.GModels` Test source model Returns ------- result : dict Result dictionary """ # Set TeV->erg conversion factor tev2erg = 1.6021764 # Set parameters ts_thres = self['sigma'].real() * self['sigma'].real() max_iter = self['max_iter'].integer() enumbins = self['enumbins'].integer() if not enumbins == 0: npix = self['npix'].integer() binsz = self['binsz'].real() else: npix = 200 binsz = 0.05 # Set flux ratio precision required for convergence to 5% ratio_precision = 0.05 # Set energy boundaries self._set_obs_ebounds(emin, emax) # Determine mean energy for energy boundary e_mean = math.sqrt(emin.TeV() * emax.TeV()) loge = math.log10(e_mean) erg_mean = e_mean * tev2erg # Compute Crab unit. This is the factor with which the Prefactor needs # to be multiplied to get 1 Crab. crab_flux = self._get_crab_flux(emin, emax) src_flux = test_model[self._srcname].spectral().flux(emin, emax) crab_unit = crab_flux / src_flux # Initialise regression coefficient regcoeff = 0.0 # Write header for energy bin self._log_string(gammalib.TERSE, '') self._log_header2(gammalib.TERSE, 'Energies: ' + str(emin) + ' - ' + str(emax)) # Write initial parameters self._log_header3(gammalib.TERSE, 'Initial parameters') self._log_value(gammalib.TERSE, 'Crab flux', str(crab_flux) + ' ph/cm2/s') self._log_value(gammalib.TERSE, 'Source model flux', str(src_flux) + ' ph/cm2/s') self._log_value(gammalib.TERSE, 'Crab unit factor', crab_unit) # Initialise loop results = [] iterations = 0 test_crab_flux = 0.1 # Initial test flux in Crab units (100 mCrab) # Write header for iterations for terse chatter level if self._logTerse(): self._log_header3(gammalib.TERSE, 'Iterations') # Loop until we break while True: # Update iteration counter iterations += 1 # Write header for iteration into logger self._log_header2(gammalib.EXPLICIT, 'Iteration ' + str(iterations)) # Create a copy of the test models, set the prefactor of the test # source in the models, and append the models to the observation. # "crab_prefactor" is the Prefactor that corresponds to a flux of # 1 Crab. models = test_model.copy() crab_prefactor = models[ self._srcname]['Prefactor'].value() * crab_unit models[self._srcname]['Prefactor'].value(crab_prefactor * test_crab_flux) self.obs().models(models) # Simulate events for the models. "sim" holds an observation # container with observations containing the simulated events. sim = obsutils.sim(self.obs(), nbins=enumbins, seed=iterations, binsz=binsz, npix=npix, log=self._log_clients, debug=self['debug'].boolean(), edisp=self['edisp'].boolean(), nthreads=1) # Determine number of events in simulation by summing the events # over all observations in the observation container nevents = 0.0 for run in sim: nevents += run.events().number() # Write simulation results into logger self._log_header3(gammalib.EXPLICIT, 'Simulation') self._log_value(gammalib.EXPLICIT, 'Number of simulated events', nevents) # Fit test source to the simulated events in the observation # container fit = ctools.ctlike(sim) fit['edisp'] = self['edisp'].boolean() fit['nthreads'] = 1 # Avoids OpenMP conflict fit['debug'] = self['debug'].boolean() fit['chatter'] = self['chatter'].integer() fit.run() # Get model fitting results logL = fit.opt().value() npred = fit.obs().npred() models = fit.obs().models() source = models[self._srcname] ts = source.ts() # Get fitted Crab, photon and energy fluxes crab_flux = source['Prefactor'].value() / crab_prefactor photon_flux = source.spectral().flux(emin, emax) energy_flux = source.spectral().eflux(emin, emax) # Compute differential sensitivity in unit erg/cm2/s by evaluating # the spectral model at the "e_mean" energy and by multipling the # result with the energy squared. Since the "eval()" method returns # an intensity in units of ph/cm2/s/MeV we multiply by 1.0e6 to # convert into ph/cm2/s/TeV, by "e_mean" to convert into ph/cm2/s, # and finally by "erg_mean" to convert to erg/cm2/s. energy = gammalib.GEnergy(e_mean, 'TeV') sensitivity = source.spectral().eval( energy) * e_mean * erg_mean * 1.0e6 # Write fit results into logger name = 'Iteration %d' % iterations value = ( 'TS=%10.4f Sim=%9.4f mCrab Fit=%9.4f mCrab ' 'Sens=%e erg/cm2/s' % (ts, test_crab_flux * 1000.0, crab_flux * 1000.0, sensitivity)) self._log_value(gammalib.TERSE, name, value) # If TS was non-positive then increase the test flux and start over if ts <= 0.0: # If the number of iterations was exceeded then stop if (iterations >= max_iter): self._log_string( gammalib.TERSE, ' Test ended after %d iterations.' % max_iter) break # Increase test flux test_crab_flux *= 3.0 # Signal start we start over self._log_string( gammalib.EXPLICIT, 'Non positive TS, increase test flux and start over.') # ... and start over continue # Append result entry to result list result = { 'ts': ts, 'crab_flux': crab_flux, 'photon_flux': photon_flux, 'energy_flux': energy_flux } results.append(result) # Predict Crab flux at threshold TS using a linear regression of # the log(TS) and log(crab_flux) values that have so far been # computed. If not enough results are available than use a simple # TS scaling relation. if len(results) > 1: pred_crab_flux, regcoeff = self._predict_flux( results, ts_thres) correct = pred_crab_flux / crab_flux else: correct = math.sqrt(ts_thres / ts) # Compute extrapolated fluxes based on the flux correction factor crab_flux = correct * crab_flux photon_flux = correct * photon_flux energy_flux = correct * energy_flux sensitivity = correct * sensitivity # If we have at least 3 results then check if the flux determination # at the TS threshold has converged if len(results) > 3: if test_crab_flux > 0: # Compute fractional change in the Crab flux between two # iterations ratio = crab_flux / test_crab_flux # If fractional change is smaller than the required position # the iterations are stopped if ratio > 1.0-ratio_precision and \ ratio < 1.0+ratio_precision: value = ('TS=%10.4f Sim=%9.4f mCrab ' ' Sens=%e erg/cm2/s' % (ts, crab_flux * 1000.0, sensitivity)) self._log_value(gammalib.TERSE, 'Converged result', value) self._log_value(gammalib.TERSE, 'Converged flux ratio', ratio) self._log_value(gammalib.TERSE, 'Regression coefficient', regcoeff) break else: self._log_value(gammalib.TERSE, 'Not converged', 'Flux is zero') break # Set test flux for next iteration test_crab_flux = crab_flux # Exit loop if number of trials exhausted if (iterations >= max_iter): self._log_string(gammalib.TERSE, ' Test ended after %d iterations.' % max_iter) break # Write fit results into logger self._log_header3(gammalib.TERSE, 'Fit results') self._log_value(gammalib.TERSE, 'Photon flux', str(photon_flux) + ' ph/cm2/s') self._log_value(gammalib.TERSE, 'Energy flux', str(energy_flux) + ' erg/cm2/s') self._log_value(gammalib.TERSE, 'Crab flux', str(crab_flux * 1000.0) + ' mCrab') self._log_value(gammalib.TERSE, 'Differential sensitivity', str(sensitivity) + ' erg/cm2/s') self._log_value(gammalib.TERSE, 'Number of simulated events', nevents) self._log_header3(gammalib.TERSE, 'Test source model fitting') self._log_value(gammalib.TERSE, 'log likelihood', logL) self._log_value(gammalib.TERSE, 'Number of predicted events', npred) for model in models: self._log_value(gammalib.TERSE, 'Model', model.name()) for par in model: self._log_string(gammalib.TERSE, str(par)) # Restore energy boundaries of observation container for i, obs in enumerate(self.obs()): obs.events().ebounds(self._obs_ebounds[i]) # Store result result = {'loge': loge, 'emin': emin.TeV(), 'emax': emax.TeV(), \ 'crab_flux': crab_flux, 'photon_flux': photon_flux, \ 'energy_flux': energy_flux, \ 'sensitivity': sensitivity, 'regcoeff': regcoeff, \ 'nevents': nevents, 'npred': npred} # Return result return result
def _get_sensitivity(self, emin, emax, test_model): """ Determine sensitivity for given observations Parameters ---------- emin : `~gammalib.GEnergy` Minimum energy for fitting and flux computation emax : `~gammalib.GEnergy` Maximum energy for fitting and flux computation test_model : `~gammalib.GModels` Test source model Returns ------- result : dict Result dictionary """ # Set TeV->erg conversion factor tev2erg = 1.6021764 # Set parameters ts_thres = self['sigma'].real() * self['sigma'].real() max_iter = self['max_iter'].integer() enumbins = self['enumbins'].integer() if not enumbins == 0: npix = self['npix'].integer() binsz = self['binsz'].real() else: npix = 200 binsz = 0.05 # Set flux ratio precision required for convergence to 5% ratio_precision = 0.05 # Set energy boundaries self._set_obs_ebounds(emin, emax) # Determine mean energy for energy boundary e_mean = math.sqrt(emin.TeV()*emax.TeV()) loge = math.log10(e_mean) erg_mean = e_mean * tev2erg # Compute Crab unit. This is the factor with which the Prefactor needs # to be multiplied to get 1 Crab. crab_flux = self._get_crab_flux(emin, emax) src_flux = test_model[self._srcname].spectral().flux(emin, emax) crab_unit = crab_flux/src_flux # Initialise regression coefficient regcoeff = 0.0 # Write header for energy bin self._log_string(gammalib.TERSE, '') self._log_header2(gammalib.TERSE, 'Energies: '+str(emin)+' - '+str(emax)) # Write initial parameters self._log_header3(gammalib.TERSE, 'Initial parameters') self._log_value(gammalib.TERSE, 'Crab flux', str(crab_flux)+' ph/cm2/s') self._log_value(gammalib.TERSE, 'Source model flux', str(src_flux)+' ph/cm2/s') self._log_value(gammalib.TERSE, 'Crab unit factor', crab_unit) # Initialise loop results = [] iterations = 0 test_crab_flux = 0.1 # Initial test flux in Crab units (100 mCrab) # Write header for iterations for terse chatter level if self._logTerse(): self._log_header3(gammalib.TERSE, 'Iterations') # Loop until we break while True: # Update iteration counter iterations += 1 # Write header for iteration into logger self._log_header2(gammalib.EXPLICIT, 'Iteration '+str(iterations)) # Create a copy of the test models, set the prefactor of the test # source in the models, and append the models to the observation. # "crab_prefactor" is the Prefactor that corresponds to a flux of # 1 Crab. models = test_model.copy() crab_prefactor = models[self._srcname]['Prefactor'].value() * crab_unit models[self._srcname]['Prefactor'].value(crab_prefactor * test_crab_flux) self.obs().models(models) # Simulate events for the models. "sim" holds an observation # container with observations containing the simulated events. sim = obsutils.sim(self.obs(), nbins=enumbins, seed=iterations, binsz=binsz, npix=npix, log=self._log_clients, debug=self['debug'].boolean(), edisp=self['edisp'].boolean()) # Determine number of events in simulation by summing the events # over all observations in the observation container nevents = 0.0 for run in sim: nevents += run.events().number() # Write simulation results into logger self._log_header3(gammalib.EXPLICIT, 'Simulation') self._log_value(gammalib.EXPLICIT, 'Number of simulated events', nevents) # Fit test source to the simulated events in the observation # container fit = ctools.ctlike(sim) fit['edisp'] = self['edisp'].boolean() fit['debug'] = self['debug'].boolean() fit['chatter'] = self['chatter'].integer() fit.run() # Get model fitting results logL = fit.opt().value() npred = fit.obs().npred() models = fit.obs().models() source = models[self._srcname] ts = source.ts() # Get fitted Crab, photon and energy fluxes crab_flux = source['Prefactor'].value() / crab_prefactor photon_flux = source.spectral().flux(emin, emax) energy_flux = source.spectral().eflux(emin, emax) # Compute differential sensitivity in unit erg/cm2/s by evaluating # the spectral model at the "e_mean" energy and by multipling the # result with the energy squared. Since the "eval()" method returns # an intensity in units of ph/cm2/s/MeV we multiply by 1.0e6 to # convert into ph/cm2/s/TeV, by "e_mean" to convert into ph/cm2/s, # and finally by "erg_mean" to convert to erg/cm2/s. energy = gammalib.GEnergy(e_mean, 'TeV') sensitivity = source.spectral().eval(energy) * e_mean*erg_mean*1.0e6 # Write fit results into logger name = 'Iteration %d' % iterations value = ('TS=%10.4f Sim=%9.4f mCrab Fit=%9.4f mCrab ' 'Sens=%e erg/cm2/s' % (ts, test_crab_flux*1000.0, crab_flux*1000.0, sensitivity)) self._log_value(gammalib.TERSE, name, value) # If TS was non-positive then increase the test flux and start over if ts <= 0.0: # If the number of iterations was exceeded then stop if (iterations >= max_iter): self._log_string(gammalib.TERSE, ' Test ended after %d iterations.' % max_iter) break # Increase test flux test_crab_flux *= 3.0 # Signal start we start over self._log_string(gammalib.EXPLICIT, 'Non positive TS, increase test flux and start over.') # ... and start over continue # Append result entry to result list result = {'ts': ts, 'crab_flux': crab_flux, 'photon_flux': photon_flux, 'energy_flux': energy_flux} results.append(result) # Predict Crab flux at threshold TS using a linear regression of # the log(TS) and log(crab_flux) values that have so far been # computed. If not enough results are available than use a simple # TS scaling relation. if len(results) > 1: pred_crab_flux, regcoeff = self._predict_flux(results, ts_thres) correct = pred_crab_flux / crab_flux else: correct = math.sqrt(ts_thres/ts) # Compute extrapolated fluxes based on the flux correction factor crab_flux = correct * crab_flux photon_flux = correct * photon_flux energy_flux = correct * energy_flux sensitivity = correct * sensitivity # If we have at least 3 results then check if the flux determination # at the TS threshold has converged if len(results) > 3: if test_crab_flux > 0: # Compute fractional change in the Crab flux between two # iterations ratio = crab_flux/test_crab_flux # If fractional change is smaller than the required position # the iterations are stopped if ratio > 1.0-ratio_precision and \ ratio < 1.0+ratio_precision: value = ('TS=%10.4f Sim=%9.4f mCrab ' ' Sens=%e erg/cm2/s' % (ts, crab_flux*1000.0, sensitivity)) self._log_value(gammalib.TERSE, 'Converged result', value) self._log_value(gammalib.TERSE, 'Converged flux ratio', ratio) self._log_value(gammalib.TERSE, 'Regression coefficient', regcoeff) break else: self._log_value(gammalib.TERSE, 'Not converged', 'Flux is zero') break # Set test flux for next iteration test_crab_flux = crab_flux # Exit loop if number of trials exhausted if (iterations >= max_iter): self._log_string(gammalib.TERSE, ' Test ended after %d iterations.' % max_iter) break # Write fit results into logger self._log_header3(gammalib.TERSE, 'Fit results') self._log_value(gammalib.TERSE, 'Photon flux', str(photon_flux)+' ph/cm2/s') self._log_value(gammalib.TERSE, 'Energy flux', str(energy_flux)+' erg/cm2/s') self._log_value(gammalib.TERSE, 'Crab flux', str(crab_flux*1000.0)+' mCrab') self._log_value(gammalib.TERSE, 'Differential sensitivity', str(sensitivity)+' erg/cm2/s') self._log_value(gammalib.TERSE, 'Number of simulated events', nevents) self._log_header3(gammalib.TERSE, 'Test source model fitting') self._log_value(gammalib.TERSE, 'log likelihood', logL) self._log_value(gammalib.TERSE, 'Number of predicted events', npred) for model in models: self._log_value(gammalib.TERSE, 'Model', model.name()) for par in model: self._log_string(gammalib.TERSE, str(par)) # Restore energy boundaries of observation container for i, obs in enumerate(self.obs()): obs.events().ebounds(self._obs_ebounds[i]) # Store result result = {'loge': loge, 'emin': emin.TeV(), 'emax': emax.TeV(), \ 'crab_flux': crab_flux, 'photon_flux': photon_flux, \ 'energy_flux': energy_flux, \ 'sensitivity': sensitivity, 'regcoeff': regcoeff, \ 'nevents': nevents, 'npred': npred} # Return result return result
def _trial(self, seed): """ Compute the pull for a single trial Parameters ---------- seed : int Random number generator seed Returns ------- result : dict Dictionary of results """ # Write header self._log_header2(gammalib.NORMAL, 'Trial %d' % (seed-self['seed'].integer()+1)) # Get number of energy bins and On source name and initialise # some parameters nbins = self['enumbins'].integer() onsrc = self['onsrc'].string() edisp = self['edisp'].boolean() statistic = self['statistic'].string() emin = None emax = None binsz = 0.0 npix = 0 proj = 'TAN' coordsys = 'CEL' # If we have a On source name then set On region radius if gammalib.toupper(onsrc) != 'NONE': onrad = self['onrad'].real() emin = self['emin'].real() emax = self['emax'].real() edisp = True # Use always energy dispersion for On/Off else: # Reset On region source name and radius onrad = 0.0 onsrc = None # If we have a binned obeservation then specify the lower and # upper energy limit in TeV if nbins > 0: emin = self['emin'].real() emax = self['emax'].real() binsz = self['binsz'].real() npix = self['npix'].integer() proj = self['proj'].string() coordsys = self['coordsys'].string() # Simulate events obs = obsutils.sim(self.obs(), emin=emin, emax=emax, nbins=nbins, onsrc=onsrc, onrad=onrad, addbounds=True, seed=seed, binsz=binsz, npix=npix, proj=proj, coord=coordsys, edisp=edisp, log=False, debug=self._logDebug(), chatter=self['chatter'].integer()) # Determine number of events in simulation nevents = 0.0 for run in obs: nevents += run.nobserved() # Write simulation results self._log_header3(gammalib.NORMAL, 'Simulation') for run in self.obs(): self._log_value(gammalib.NORMAL, 'Input observation %s' % run.id(), self._obs_string(run)) for run in obs: self._log_value(gammalib.NORMAL, 'Output observation %s' % run.id(), self._obs_string(run)) self._log_value(gammalib.NORMAL, 'Number of simulated events', nevents) # Fit model if self['profile'].boolean(): models = self.obs().models() for model in models: like = ctools.cterror(obs) like['srcname'] = model.name() like['edisp'] = edisp like['statistic'] = statistic like['debug'] = self._logDebug() like['chatter'] = self['chatter'].integer() like.run() else: like = ctools.ctlike(obs) like['edisp'] = edisp like['statistic'] = statistic like['debug'] = self._logDebug() like['chatter'] = self['chatter'].integer() like.run() # Store results logL = like.opt().value() npred = like.obs().npred() models = like.obs().models() # Write result header self._log_header3(gammalib.NORMAL, 'Pulls') # Gather results in form of a list of result columns and a # dictionary containing the results. The result contains the # log-likelihood, the number of simulated events, the number of # predicted events and for each fitted parameter the fitted value, # the pull and the fit error. # # Note that we do not use the model and parameter iterators # because we need the indices to get the true (or real) parameter # values from the input models. colnames = ['LogL', 'Sim_Events', 'Npred_Events'] values = {'LogL': logL, 'Sim_Events': nevents, 'Npred_Events': npred} for i in range(models.size()): model = models[i] for k in range(model.size()): par = model[k] if par.is_free(): # Set name as a combination of model name and parameter # name separated by an underscore. In that way each # parameter has a unique name. name = model.name()+'_'+par.name() # Append parameter, Pull_parameter and e_parameter column # names colnames.append(name) colnames.append('Pull_'+name) colnames.append('e_'+name) # Compute pull for this parameter as the difference # (fitted - true) / error # In case that the error is 0 the pull is set to 99 fitted_value = par.value() real_value = self.obs().models()[i][k].value() error = par.error() if error != 0.0: pull = (fitted_value - real_value) / error else: pull = 99.0 # Store results in dictionary values[name] = fitted_value values['Pull_'+name] = pull values['e_'+name] = error # Write results into logger value = '%.4f (%e +/- %e)' % (pull, fitted_value, error) self._log_value(gammalib.NORMAL, name, value) # Bundle together results in a dictionary result = {'colnames': colnames, 'values': values} # Return return result
def trial(self, seed): """ Create the pull for a single trial. Parameters: seed - Random number generator seed """ # Write header if self.logNormal(): self.log.header2("Trial "+str(seed+1)) # Simulate events obs = obsutils.sim(self.obs, nbins=self.m_enumbins, seed=seed, binsz=self.m_binsz, npix=self.m_npix, proj=self.m_proj, coord=self.m_coordsys, edisp=self.m_edisp, log=self.m_log, debug=self.m_debug, chatter=self.m_chatter) # If stacked, add stacked responses and model if self.obs.size() > 1 and self.m_enumbins > 0: obs[0].response(self.m_exposure, self.m_psfcube, self.m_bckcube) obs.models(self.m_stackmodels) # Determine number of events in simulation nevents = 0.0 for run in obs: nevents += run.events().number() # Write simulation results if self.logNormal(): self.log.header3("Simulation") self.log.parformat("Number of simulated events") self.log(nevents) self.log("\n") # Fit model if self.m_profile: models = obs.models() for i in range(models.size()): model_name = models[i].name() like = obsutils.cterror(obs, model_name, log=self.m_log, debug=self.m_debug, chatter=self.m_chatter) else: like = obsutils.fit(obs, edisp=self.m_edisp, log=self.m_log, debug=self.m_debug, chatter=self.m_chatter) # Store results logL = like.opt().value() npred = like.obs().npred() models = like.obs().models() # Write result header if self.logNormal(): self.log.header3("Pulls") # Gather results colnames = [] values = {} colnames.append("LogL") colnames.append("Sim_Events") colnames.append("Npred_Events") values["LogL"] = logL values["Sim_Events"] = nevents values["Npred_Events"] = npred for i in range(models.size()): model = models[i] model_name = model.name() for k in range(model.size()): par = model[k] if par.is_free(): # Set parameter name name = model_name+"_"+par.name() # Append parameter, Pull_parameter and Unc_parameter colnames.append(name) colnames.append("Pull_"+name) colnames.append("Unc_"+name) # Compute pull fitted_value = par.value() real_value = self.obs.models()[i][k].value() error = par.error() if error != 0.0: pull = (fitted_value - real_value) / error else: pull = 99.0 # Store results values[name] = fitted_value values["Pull_"+name] = pull values["Unc_"+name] = error # Write result if self.logNormal(): self.log.parformat(name) self.log(pull) self.log(" (") self.log(fitted_value) self.log(" +/- ") self.log(error) self.log(")\n") # Bundle together results result = {'colnames': colnames, 'values': values} # Return return result
def _test_sim_onoff(self): """ Test sim() function in On/Off mode """ # Set-up observation container pnt = gammalib.GSkyDir() pnt.radec_deg(83.63, 22.51) obs = gammalib.GObservations() run = obsutils.set_obs(pnt, duration=100.0, emin=1.0, emax=10.0, obsid='0') obs.append(run) pnt.radec_deg(83.63, 21.51) run = obsutils.set_obs(pnt, duration=100.0, emin=1.0, emax=10.0, obsid='1') obs.append(run) obs.models(gammalib.GModels(self._model)) # Simulate On/Off observations res = obsutils.sim(obs, onsrc='Crab', nbins=5) # Check simulation results self.test_value(res.size(), 2, 'Check number of observations') self.test_value(res.models().size(), 2, 'Check number of models') self.test_value(res.nobserved(), 48, 'Check number of observed events') self.test_value(res.npred(), 0.0, 'Check number of predicted events') # Check results of first observation self.test_value(res[0].on_spec().ebounds().emin().TeV(), 1.0, 'Check minimum energy of On spectrum') self.test_value(res[0].on_spec().ebounds().emax().TeV(), 10.0, 'Check maximum energy of On spectrum') self.test_value(res[0].on_spec().ebounds().size(), 5, 'Check number of energy bins of On spectrum') self.test_value(res[0].on_spec().counts(), 26, 'Check number of events in of On spectrum') self.test_value(res[0].off_spec().ebounds().emin().TeV(), 1.0, 'Check minimum energy of Off spectrum') self.test_value(res[0].off_spec().ebounds().emax().TeV(), 10.0, 'Check maximum energy of Off spectrum') self.test_value(res[0].off_spec().ebounds().size(), 5, 'Check number of energy bins of Off spectrum') self.test_value(res[0].off_spec().counts(), 1, 'Check number of events in of Off spectrum') self.test_value(res[0].arf().ebounds().emin().TeV(), 0.5, 'Check minimum energy of ARF') self.test_value(res[0].arf().ebounds().emax().TeV(), 12.0, 'Check maximum energy of ARF') self.test_value(res[0].arf().ebounds().size(), 41, 'Check number of energy bins of ARF') self.test_value(res[0].rmf().etrue().emin().TeV(), 0.5, 'Check minimum true energy of RMF') self.test_value(res[0].rmf().etrue().emax().TeV(), 12.0, 'Check maximum true energy of RMF') self.test_value(res[0].rmf().etrue().size(), 41, 'Check number of true energy bins of RMF') self.test_value(res[0].rmf().emeasured().emin().TeV(), 1.0, 'Check minimum reconstructed energy of RMF') self.test_value(res[0].rmf().emeasured().emax().TeV(), 10.0, 'Check maximum reconstructed energy of RMF') self.test_value(res[0].rmf().emeasured().size(), 5, 'Check number of reconstructed energy bins of RMF') # Check results of second observation self.test_value(res[1].on_spec().ebounds().emin().TeV(), 1.0, 'Check minimum energy of On spectrum') self.test_value(res[1].on_spec().ebounds().emax().TeV(), 10.0, 'Check maximum energy of On spectrum') self.test_value(res[1].on_spec().ebounds().size(), 5, 'Check number of energy bins of On spectrum') self.test_value(res[1].on_spec().counts(), 22, 'Check number of events in of On spectrum') self.test_value(res[1].off_spec().ebounds().emin().TeV(), 1.0, 'Check minimum energy of Off spectrum') self.test_value(res[1].off_spec().ebounds().emax().TeV(), 10.0, 'Check maximum energy of Off spectrum') self.test_value(res[1].off_spec().ebounds().size(), 5, 'Check number of energy bins of Off spectrum') self.test_value(res[1].off_spec().counts(), 1, 'Check number of events in of Off spectrum') self.test_value(res[1].arf().ebounds().emin().TeV(), 0.5, 'Check minimum energy of ARF') self.test_value(res[1].arf().ebounds().emax().TeV(), 12.0, 'Check maximum energy of ARF') self.test_value(res[1].arf().ebounds().size(), 41, 'Check number of energy bins of ARF') self.test_value(res[1].rmf().etrue().emin().TeV(), 0.5, 'Check minimum true energy of RMF') self.test_value(res[1].rmf().etrue().emax().TeV(), 12.0, 'Check maximum true energy of RMF') self.test_value(res[1].rmf().etrue().size(), 41, 'Check number of true energy bins of RMF') self.test_value(res[1].rmf().emeasured().emin().TeV(), 1.0, 'Check minimum reconstructed energy of RMF') self.test_value(res[1].rmf().emeasured().emax().TeV(), 10.0, 'Check maximum reconstructed energy of RMF') self.test_value(res[1].rmf().emeasured().size(), 5, 'Check number of reconstructed energy bins of RMF') # Return return
def _trial(self, seed, full_model, bkg_model): """ Create the TS for a single trial. Args: seed: Random number generator seed full_model: Full model bkg_model: Background model Returns: Result dictionary """ # Write header if self._logExplicit(): self._log.header2("Trial " + str(seed + 1)) # Simulate events sim = obsutils.sim(self._obs, nbins=self._enumbins, seed=seed, binsz=self._binsz, npix=self._npix, log=self._log_clients, debug=self._debug) # Determine number of events in simulation nevents = 0.0 for run in sim: nevents += run.events().number() # Write simulation results if self._logExplicit(): self._log.header3("Simulation") self._log.parformat("Number of simulated events") self._log(nevents) self._log("\n") # Fit background only sim.models(bkg_model) like_bgm = obsutils.fit(sim, log=self._log_clients, debug=self._debug) result_bgm = like_bgm.obs().models().copy() LogL_bgm = like_bgm.opt().value() npred_bgm = like_bgm.obs().npred() # Write background fit results if self._logExplicit(): self._log.header3("Background model fit") self._log.parformat("log likelihood") self._log(LogL_bgm) self._log("\n") self._log.parformat("Number of predicted events") self._log(npred_bgm) self._log("\n") for model in result_bgm: self._log.parformat("Model") self._log(model.name()) self._log("\n") for par in model: self._log(str(par) + "\n") # Fit background and test source sim.models(full_model) like_all = obsutils.fit(sim, log=self._log_clients, debug=self._debug) result_all = like_all.obs().models().copy() LogL_all = like_all.opt().value() npred_all = like_all.obs().npred() ts = 2.0 * (LogL_bgm - LogL_all) # Write background and test source fit results if self._logExplicit(): self._log.header3("Background and test source model fit") self._log.parformat("Test statistics") self._log(ts) self._log("\n") self._log.parformat("log likelihood") self._log(LogL_all) self._log("\n") self._log.parformat("Number of predicted events") self._log(npred_all) self._log("\n") for model in result_all: self._log.parformat("Model") self._log(model.name()) self._log("\n") for par in model: self._log(str(par) + "\n") # Write result elif self._logTerse(): self._log.parformat("Trial " + str(seed)) self._log("TS=") self._log(ts) self._log(" Prefactor=") self._log(result_all[self._srcname]["Prefactor"].value()) self._log("+/-") self._log(result_all[self._srcname]["Prefactor"].error()) self._log("\n") # Initialise results colnames = [] values = {} # Set TS value colnames.append("TS") values["TS"] = ts # Set logL for background fit colnames.append("LogL_bgm") values["LogL_bgm"] = LogL_bgm # Set logL for full fit colnames.append("LogL_all") values["LogL_all"] = LogL_all # Set Nevents colnames.append("Nevents") values["Nevents"] = nevents # Set Npred for background fit colnames.append("Npred_bkg") values["Npred_bkg"] = npred_bgm # Set Npred for full fit colnames.append("Npred_all") values["Npred_all"] = npred_all # Gather free full fit parameters for i in range(result_all.size()): model = result_all[i] model_name = model.name() for k in range(model.size()): par = model[k] if par.is_free(): # Set parameter name name = model_name + "_" + par.name() # Append value colnames.append(name) values[name] = par.value() # Append error name = "Unc_" + name colnames.append(name) values[name] = par.error() # Bundle together results result = {'colnames': colnames, 'values': values} # Return return result
def _get_sensitivity(self, emin, emax, bkg_model, full_model): """ Determine sensitivity for given observations. Args: emin: Minimum energy for fitting and flux computation emax: Maximum energy for fitting and flux computation bkg_model: Background model full_model: Source model Returns: Result dictionary """ # Set TeV->erg conversion factor tev2erg = 1.6021764 # Set energy boundaries self._set_obs_ebounds(emin, emax) # Determine energy boundaries from first observation in the container loge = math.log10(math.sqrt(emin.TeV() * emax.TeV())) e_mean = math.pow(10.0, loge) erg_mean = e_mean * tev2erg # Compute Crab unit (this is the factor with which the Prefactor needs # to be multiplied to get 1 Crab crab_flux = self._get_crab_flux(emin, emax) src_flux = full_model[self._srcname].spectral().flux(emin, emax) crab_unit = crab_flux / src_flux # Write header if self._logTerse(): self._log("\n") self._log.header2("Energies: " + str(emin) + " - " + str(emax)) self._log.parformat("Crab flux") self._log(crab_flux) self._log(" ph/cm2/s\n") self._log.parformat("Source model flux") self._log(src_flux) self._log(" ph/cm2/s\n") self._log.parformat("Crab unit factor") self._log(crab_unit) self._log("\n") # Initialise loop crab_flux_value = [] photon_flux_value = [] energy_flux_value = [] sensitivity_value = [] iterations = 0 test_crab_flux = 0.1 # Initial test flux in Crab units (100 mCrab) # Loop until we break while True: # Update iteration counter iterations += 1 # Write header if self._logExplicit(): self._log.header2("Iteration " + str(iterations)) # Set source model. crab_prefactor is the Prefactor that # corresponds to 1 Crab src_model = full_model.copy() crab_prefactor = src_model[self._srcname]['Prefactor'].value() * \ crab_unit src_model[self._srcname]['Prefactor'].value(crab_prefactor * \ test_crab_flux) self._obs.models(src_model) # Simulate events sim = obsutils.sim(self._obs, nbins=self._enumbins, seed=iterations, binsz=self._binsz, npix=self._npix, log=self._log_clients, debug=self._debug, edisp=self._edisp) # Determine number of events in simulation nevents = 0.0 for run in sim: nevents += run.events().number() # Write simulation results if self._logExplicit(): self._log.header3("Simulation") self._log.parformat("Number of simulated events") self._log(nevents) self._log("\n") # Fit background only sim.models(bkg_model) like = obsutils.fit(sim, log=self._log_clients, debug=self._debug, edisp=self._edisp) result_bgm = like.obs().models().copy() LogL_bgm = like.opt().value() npred_bgm = like.obs().npred() # Assess quality based on a comparison between Npred and Nevents quality_bgm = npred_bgm - nevents # Write background fit results if self._logExplicit(): self._log.header3("Background model fit") self._log.parformat("log likelihood") self._log(LogL_bgm) self._log("\n") self._log.parformat("Number of predicted events") self._log(npred_bgm) self._log("\n") self._log.parformat("Fit quality") self._log(quality_bgm) self._log("\n") # Write model fit results if self._logExplicit(): for model in result_bgm: self._log.parformat("Model") self._log(model.name()) self._log("\n") for par in model: self._log(str(par) + "\n") # Fit background and test source sim.models(src_model) like = obsutils.fit(sim, log=self._log_clients, debug=self._debug, edisp=self._edisp) result_all = like.obs().models().copy() LogL_all = like.opt().value() npred_all = like.obs().npred() ts = 2.0 * (LogL_bgm - LogL_all) # Assess quality based on a comparison between Npred and Nevents quality_all = npred_all - nevents # Write background and test source fit results if self._logExplicit(): self._log.header3("Background and test source model fit") self._log.parformat("Test statistics") self._log(ts) self._log("\n") self._log.parformat("log likelihood") self._log(LogL_all) self._log("\n") self._log.parformat("Number of predicted events") self._log(npred_all) self._log("\n") self._log.parformat("Fit quality") self._log(quality_all) self._log("\n") # for model in result_all: self._log.parformat("Model") self._log(model.name()) self._log("\n") for par in model: self._log(str(par) + "\n") # Start over if TS was non-positive if ts <= 0.0: if self._logExplicit(): self._log("Non positive TS. Start over.\n") continue # Get fitted Crab, photon and energy fluxes crab_flux = result_all[self._srcname]['Prefactor'].value() / \ crab_prefactor #crab_flux_err = result_all[self._srcname]['Prefactor'].error() / \ # crab_prefactor photon_flux = result_all[self._srcname].spectral().flux(emin, emax) energy_flux = result_all[self._srcname].spectral().eflux( emin, emax) # Compute differential sensitivity in unit erg/cm2/s energy = gammalib.GEnergy(e_mean, "TeV") time = gammalib.GTime() sensitivity = result_all[self._srcname].spectral().eval(energy, time) * \ e_mean*erg_mean * 1.0e6 # Compute flux correction factor based on average TS correct = 1.0 if ts > 0: correct = math.sqrt(self._ts_thres / ts) # Compute extrapolated fluxes crab_flux = correct * crab_flux photon_flux = correct * photon_flux energy_flux = correct * energy_flux sensitivity = correct * sensitivity crab_flux_value.append(crab_flux) photon_flux_value.append(photon_flux) energy_flux_value.append(energy_flux) sensitivity_value.append(sensitivity) # Write background and test source fit results if self._logExplicit(): self._log.parformat("Photon flux") self._log(photon_flux) self._log(" ph/cm2/s\n") self._log.parformat("Energy flux") self._log(energy_flux) self._log(" erg/cm2/s\n") self._log.parformat("Crab flux") self._log(crab_flux * 1000.0) self._log(" mCrab\n") self._log.parformat("Differential sensitivity") self._log(sensitivity) self._log(" erg/cm2/s\n") for model in result_all: self._log.parformat("Model") self._log(model.name()) self._log("\n") for par in model: self._log(str(par) + "\n") elif self._logTerse(): self._log.parformat("Iteration " + str(iterations)) self._log("TS=") self._log(ts) self._log(" ") self._log("corr=") self._log(correct) self._log(" ") self._log(photon_flux) self._log(" ph/cm2/s = ") self._log(energy_flux) self._log(" erg/cm2/s = ") self._log(crab_flux * 1000.0) self._log(" mCrab = ") self._log(sensitivity) self._log(" erg/cm2/s\n") # Compute sliding average of extrapolated fitted prefactor, # photon and energy flux. This damps out fluctuations and # improves convergence crab_flux = 0.0 photon_flux = 0.0 energy_flux = 0.0 sensitivity = 0.0 num = 0.0 for k in range(self._num_avg): inx = len(crab_flux_value) - k - 1 if inx >= 0: crab_flux += crab_flux_value[inx] photon_flux += photon_flux_value[inx] energy_flux += energy_flux_value[inx] sensitivity += sensitivity_value[inx] num += 1.0 crab_flux /= num photon_flux /= num energy_flux /= num sensitivity /= num # Compare average flux to last average if iterations > self._num_avg: if test_crab_flux > 0: ratio = crab_flux / test_crab_flux # We have 2 convergence criteria: # 1. The average flux does not change # 2. The flux correction factor is small if ratio >= 0.99 and ratio <= 1.01 and \ correct >= 0.9 and correct <= 1.1: if self._logTerse(): self._log(" Converged (" + str(ratio) + ")\n") break else: if self._logTerse(): self._log(" Flux is zero.\n") break # Use average for next iteration test_crab_flux = crab_flux # Exit loop if number of trials exhausted if (iterations >= self._max_iter): if self._logTerse(): self._log(" Test ended after " + str(self._max_iter) + " iterations.\n") break # Write fit results if self._logTerse(): self._log.header3("Fit results") self._log.parformat("Test statistics") self._log(ts) self._log("\n") self._log.parformat("Photon flux") self._log(photon_flux) self._log(" ph/cm2/s\n") self._log.parformat("Energy flux") self._log(energy_flux) self._log(" erg/cm2/s\n") self._log.parformat("Crab flux") self._log(crab_flux * 1000.0) self._log(" mCrab\n") self._log.parformat("Differential sensitivity") self._log(sensitivity) self._log(" erg/cm2/s\n") self._log.parformat("Number of simulated events") self._log(nevents) self._log("\n") self._log.header3("Background and test source model fitting") self._log.parformat("log likelihood") self._log(LogL_all) self._log("\n") self._log.parformat("Number of predicted events") self._log(npred_all) self._log("\n") for model in result_all: self._log.parformat("Model") self._log(model.name()) self._log("\n") for par in model: self._log(str(par) + "\n") self._log.header3("Background model fit") self._log.parformat("log likelihood") self._log(LogL_bgm) self._log("\n") self._log.parformat("Number of predicted events") self._log(npred_bgm) self._log("\n") for model in result_bgm: self._log.parformat("Model") self._log(model.name()) self._log("\n") for par in model: self._log(str(par) + "\n") # Restore energy boundaries of observation container for i, obs in enumerate(self._obs): obs.events().ebounds(self._obs_ebounds[i]) # Store result result = {'loge': loge, 'emin': emin.TeV(), 'emax': emax.TeV(), \ 'crab_flux': crab_flux, 'photon_flux': photon_flux, \ 'energy_flux': energy_flux, \ 'sensitivity': sensitivity} # Return result return result
# Remove any existing result files list = [glob.glob("*.fits"), glob.glob("*.log"), glob.glob("*.xml")] for files in list: for file in files: os.remove(file) # Setup single observation survey #obs = survey_single() obs = survey_gplane() # Add background model obs = add_background_model(obs) # Simulate events print("Simulate events") obs = obsutils.sim(obs) # Make counts map print("Make counts map") cntmap = obsutils.cntmap(obs) # Fit observations print("Fit observations") like = obsutils.fit(obs) #print like.opt() #print like.obs().models() # Make model map print("Make model map (this step will take some time)") modmap = obsutils.modmap(obs)