def augment_error_str(err_str, model, comp, par_names): """Append more stuff to an error string WARNING: model must be the same as for the original error string. This provides a crude mechanism to construct error strings for different components in a given model """ assert model.name == err_str.split(':')[0] for p_name in par_names: par = comp.__getattribute__(p_name) # Does NOT modify string in calling scope err_str = err_str + ",{:d}".format(xs_utils.par_num(model, par)) return err_str
def bkg_only_fit(output, steppar=False, error=False): """ Fit bkg region alone to XRB model """ out = g309.load_data_and_models(["bkg"], snr_model=None) set_energy_range(out['bkg']) xs.AllData.ignore("bad") # Reset XRB to "typical" values # As usual, fit is pretty sensitive to initial values # (initial kT values that are too small drive fit to a bad local minimum) xrb = xs.AllModels(1, 'xrb') xrb.setPars({xrb.apec.kT.index : "0.2, , 0, 0, 0.5, 1"}, # Unabsorped apec (local bubble) {xrb.tbnew_gas.nH.index : "1.5, , 0.01, 0.1, 5, 10"}, # Galactic absorption {xrb.apec_6.kT.index : "0.7, , 0, 0, 2, 4"}, # Absorbed apec (galactic halo) {xrb.apec.norm.index : 1e-3}, {xrb.apec_6.norm.index : 1e-3} ) xrb.apec.kT.frozen = False xrb.tbnew_gas.nH.frozen = False xrb.apec_6.kT.frozen = False xrb.apec.norm.frozen = False xrb.apec_6.norm.frozen = False xs.Fit.perform() if xs.Plot.device == "/xs": xs.Plot("ldata delchi") if steppar: xs.Fit.steppar("xrb:{:d} 0.1 0.5 20".format(xs_utils.par_num(xrb, xrb.apec.kT))) xs.Fit.steppar("xrb:{:d} 0.4 0.8 20".format(xs_utils.par_num(xrb, xrb.apec_6.kT))) if xs.Plot.device == "/xs": xs.Plot("ldata delchi") if error: xs.Xset.openLog(output + "_error.log") print "Error run started:", datetime.now() xs.Fit.error("xrb:{:d}".format(xs_utils.par_num(xrb, xrb.apec.kT)) + " xrb:{:d}".format(xs_utils.par_num(xrb, xrb.apec.norm)) + " xrb:{:d}".format(xs_utils.par_num(xrb, xrb.tbnew_gas.nH)) + " xrb:{:d}".format(xs_utils.par_num(xrb, xrb.apec_6.kT)) + " xrb:{:d}".format(xs_utils.par_num(xrb, xrb.apec_6.norm))) print "Error run complete:", datetime.now() xs.Xset.closeLog() # Dump useful things here... products(output) print_model(xrb, output + "_xrb.txt")
def error_str_all_free(model): """Construct error string for all free and unlinked parameters in an XSPEC model """ # Alternative: we could just write 1--len(model.nParameters) # but this tends to generate warnings err_str = model.name + ":" for par in xs_utils.get_all_pars(model): if not par.frozen and par.link == '': err_str += ",{:d}".format(xs_utils.par_num(model, par)) err_str = err_str.replace(":,", ":") return err_str
def single_fit(output, region='src', with_bkg=True, free_elements=None, error=False, error_rerun=False, tau_scan=False, tau_freeze=None, nH_freeze=None, snr_model='vnei', **kwargs): """Fit any region to an arbitrary remnant model, possibly fitting XRB with background region as well. Arguments output: file stem string Keyword arguments region: region spectrum to be fitted with_bkg: fit to X-ray background region (bkg) simultaneously snr_model: snr model expression (and parameter setup) to use free_elements: (default) is [Si,S] if [], use solar abundances error: perform single error run tau_scan: steppar over plausible Tau values to ensure convergence to "correct" best fit tau_freeze: freeze ionization timescale to provided value nH_freeze: freeze SNR absorption to provided value kwargs - passed to g309_models.load_data_and_models (suffix, mosmerge, marfrmf) """ if free_elements is None: free_elements = ['Si', 'S'] # Set up spectra and models in XSPEC if with_bkg: out = g309.load_data_and_models([region, 'bkg'], snr_model=snr_model, **kwargs) set_energy_range(out['bkg']) else: out = g309.load_data_and_models([region], **kwargs) set_energy_range(out[region]) xs.AllData.ignore("bad") xrb = xs.AllModels(1, 'xrb') snr = xs.AllModels(1, 'snr_' + region) # Reset XRB to "typical" values, do NOT vary yet if with_bkg: xrb.setPars({xrb.apec.kT.index : "0.1, , 0, 0, 0.5, 1"}, # Unabsorped apec (local bubble) {xrb.tbnew_gas.nH.index : "1, , 0.1, 0.5, 5, 10"}, # Extragalactic absorption {xrb.tbnew_gas_5.nH.index : "1, , 0.1, 0.5, 5, 10"}, # Ridge absorption {xrb.apec_6.kT.index : "0.5, , 0, 0, 2, 4"}, # Galactic ridge (+ minimal halo maybe) {xrb.apec.norm.index : 1e-3}, {xrb.apec_6.norm.index : 1e-3} ) xs_utils.freeze_model(xrb) # Try floating norms to help initial fit xrb.apec.norm.frozen = False xrb.apec_6.norm.frozen = False def thaw_bkg(): for c in [xrb.apec.kT, xrb.tbnew_gas.nH, xrb.tbnew_gas_5.nH, xrb.apec_6.kT]: c.frozen = False xs.Fit.renorm() # Let SNR model vary (NOTE: this assumes default to be vnei...) if snr_model.startswith('vnei'): xs_utils.freeze_model(snr) # Configure initial SNR parameters if nH_freeze: snr.tbnew_gas.nH = nH_freeze else: snr.tbnew_gas.nH.frozen=False snr.vnei.kT.frozen=False snr.vnei.norm.frozen=False if tau_freeze: snr.vnei.Tau = tau_freeze else: snr.vnei.Tau.frozen = False for elem in free_elements: comp = snr.vnei.__getattribute__(elem) comp.frozen = False if snr_model == 'vnei+nei': snr.nei.norm = 0 elif snr_model == 'vnei+powerlaw': snr.powerlaw.PhoIndex = 2 snr.powerlaw.norm = 0 # zero elif snr_model == 'vnei+srcutlog': # srcutlog, w/ one free parameter, behaves better than powerlaw snr.srcutlog.__getattribute__('break').frozen = False # Run initial fit if with_bkg: # Fit has enormous trouble converging if tau_freeze: thaw_bkg() xs.Fit.perform() else: snr.vnei.Tau = 2e10 snr.vnei.Tau.frozen = True xs.Fit.perform() thaw_bkg() xs.Fit.perform() snr.vnei.Tau.frozen = False xs.Fit.perform() else: xs.Fit.perform() # Post-processing on initial fit if tau_scan: xs.Fit.steppar("log {:s}:{:d} 1e9 5e13 15".format(snr.name, xs_utils.par_num(snr, snr.vnei.Tau))) if snr_model == 'vnei+nei': snr.nei.kT.frozen = False snr.nei.Tau.frozen = False snr.nei.norm.frozen = False xs.Fit.perform() elif snr_model == 'vnei+powerlaw': snr.powerlaw.PhoIndex = 2 snr.powerlaw.norm = 0 # zero snr.powerlaw.norm.frozen = False xs.Fit.perform() snr.powerlaw.PhoIndex.frozen = False xs.Fit.perform() # Because powerlaw norm generally runs to zero, traverse moderately # strong power law cases xs.Fit.steppar("log {:s}:{:d} 1e-5 1e-2 30".format(snr.name, xs_utils.par_num(snr, snr.powerlaw.norm))) elif snr_model == 'vnei+srcutlog': # Check reasonably high break values: 15 -- 17 xs.Fit.steppar("{:s}:{:d} 15 17 20".format(snr.name, xs_utils.par_num(snr, snr.srcutlog.__getattribute__('break')) )) elif snr_model == 'vpshock': xs_utils.freeze_model(snr) snr.tbnew_gas.nH.frozen=False snr.vpshock.kT.frozen=False snr.vpshock.norm.frozen=False if tau_freeze: raise Exception("ERROR: vpshock not configured for fixed Tau") for elem in free_elements: comp = snr.vpshock.__getattribute__(elem) comp.frozen = False # vpshock fits are very ill behaved, must coerce into best fit snr.vpshock.Tau_l = 1e8 snr.vpshock.Tau_u = 5e10 xs.Fit.perform() if with_bkg: thaw_bkg() xs.Fit.perform() snr.vpshock.Tau_l.frozen = False snr.vpshock.Tau_u.frozen = False xs.Fit.perform() if tau_scan: # Since Tau_u is constrained to be greater than Tau_l (?) this # should ensure that both Tau_u and Tau_l traverse a range of # values. But I haven't checked it yet... xs.Fit.steppar("log {:s}:{:d} 1e9 5e13 15".format(snr.name, xs_utils.par_num(snr, snr.vpshock.Tau_u))) else: raise Exception("Invalid SNR model - please add branch") # Compute standard 90% errors if error: xs.Xset.openLog(output + "_error.log") if with_bkg: xs.Fit.error(error_str_all_free(xrb)) xs.Fit.error(error_str_all_free(snr)) if error_rerun: if with_bkg: xs.Fit.error(error_str_all_free(xrb)) xs.Fit.error(error_str_all_free(snr)) xs.Xset.closeLog() # Dump standard outputs products(output) print_model(snr, output + "_{:s}.txt".format(snr.name)) if with_bkg: print_model(xrb, output + "_xrb.txt")
def error_str(model, comp, par_names): """Construct an error string""" pars = [comp.__getattribute__(par_name) for par_name in par_names] par_nums = ["{:d}".format(xs_utils.par_num(model, par)) for par in pars] err_str = model.name + ":" + ",".join(par_nums) return err_str
def single_fit(output, region='src', with_bkg=True, free_elements=None, error=False, error_rerun=False, tau_scan=False, tau_freeze=None, nH_freeze=None, snr_model='vnei', **kwargs): """Fit any region to an arbitrary remnant model, possibly fitting XRB with background region as well. Arguments output: file stem string Keyword arguments region: region spectrum to be fitted with_bkg: fit to X-ray background region (bkg) simultaneously snr_model: snr model expression (and parameter setup) to use free_elements: (default) is [Si,S] if [], use solar abundances error: perform single error run tau_scan: steppar over plausible Tau values to ensure convergence to "correct" best fit tau_freeze: freeze ionization timescale to provided value nH_freeze: freeze SNR absorption to provided value kwargs - passed to g309_models.load_data_and_models (suffix, mosmerge, marfrmf) """ if free_elements is None: free_elements = ['Si', 'S'] # Set up spectra and models in XSPEC if with_bkg: out = g309.load_data_and_models([region, 'bkg'], snr_model=snr_model, **kwargs) set_energy_range(out['bkg']) else: out = g309.load_data_and_models([region], snr_model=snr_model, **kwargs) set_energy_range(out[region]) xs.AllData.ignore("bad") if snr_model == 'gauss': for extr in out[region]: extr.spec.ignore("**-5.0, 8.0-**") xrb = xs.AllModels(1, 'xrb') snr = xs.AllModels(1, 'snr_' + region) # Reset XRB to "typical" values, do NOT vary yet if with_bkg: xrb.setPars({xrb.apec.kT.index : "0.1, , 0, 0, 0.5, 1"}, # Unabsorped apec (local bubble) {xrb.tbnew_gas.nH.index : "1, , 0.1, 0.5, 5, 10"}, # Extragalactic absorption {xrb.tbnew_gas_5.nH.index : "1, , 0.1, 0.5, 5, 10"}, # Ridge absorption {xrb.apec_6.kT.index : "0.5, , 0, 0, 2, 4"}, # Galactic ridge (+ minimal halo maybe) {xrb.apec.norm.index : 1e-3}, {xrb.apec_6.norm.index : 1e-3} ) xs_utils.freeze_model(xrb) # Try floating norms to help initial fit xrb.apec.norm.frozen = False xrb.apec_6.norm.frozen = False def thaw_bkg(): for c in [xrb.apec.kT, xrb.tbnew_gas.nH, xrb.tbnew_gas_5.nH, xrb.apec_6.kT]: c.frozen = False xs.Fit.renorm() # Let SNR model vary (NOTE: this assumes default to be vnei...) if snr_model.startswith('vnei'): xs_utils.freeze_model(snr) # Configure initial SNR parameters if nH_freeze: snr.tbnew_gas.nH = nH_freeze else: snr.tbnew_gas.nH.frozen=False snr.vnei.kT.frozen=False snr.vnei.norm.frozen=False if tau_freeze: snr.vnei.Tau = tau_freeze else: snr.vnei.Tau.frozen = False for elem in free_elements: comp = snr.vnei.__getattribute__(elem) comp.frozen = False if snr_model == 'vnei+nei': snr.nei.norm = 0 elif snr_model == 'vnei+powerlaw': snr.powerlaw.PhoIndex = 2 snr.powerlaw.norm = 0 # zero elif snr_model == 'vnei+srcutlog': # srcutlog, w/ one free parameter, behaves better than powerlaw snr.srcutlog.__getattribute__('break').frozen = False # Run initial fit if with_bkg: # Fit has enormous trouble converging if tau_freeze: thaw_bkg() xs.Fit.perform() else: snr.vnei.Tau = 2e10 snr.vnei.Tau.frozen = True xs.Fit.perform() thaw_bkg() xs.Fit.perform() snr.vnei.Tau.frozen = False xs.Fit.perform() else: xs.Fit.perform() # Post-processing on initial fit if tau_scan: xs.Fit.steppar("log {:s}:{:d} 1e9 5e13 15".format(snr.name, xs_utils.par_num(snr, snr.vnei.Tau))) if snr_model == 'vnei+nei': snr.nei.kT.frozen = False snr.nei.Tau.frozen = False snr.nei.norm.frozen = False xs.Fit.perform() elif snr_model == 'vnei+powerlaw': snr.powerlaw.PhoIndex = 2 snr.powerlaw.norm = 0 # zero snr.powerlaw.norm.frozen = False xs.Fit.perform() snr.powerlaw.PhoIndex.frozen = False xs.Fit.perform() # Because powerlaw norm generally runs to zero, traverse moderately # strong power law cases xs.Fit.steppar("log {:s}:{:d} 1e-5 1e-2 30".format(snr.name, xs_utils.par_num(snr, snr.powerlaw.norm))) elif snr_model == 'vnei+srcutlog': # Check reasonably high break values: 15 -- 17 xs.Fit.steppar("{:s}:{:d} 15 17 20".format(snr.name, xs_utils.par_num(snr, snr.srcutlog.__getattribute__('break')) )) elif snr_model == 'vpshock': xs_utils.freeze_model(snr) snr.tbnew_gas.nH.frozen=False snr.vpshock.kT.frozen=False snr.vpshock.norm.frozen=False if tau_freeze: raise Exception("ERROR: vpshock not configured for fixed Tau") for elem in free_elements: comp = snr.vpshock.__getattribute__(elem) comp.frozen = False # vpshock fits are very ill behaved, must coerce into best fit snr.vpshock.Tau_l = 1e8 snr.vpshock.Tau_u = 5e10 xs.Fit.perform() if with_bkg: thaw_bkg() xs.Fit.perform() snr.vpshock.Tau_l.frozen = False snr.vpshock.Tau_u.frozen = False xs.Fit.perform() if tau_scan: # Since Tau_u is constrained to be greater than Tau_l (?) this # should ensure that both Tau_u and Tau_l traverse a range of # values. But I haven't checked it yet... xs.Fit.steppar("log {:s}:{:d} 1e9 5e13 15".format(snr.name, xs_utils.par_num(snr, snr.vpshock.Tau_u))) elif snr_model == 'gauss': # Not supported! Not at all constrained . . . assert not with_bkg xs_utils.freeze_model(snr) # Coupling between code is too tight, abstractions keep leaking. # Because models must be addressed by NUMBER in XSPEC # commands to tweak model parameters necessarily break # abstraction interface between "load models" and "fit models" # Possible solutions: (1) give up and let interfaces merge # (monolithic "g309_models_fits"), or . . . (2) stop whining if 'mosmerge' not in kwargs or kwargs['mosmerge']: instr_1 = xs.AllModels(1, 'instr_1') #instr_2 = xs.AllModels(2, 'instr_2') # Let PN instr lines fit instr_3 = xs.AllModels(3, 'instr_3') for instr in [instr_1, instr_3]: # Must update parameter lower limits instr.constant.factor.values = "0, , 0, 0, , " instr.constant.factor.frozen = True else: # No MOS merging instr_1 = xs.AllModels(1, 'instr_1') instr_2 = xs.AllModels(2, 'instr_2') #instr_3 = xs.AllModels(3, 'instr_3') # Let PN instr lines fit instr_4 = xs.AllModels(4, 'instr_4') instr_5 = xs.AllModels(5, 'instr_5') for instr in [instr_1, instr_2, instr_4, instr_5]: # Must update parameter lower limits instr.constant.factor.values = "0, , 0, 0, , " instr.constant.factor.frozen = True snr.tbnew_gas.nH = 0 snr.tbnew_gas.nH.frozen = True # LineE: lower/upper bounds set from Yamaguchi+ 2014 # Sigma: prevent line from over-widening to fit as "constant" addition # norm: no bounds snr.setPars({snr.gaussian.LineE.index : "6.55, , 6.2, 6.3, 6.8, 6.9", snr.gaussian.Sigma.index : "0.1, , 0, 0, 0.2, 0.5"} ) snr.gaussian.LineE.frozen=False snr.gaussian.Sigma.frozen=False snr.gaussian.norm.frozen=False xs.Fit.perform() else: raise Exception("Invalid SNR model - please add branch") # Compute standard 90% errors if error: xs.Xset.openLog(output + "_error.log") if with_bkg: xs.Fit.error(error_str_all_free(xrb)) xs.Fit.error(error_str_all_free(snr)) if error_rerun: if with_bkg: xs.Fit.error(error_str_all_free(xrb)) xs.Fit.error(error_str_all_free(snr)) xs.Xset.closeLog() # Dump standard outputs products(output) print_model(snr, output + "_{:s}.txt".format(snr.name)) if with_bkg: print_model(xrb, output + "_xrb.txt")