def mod_from_row(iso, row, props, name_col='tmass_key', rootdir=os.path.join(PROJECT_DIR, 'fit_results'), halo_fraction=0.5, tag=None, **kwargs): kwargs.update({ p: (np.float64(row[p]), np.float64(row['{}_unc'.format(p)])) for p in props if row[p] > 0 and row['{}_unc'.format(p)] > 0 }) if 'feh' in props: if np.isfinite(row['feh']): kwargs.update( {'feh': (np.float64(row['feh']), np.float64(row['feh_unc']))}) name = row[name_col] if tag is not None: name = '{}_{}'.format(name, tag) kwargs.update({'name': name, 'directory': os.path.join(rootdir, name)}) max_distance = min((1000. / (row['parallax'] - row['parallax_unc'])) * 2, 30000) kwargs['max_distance'] = max_distance kwargs['halo_fraction'] = halo_fraction mod = BasicStarModel(iso, **kwargs) mod.set_prior('distance', FlatPrior((0, max_distance))) AV_inf = get_AV_infinity(row['ra_1'], row['dec_1']) mod.set_prior('feh', FehPrior(halo_fraction=halo_fraction, local=False)) mod.set_prior('AV', GaussianPrior(AV_inf, AV_inf / 2, bounds=(0, 5))) mod.set_bounds(mass=(0.1, 20), feh=iso.model_grid.get_limits('feh')) print(mod.mnest_basename) return mod
def setUp(self): mist = get_ichrone("mist") sfh = StarFormationHistory() # Constant SFR for 10 Gyr; or, e.g., dist=norm(3, 0.2) imf = SalpeterPrior(bounds=(0.4, 10)) # bounds on solar masses binaries = BinaryDistribution(fB=0.4, gamma=0.3) feh = GaussianPrior(-0.2, 0.2) distance = DistancePrior(max_distance=3000) # pc AV = AVPrior(bounds=[0, 2]) pop = StarPopulation( mist, sfh=sfh, imf=imf, feh=feh, distance=distance, binary_distribution=binaries, AV=AV ) self.pop = pop self.mist = mist self.df = pop.generate(1000) self.dereddened_df = deredden(mist, self.df)
def fit(self, inits=[329.58, 9.5596, -.0478, 260, .0045], nwalkers=50, max_n=100000, thin_by=100, burnin=0, iso_only=False, gyro_only=False, optimize=False, rossby=True, model="praesepe", seed=None, save_samples=False): """Run MCMC on a star using emcee. Explore the posterior probability density function of the stellar parameters using MCMC (via emcee). Args: inits (Optional[array-like]): A list of initial values to use for EEP, age (in log10[yrs]), feh, distance (in pc) and Av. nwalkers (Optional[int]): The number of walkers to use with emcee. The default is 50. max_n (Optional[int]): The maximum number of samples to obtain (although not necessarily to save -- see thin_by). The default is 100000. thin_by (Optional[int]): Only one in every thin_by samples will be saved. The default is 100. Set = 1 to save every sample (note this substantially slows down the MCMC process because of the additional I/O time. burnin (Optional[int]): The number of SAVED samples to throw away when accessing the results. This number cannot exceed the number of saved samples (which is max_n/thin_by). Default = 0. iso_only (Optional[bool]): If true only the isochronal likelihood function will be used. gyro_only (Optional[bool]): If true only the gyrochronal likelihood function will be used. Beware: this may not do what you might assume it does... In general this setting is not currently very useful! optimize (Optional[bool]): If True, initial parameters will be found via optimization. Default is False. rossby (Optional[bool]): If True, magnetic braking will cease after Ro = 2. Default is True. model (Optional[bool]): The gyrochronology model to use. The default is "praesepe" (the Praesepe-based model). Can also be "angus15" for the Angus et al. (2015) model. seed (Optional[int]): The random number seed. Set this if you want to regenerate exactly the same samples each time. save_samples (Optional[bool]): saving samples is the computational bottleneck. If you want to save time and don't need to save the samples using the HDF5 backend, set this to False. """ self.max_n = max_n self.nwalkers = nwalkers self.thin_by = thin_by self.save_samples = save_samples if burnin > max_n/thin_by: burnin = int(max_n/thin_by/3) print("Automatically setting burn in to {}".format(burnin)) p_init = [inits[0], inits[1], inits[2], np.log(inits[3]), inits[4]] self.p_init = p_init if seed is not None: np.random.seed(seed) # Create the directory if it doesn't already exist. if save_samples: if not os.path.exists(self.savedir): os.makedirs(self.savedir) # Set up the backend # Don't forget to clear it in case the file already exists ndim = 5 if save_samples: fn = "{0}/{1}.h5".format(self.savedir, self.filename) backend = emcee.backends.HDFBackend(fn) backend.reset(nwalkers, ndim) self.backend = backend # Set up the StarModel object needed to calculate the likelihood. mod = SingleStarModel(mist, **self.iso_params) # StarModel isochrones obj # mod.set_prior(age=FlatPrior(bounds=(8, 10.14))) # Set up a Gaussian prior with the extinction, if provided. if self.Av_mean is not None and self.Av_sigma is not None: mod.set_prior(AV=GaussianPrior(self.Av_mean, self.Av_sigma, bounds=(0, 1))) # lnprob arguments args = [mod, self.prot, self.prot_err, iso_only, gyro_only, rossby, model] self.args = args # Optimize. Try a few inits and pick the best. if optimize: neep, nage = 5, 5 likes1, likes2 = np.zeros(nage), np.zeros(neep) likes = np.zeros((neep, nage)) inds = np.zeros(nage) result_list = np.zeros((neep, nage, 5)) EEPS, AGES = np.meshgrid(np.linspace(200, 500, neep), np.linspace(7, 10., nage), indexing="ij") for i in range(neep): for j in range(nage): inits = [EEPS[i, j], AGES[i, j], 0., np.log(1000.), .01] results = spo.minimize(nll, inits, args=args) likes1[j] = lnprob(results.x, *args)[0] likes[i, j] = likes1[j] result_list[i, j, :] = results.x inds[i] = np.argmax(likes1) likes2[i] = max(likes1) eep_ind = np.argmax(likes2) age_ind = int(inds[eep_ind]) self.p_init = result_list[eep_ind, age_ind, :] # Run the MCMC sampler = self.run_mcmc() self.sampler = sampler nwalkers, nsteps, ndim = np.shape(self.sampler.chain) print("nsteps", nsteps, "burnin", burnin) self.samples = np.reshape(self.sampler.chain[:, burnin:, :], (nwalkers*(nsteps-burnin), ndim))
def main(index, overwrite=False): # First make sure paths exist: # os.makedirs('../cache', exist_ok=True) # os.makedirs('../plots/isochrones', exist_ok=True) # Load the DECam photometry decam = load_data('../data/decam_apw.fits') iso = MIST_Isochrone(['PanSTARRS_g', 'PanSTARRS_i', 'SkyMapper_u']) row = decam[index] name = 'lmcla-{0}-'.format(row['index']) model_file = '../cache/starmodels-{0}.hdf5'.format(str(row['index'])) if path.exists(model_file) and not overwrite: print('skipping {0} - already exists'.format(name)) sys.exit(0) # This is our "anchor star": it was identified as being near the turnoff, # bright, and positionally consistent with being in the LA cluster: j1, = np.where(decam['index'] == 24365)[0] j2 = index if j1 == j2: print('skipping anchor-anchor pair') sys.exit(0) # To fit pairs as resolved binaries, we have to construct the observation # tree manually: tree = ObservationTree() for b in ['PanSTARRS_g', 'PanSTARRS_i', 'SkyMapper_u']: survey, band = b.split('_') if band == 'u' and decam[band.capitalize() + 'MAG'][j2] > 21: extra_s = 0.2 else: extra_s = 0.005 o = Observation(survey, b, 1.) s0 = Source( decam[band.capitalize() + 'MAG'][j1], np.sqrt(0.005**2 + decam[band.capitalize() + 'ERR'][j1]**2)) s1 = Source(decam[band.capitalize() + 'MAG'][j2], np.sqrt(extra_s**2 + decam[band.capitalize() + 'ERR'][j2]**2), separation=100.) o.add_source(s0) o.add_source(s1) tree.add_observation(o) model = StarModel(ic=iso, obs=tree, N=[1, 2]) # model = StarModel(ic=iso, obs=tree) print('setting priors') dist_bounds = [1 * 1e3, 100 * 1e3] # pc # model._priors['distance'] = FlatPrior(dist_bounds) model._priors['distance'] = GaussianPrior(30 * 1e3, 10 * 1e3, bounds=dist_bounds) model.set_bounds(distance=dist_bounds) # 1 to 100 kpc feh_bounds = (-2., 0.5) model.set_bounds(feh=feh_bounds) # model._priors['feh'] = FlatPrior(feh_bounds) model._priors['feh'] = GaussianPrior(-1.1, 0.5, bounds=feh_bounds) AV_bounds = (0, 1) model.set_bounds(AV=AV_bounds) model._priors['AV'] = PowerLawPrior(-1.1, (1e-3, 1)) # model._priors['AV'] = GaussianPrior(0.2, 0.1, bounds=AV_bounds) age_bounds = (7, 9.5) model.set_bounds(age=age_bounds) # model._priors['age'] = GaussianPrior(8, 0.5, bounds=age_bounds) model._priors['age'] = FlatPrior(age_bounds) print('sampling star {0}'.format(row['index'])) model.fit_multinest(basename=name, refit=overwrite, overwrite=overwrite, n_live_points=2048) # model.fit_mcmc(nwalkers=nwalkers, # p0=np.array([350., 8., -0.5, 30000., 0.1]), # nburn=1024, niter=2048) model.save_hdf(model_file) fig = model.corner_physical() fig.savefig('../plots/isochrones/{0}-physical.png'.format(row['index']), dpi=200) plt.close(fig) fig = model.corner_observed() fig.savefig('../plots/isochrones/{0}-observed.png'.format(row['index']), dpi=200) plt.close(fig) # model._samples = model.samples[::1024] # model.save_hdf(sm_model_file) sys.exit(0)
#=============== Observations ======================= for true, obs, unc in zip(list_mod, list_obs, list_unc): if np.isfinite(datum[obs]): params[true] = (datum[obs], datum[unc]) #====================================================== #------ Start the model --------------------- model = SingleStarModel(mist, **params) model.mnest_basename = dir_chain + str(ID) + "-" #--------- Prior ------------------- model.set_prior(age=AgePrior(), AV=GaussianPrior(prior_Av["loc"], prior_Av["scale"], bounds=(prior_Av["lower"], prior_Av["upper"])), distance=GaussianPrior(prior_distance["loc"], prior_distance["scale"], bounds=(prior_distance["lower"], prior_distance["upper"]))) #------ Fit --------------------------------- model.fit(n_live_points=1000, verbose=False) #------------------------------------------- #------ Statistics ------- mean = model.derived_samples.mean() stds = model.derived_samples.std() row = {identifier: str(ID)}
def estimate(bands, params, logg=True, out_folder='.'): """Estimate logg using MIST isochrones.""" mist = get_ichrone('mist', bands=bands) model = SingleStarModel(mist, **params) if 'distance' in params.keys(): dist, dist_e = params['distance'] elif 'parallax' in params.keys(): dist = 1 / (params['parallax'][0] * 0.001) dist_e = dist * params['parallax'][1] / params['parallax'][0] else: msg = 'No parallax or distance found.' msg += 'Aborting age and mass calculation.' InputError(msg).warn() return np.zeros(10), np.zeros(10) if 'feh' in params.keys(): fe, fe_e = params['feh'] if fe + fe_e >= 0.5: model._priors['feh'] = FlatPrior([-0.5, 0.5]) else: model._priors['feh'] = GaussianPrior(fe, fe_e) if 'mass' in params.keys(): m, m_e = params['mass'] model._priors['mass'] = GaussianPrior(m, m_e) if 'AV' in params.keys(): av, av_e = params['AV'] model._priors['AV'] = GaussianPrior(av, av_e) model._priors['distance'] = GaussianPrior(dist, dist_e) sampler = dynesty.NestedSampler( loglike, prior_transform, model.n_params + len(bands), nlive=500, bound='multi', sample='rwalk', logl_args=([model, params, bands]), ptform_args=([model]) ) try: sampler.run_nested(dlogz=0.01) except ValueError as e: dump_out = f'{out_folder}/isochrone_DUMP.pkl' pickle.dump(sampler.results, open(dump_out, 'wb')) DynestyError(dump_out, 'isochrone', e).__raise__() results = sampler.results samples = resample_equal( results.samples, np.exp(results.logwt - results.logz[-1]) ) ########################################################################### # Written by Dan Foreman-mackey # https://github.com/dfm/gaia-isochrones df = model._samples = pd.DataFrame( dict( zip( list(model.param_names), samples.T, ) ) ) model._derived_samples = model.ic( *[df[c].values for c in model.param_names]) model._derived_samples["parallax"] = 1000.0 / df["distance"] model._derived_samples["distance"] = df["distance"] model._derived_samples["AV"] = df["AV"] ########################################################################### if logg: samples = model._derived_samples['logg'] med, lo, up = credibility_interval(samples, 5) med_e = max([med - lo, up - med]) return med, med_e else: age_samples = 10 ** (model._derived_samples['age'] - 9) mass_samples = model._derived_samples['mass'] eep_samples = model._derived_samples['eep'] return age_samples, mass_samples, eep_samples