Exemplo n.º 1
0
def plot_Galactic_slice(Rmax, Zmax, component=None, log_scale=True):
	# Determine density in slice
	model = TGalacticModel()
	R = np.abs(np.linspace(-Rmax*1000, Rmax*1000, 500))
	Z = np.abs(np.linspace(-Zmax*1000, Zmax*1000, 500))
	RR, ZZ = np.meshgrid(R, Z)
	rho = model.rho_rz(RR.flatten(), ZZ.flatten(), component=component)
	
	# Remove overdensity at center
	idx = np.isfinite(rho)
	rho[~idx] = np.max(rho[idx])
	rho_sorted = rho.__copy__()
	rho_sorted.sort()
	rho_max = rho_sorted[int(0.95*(rho_sorted.size-1))]
	idx = (rho > rho_max)
	rho[idx] = rho_max
	
	if log_scale:
		rho = np.log(rho)
	
	rho.shape = (R.size, Z.size)
	
	# Set matplotlib options
	mplib.rc('text',usetex=True)
	mplib.rc('xtick.major', size=6)
	mplib.rc('xtick.minor', size=4)
	mplib.rc('axes', grid=True)
	
	# Set up figure
	fig = plt.figure(figsize=(12,7), dpi=100)
	title = None
	if log_scale:
		title = r'$\mathrm{Stellar\ number\ density\ (arbitrary\ log\ scale)}$'
	else:
		r'$\mathrm{Stellar\ number\ density\ (arbitrary\ scale)}$'
	fig.suptitle(title, fontsize=22, y=0.95)
	ax = fig.add_subplot(1,1,1)
	fig.subplots_adjust(top=0.88)
	ax.imshow(rho, extent=(-Rmax, Rmax, -Zmax, Zmax), cmap='gray')
	ax.set_xlabel(r'$R \, (\mathrm{kpc})$', fontsize=18)
	ax.set_ylabel(r'$Z \, (\mathrm{kpc})$', fontsize=18)
	ax.grid(which='major', alpha=0.2)
	
	fig.savefig('../plots/log_rho.png', dpi=300)
	plt.show()
Exemplo n.º 2
0
def main():
	parser = argparse.ArgumentParser(prog='gen_test_input.py',
	                                 description='Generates test input file for galstar.',
	                                 add_help=True)
	parser.add_argument('-N', type=int, default=None, help='# of stars to generate.')
	parser.add_argument('-rad', '--radius', type=float, default=None, help='Radius of beam.')
	parser.add_argument('-o', '--output', type=str, default=None,
	                    help='Output filename (creates Bayestar input file).')
	parser.add_argument('-lb', '--gal-lb', type=float, nargs=2,
	                    metavar='deg', default=(90., 10.),
	                    help='Galactic latitude and longitude, in degrees.')
	parser.add_argument('-EBV', '--mean-EBV', type=float, default=0.02,
	                    metavar='mags', help='Mean E(B-V) extinction.')
	parser.add_argument('--EBV-uniform', action='store_true',
	                    help='Draw E(B-V) from U(0,2).')
	parser.add_argument('-cl', '--clouds', type=float, nargs='+',
	                    default=None, metavar='mu Delta_EBV',
	                    help='Place clouds of reddening Delta_EBV at distances mu')
	parser.add_argument('-r', '--max-r', type=float, default=23.,
	                    metavar='mags', help='Limiting apparent r-band magnitude.')
	parser.add_argument('-lim', '--limiting-mag', metavar='mags', type=float,
	                    nargs=5, default=(22.4, 21.7, 21.7, 21.1, 20.1),
	                    help='Limiting magnitudes in grizy_{P1}.')
	parser.add_argument('-nb', '--n-bands', type=int, default=4,
	                    help='# of bands required to keep object.')
	parser.add_argument('-flat', '--flat', action='store_true',
	                    help='Draw parameters from flat distribution')
	parser.add_argument('-sh', '--show', action='store_true',
	                    help='Plot distribution of DM, Mr and E(B-V).')
	parser.add_argument('-err', '--scale-errors', type=float, default=1.,
	                    help='Stretch uncertainties by given amount.')
	parser.add_argument('-LF', '--luminosity-function', type=str, default=None,
	                    help='Filename of luminosity function.')
	parser.add_argument('-t', '--templates', type=str, default=None,
	                    help='Filename of stellar templates.')
	#parser.add_argument('-b', '--binary', action='store_true', help='Generate binary stars.')
	if 'python' in sys.argv[0]:
		offset = 2
	else:
		offset = 1
	args = parser.parse_args(sys.argv[offset:])
	
	# Parameters for Galactic and stellar models
	LF_fname = args.luminosity_function
	t_fname = args.templates
	
	model_kwargs = {'fh':fh}
	
	if LF_fname != None:
		model_kwargs['LF_fname'] = LF_fname
	
	# Determine number of stars to draw
	redraw = False
	N_stars = None
	
	if args.N == None:
		if args.radius == None:
			print 'Either -N or -rad must be specified'
		
		model = TGalacticModel(**model_kwargs)
		N_stars = model.tot_num_stars(args.gal_lb[0], args.gal_lb[1], args.radius)
		N_stars = np.random.poisson(lam=N_stars)
	else:
		if args.radius != None:
			print 'Cannot specify both -N and -rad'
		
		redraw = True
		N_stars = args.N
	
	EBV_of_mu = None
	if args.clouds != None:
		mu = np.linspace(-5., 35., 1000)
		dmu = mu[1] - mu[0]
		Delta_EBV = 0.01 * dmu * np.ones(mu.size)
		for i in range(len(args.clouds)/2):
			s = 0.05
			m = args.clouds[2*i]
			EBV = args.clouds[2*i+1]
			Delta_EBV += EBV/np.sqrt(2.*np.pi)/s*np.exp(-(mu-m)*(mu-m)/2./s/s)
		EBV = np.cumsum(Delta_EBV) * dmu
		EBV_of_mu = InterpolatedUnivariateSpline(mu, EBV)
		mu = np.linspace(4., 19., 1000) 
		EBV = EBV_of_mu(mu)
		fig = plt.figure()
		ax = fig.add_subplot(1,1,1)
		ax.plot(mu, EBV)
		plt.show()
		#print Ar
	
	params = None
	if args.flat:
		pass
		#params = draw_flat(values.N, EBV_spread=args.mean_EBV,
		#                   r_max=values.max_r, EBV_of_mu=EBV_of_mu)
	else:
		params = draw_from_model(args.gal_lb[0], args.gal_lb[1],
		                         N_stars, EBV_spread=args.mean_EBV,
		                         mag_lim=args.limiting_mag, EBV_of_mu=EBV_of_mu,
		                         EBV_uniform=args.EBV_uniform, redraw=redraw,
		                         n_bands=args.n_bands, scale=args.scale_errors,
		                         model_kwargs=model_kwargs, t_fname=t_fname)
		print '%d stars observed' % (len(params))
	
	#params['mag'][:,4] -= 0.05
	
	# Write Bayestar input file
	if args.output != None:
		mag_lim = np.array(args.limiting_mag)
		mag_lim.shape = (1, 5)
		mag_lim = np.repeat(mag_lim, len(params), axis=0)
		write_infile(args.output, params['mag'], params['err'], mag_lim,
		             l=args.gal_lb[0], b=args.gal_lb[1],
		             access_mode='w')
		
		# Write true parameter values
		write_true_params(args.output, params['DM'], params['EBV'],
		                  params['Mr'], params['FeH'],
		                  l=args.gal_lb[0], b=args.gal_lb[1])
	
	header = '''# Format:
# l  b
# DM  E(B-V)  Mr  FeH
# DM  E(B-V)  Mr  FeH
# DM  E(B-V)  Mr  FeH
# ...'''
	#print header
	#print '%.3f  %.3f' % (args.gal_lb[0], args.gal_lb[1])
	#for p in params:
	#	print '%.3f  %.3f  %.3f  %.3f' % (p['DM'], p['EBV'], p['Mr'], p['FeH']), p['mag'], p['err']
	
	if args.show:
		model = TGalacticModel(**model_kwargs)
		l = np.pi/180. * args.gal_lb[0]
		b = np.pi/180. * args.gal_lb[1]
		cos_l, sin_l = np.cos(l), np.sin(l)
		cos_b, sin_b = np.cos(b), np.sin(b)
		dN_dDM = lambda mu: model.dn_dDM(mu, cos_l, sin_l, cos_b, sin_b)
		
		mplib.rc('text', usetex=True)
		
		fig = plt.figure(figsize=(6,4), dpi=300)
		
		ax = fig.add_subplot(2,2,1)
		ax.hist(params['DM'], bins=100, normed=True, alpha=0.3)
		xlim = ax.get_xlim()
		x = np.linspace(xlim[0], xlim[1], 1000)
		ax.plot(x, dN_dDM(x)/quad(dN_dDM, 1., 25.)[0], 'g-', lw=1.3, alpha=0.5)
		ax.set_xlim(xlim)
		ax.set_xlabel(r'$\mu$', fontsize=14)
		
		ax = fig.add_subplot(2,2,2)
		ax.hist(params['Mr'], bins=100, normed=True, alpha=0.3)
		xlim = ax.get_xlim()
		x = np.linspace(model.Mr_min, model.Mr_max, 1000)
		ax.plot(x, model.LF(x)/quad(model.LF, x[0], x[-1], full_output=1)[0],
		                                               'g-', lw=1.3, alpha=0.5)
		ax.set_xlim(xlim)
		ylim = ax.get_ylim()
		ax.set_ylim(0., ylim[1])
		ax.set_xlabel(r'$M_{r}$', fontsize=14)
		
		ax = fig.add_subplot(2,2,3)
		ax.hist(params['EBV'], bins=100, normed=True, alpha=0.3)
		ax.set_xlabel(r'$\mathrm{E} \! \left( B \! - \! V \right)$', fontsize=14)
		
		ax = fig.add_subplot(2,2,4)
		ax.hist(params['FeH'], bins=100, normed=True, alpha=0.3)
		xlim = ax.get_xlim()
		x = np.linspace(xlim[0], xlim[1], 100)
		y = model.p_FeH_los(x, cos_l, sin_l, cos_b, sin_b)
		ax.plot(x, y, 'g-', lw=1.3, alpha=0.5)
		ax.set_xlabel(r'$\left[ \mathrm{Fe} / \mathrm{H} \right]$', fontsize=14)
		ax.set_xlim(xlim)
		
		fig.subplots_adjust(hspace=0.40, wspace=0.25,
		                    bottom=0.13, top=0.95,
		                    left=0.1, right=0.9)
		
		# CMD of stars
		
		fig = plt.figure(figsize=(6,4), dpi=150)
		ax = fig.add_subplot(1,1,1)
		idx = ((params['err'][:,0] < 1.e9)
		       & (params['err'][:,1] < 1.e9)
		       & (params['err'][:,2] < 1.e9))
		ax.hexbin(params['mag'][idx,0] - params['mag'][idx,2],
		          params['mag'][idx,1],
		          gridsize=100, bins='log')
		ax.set_xlabel(r'$g - i$', fontsize=14)
		ax.set_ylabel(r'$m_{r}$', fontsize=14)
		ylim = ax.get_ylim()
		ax.set_ylim(ylim[1], ylim[0])
		
		plt.show()
	
	return 0
Exemplo n.º 3
0
def main():
	parser = argparse.ArgumentParser(prog='plot_prior.py',
	            description='Plot galstar priors along a line of sight',
	            add_help=True)
	parser.add_argument('RA',
	                    type=str, help='Right Ascension (hh:mm:ss) or degrees')
	parser.add_argument('DEC',
	                    type=str, help='Declination (deg:mm:ss) or degrees')
	parser.add_argument('Radius',
	                    type=str,
	                    nargs='?',
	                    default="'1d'",
	                    help='Radius (hh:mm:ss) or degrees')
	parser.add_argument('--lb',
	                    action='store_true',
	                    help='Interpret RA and DEC as Galactic l and b, respectively')
	parser.add_argument('--correct',
	                    action='store_true',
	                    help='Correct for Malmquist bias.')
	#parser.add_argument('--density', action='store_true', help='Plot stellar number density rather than p(mu)')
	parser.add_argument('--output',
	                    type=str,
	                    default=None,
	                    help='Save output to file (default: open window with output)')
	if sys.argv[0] == 'python':
		offset = 2
	else:
		offset = 1
	args = []
	for arg in sys.argv[offset:]:
		args.append(arg)
		if '--' not in arg: args[-1] = "'%s'" % args[-1]
	values = parser.parse_args(args)
	
	# Get Galactic coordinates
	if values.lb:
		l = float(values.RA[1:-1])
		b = float(values.DEC[1:-1])
	else:
		# Parse RA and DEC
		RA = parse_dhms(values.RA[1:-1])
		if RA == None: RA = parse_RA(values.RA[1:-1])
		DEC = parse_dhms(values.DEC[1:-1])
		if DEC == None: DEC = parse_DEC(values.DEC[1:-1])
		l, b = equatorial2galactic(RA, DEC)
	
	# Parse Radius
	radius = parse_dhms(values.Radius[1:-1])
	if radius == None: radius = float(values.Radius[1:-1])
	radius = deg2rad(radius)
	
	print '# Galactic Coordinates (l, b): %d %d' % (round(l) , round(b))
	
	# Set up figure
	mplib.rc('text',usetex=True)
	mplib.rc('xtick.major', size=6)
	mplib.rc('xtick.minor', size=4)
	mplib.rc('axes', grid=True)
	fig = plt.figure(figsize=(8.5, 11.))
	fig.suptitle(r'$\mathrm{Priors \ for} \ ( \ell = %d^{\circ} , \, b = %d^{\circ} )$' % (round(l) , round(b)), fontsize=24, y=0.97)
	ax = []
	for i in range(2):
		ax.append(fig.add_subplot(2, 1, i+1))
	#fig.subplots_adjust(left=0.12, right=0.90, hspace=0.25)
	fig.subplots_adjust(left=0.14, right=0.90, top=0.88, bottom=0.08, hspace=0.20)
	
	# Intialize Galactic model
	model = TGalacticModel(fh=0.0030)
	model.L_epsilon = 500.
	
	# Precompute trigonometric functions
	l, b  = deg2rad(l), deg2rad(b)
	cos_l, sin_l, cos_b, sin_b = cos(l), sin(l), cos(b), sin(b)
	
	# Determine total number of stars along line of sight
	f_disk = lambda x: model.dn_dDM(x, cos_l, sin_l, cos_b, sin_b,
	                                radius, component='disk',
	                                correct=values.correct)
	N_disk = quad(f_disk, 0.01, 20., epsrel=1.e-5)[0]
	print '# of stars in disk: %d' % int(N_disk)
	
	f_halo = lambda x: model.dn_dDM(x, cos_l, sin_l, cos_b, sin_b,
	                                radius, component='halo',
	                                correct=values.correct)
	N_halo = quad(f_halo, 0.01, 40., epsrel=1.e-5)[0]
	print '# of stars in halo: %d' % int(N_halo)
	
	f_bulge = lambda x: model.dn_dDM(x, cos_l, sin_l, cos_b, sin_b,
	                                 radius, component='bulge',
	                                 correct=values.correct)
	N_bulge = quad(f_bulge, 0.01, 40., epsrel=1.e-5)[0]
	print '# of stars in bulge: %d' % int(N_bulge)
	
	# Calculate dn/dDM and rho
	DM_range = np.linspace(4., 20., 500)
	dn_dDM_disk, dn_dDM_halo, dn_dDM_bulge = [np.empty(500, dtype='f8') for i in xrange(3)]
	rho_disk, rho_halo, rho_bulge = [np.empty(500, dtype='f8') for i in xrange(3)]
	
	for i,DM in enumerate(DM_range):
		rho_halo[i] = model.rho(DM, cos_l, sin_l, cos_b, sin_b, component='halo')
		rho_disk[i] = model.rho(DM, cos_l, sin_l, cos_b, sin_b, component='disk')
		rho_bulge[i] = model.rho(DM, cos_l, sin_l, cos_b, sin_b, component='bulge')
		
		dn_dDM_halo[i] = 1./(N_halo+N_disk+N_bulge) * model.dn_dDM(DM,
		                                          cos_l, sin_l,
		                                          cos_b, sin_b,
		                                          radius, component='halo',
		                                          correct=values.correct)
		dn_dDM_disk[i] = 1./(N_halo+N_disk+N_bulge) * model.dn_dDM(DM,
		                                          cos_l, sin_l,
		                                          cos_b, sin_b,
		                                          radius, component='disk',
		                                          correct=values.correct)
		
		dn_dDM_bulge[i] = 1./(N_halo+N_disk+N_bulge) * model.dn_dDM(DM,
		                                          cos_l, sin_l,
		                                          cos_b, sin_b,
		                                          radius, component='bulge',
		                                          correct=values.correct)
	
	rho_bulge[~np.isfinite(rho_bulge)] = 0.
	dn_dDM_bulge[~np.isfinite(dn_dDM_bulge)] = 0.
	
	print np.sum(dn_dDM_bulge)
	
	rho = rho_disk + rho_halo + rho_bulge
	dn_dDM = dn_dDM_halo + dn_dDM_disk + dn_dDM_bulge
	
	# Plot dn/dDM
	y_min = min(dn_dDM_disk.min(), dn_dDM_halo.min())
	
	ax[0].fill_between(DM_range, y_min, dn_dDM, alpha=0.4, facecolor='k', label='__nolabel__')
	
	ax[0].fill_between(DM_range, y_min, dn_dDM_disk, alpha=0.4, facecolor='g')
	rect = Rectangle((0,0), 0, 0, facecolor='g', label=r'$\mathrm{disk}$', alpha=0.6)
	ax[0].add_patch(rect)
	
	ax[0].fill_between(DM_range, y_min, dn_dDM_halo, alpha=0.4, facecolor='b')
	rect = Rectangle((0,0), 0, 0, facecolor='b', label=r'$\mathrm{halo}$', alpha=0.6)
	ax[0].add_patch(rect)
	
	ax[0].fill_between(DM_range, y_min, dn_dDM_bulge, alpha=0.4, facecolor='r')
	rect = Rectangle((0,0), 0, 0, facecolor='r', label=r'$\mathrm{bulge}$', alpha=0.6)
	ax[0].add_patch(rect)
	
	ax[0].set_title(r'$\mathrm{Probability}$', fontsize=20)
	ax[0].set_xlabel(r'$\mu$', fontsize=20)
	ax[0].set_ylabel(r'$p(\mu)$', fontsize=18)
	ax[0].yaxis.set_major_locator(MaxNLocator(nbins=5))
	ax[0].yaxis.set_minor_locator(MaxNLocator(nbins=20))
	y_max = ax[0].get_ylim()[1]
	ax[0].set_ylim(0., y_max)
	ax[0].legend(loc='upper left')
	ax[0].get_legend().get_frame().set_alpha(0.)
	
	# Plot rho
	y_min = min(dn_dDM_disk.min(), dn_dDM_halo.min())
	ax[1].fill_between(DM_range, y_min, rho, alpha=0.4, facecolor='k')
	ax[1].fill_between(DM_range, y_min, rho_disk, alpha=0.4, facecolor='g')
	ax[1].fill_between(DM_range, y_min, rho_halo, alpha=0.4, facecolor='b')
	ax[1].fill_between(DM_range, y_min, rho_bulge, alpha=0.4, facecolor='r')
	ax[1].set_title(r'$\mathrm{Density}$', fontsize=20)
	ax[1].set_xlabel(r'$\mu$', fontsize=20)
	ax[1].set_ylabel(r'$n (\mu)$', fontsize=18)
	ax[1].set_yscale('log')
	y_min = max(dn_dDM_disk.min(), dn_dDM_halo.min())
	y_max = ax[1].get_ylim()[1]
	ax[1].set_ylim(y_min, y_max)
	
	'''
	# dn/dFeH
	FeH_range = np.linspace(-2.5, 0.5, 100)
	p_FeH_range = np.empty(100, dtype=float)
	for i,FeH in enumerate(FeH_range):
		p_FeH_range[i] = model.p_FeH_los(FeH, cos_l, sin_l, cos_b, sin_b, DM_max=20.)
	func = lambda x: model.p_FeH_los(x, cos_l, sin_l, cos_b, sin_b, DM_max=20.)
	norm = quad(func, -2.5, 0.5, epsrel=1.e-3)[0]
	for i in range(len(FeH_range)): FeH_range[i] /= norm
	ax[1].fill_between(FeH_range, 0, p_FeH_range, alpha=0.8)
	ax[1].set_xlabel(r'$[Fe/H]$', fontsize=17)
	ax[1].set_ylabel(r'$p([Fe/H])$', fontsize=16)
	y_max = ax[1].get_ylim()[1]
	ax[1].set_ylim(0., y_max)
	ax[1].set_xlim(-2.5, 0.5)
	ax[1].xaxis.set_major_locator(MaxNLocator(nbins=4, prune='lower'))
	ax[1].xaxis.set_minor_locator(MaxNLocator(nbins=20))
	'''
	
	for ax_i in ax:
		ax_i.set_xlim(4., 20.)
		ax_i.xaxis.set_major_locator(MultipleLocator(4.))
		ax_i.xaxis.set_minor_locator(MultipleLocator(1.))
		#for tick in ax_i.xaxis.get_major_ticks() + ax_i.xaxis.get_minor_ticks() + ax_i.yaxis.get_major_ticks() + ax_i.yaxis.get_minor_ticks():
		#	tick.tick2On = False
		ax_i.grid(which='minor', alpha=0.3)
	
	if values.output == None:
		plt.show()
	else:
		fn = values.output[1:-1]
		if '.' not in fn: fn += '.png' 
		fig.savefig(fn, dpi=300)
	
	return 0
Exemplo n.º 4
0
def draw_from_model(l, b, N, EBV_spread=0.02,
                    mag_lim=(23., 22., 22., 21., 20.),
                    EBV_of_mu=None, EBV_uniform=False,
                    redraw=True, n_bands=4,
                    scale=1., model_kwargs={}, t_fname=None):
	dtype = [('DM', 'f8'), ('EBV', 'f8'),
	         ('Mr', 'f8'), ('FeH', 'f8'),
	         ('mag', '5f8'), ('err', '5f8')]
	ret = np.empty(N, dtype=dtype)
	
	l = np.pi/180. * l
	b = np.pi/180. * b
	cos_l, sin_l = np.cos(l), np.sin(l)
	cos_b, sin_b = np.cos(b), np.sin(b)
	
	gal_model = TGalacticModel(**model_kwargs)
	
	if t_fname == None:
		t_fname = os.path.expanduser('~/projects/bayestar/data/PScolors.dat')
	
	stellar_model = TStellarModel(t_fname)
	
	R = np.array([3.172, 2.271, 1.682, 1.322, 1.087])
	
	mu_max = mag_lim[1] - gal_model.Mr_min + 3.
	mu_min = min(0., mu_max-25.)
	Mr_max = min(mag_lim[1]+3., gal_model.Mr_max)
	
	dN_dDM = lambda mu: gal_model.dn_dDM(mu, cos_l, sin_l, cos_b, sin_b)
	
	# Set up 1D samplers
	draw_mu = TSample1D(dN_dDM, mu_min, mu_max, 500, 10000)
	draw_Mr = TSample1D(gal_model.LF, gal_model.Mr_min, Mr_max, 10000, 1)
	
	idx = np.arange(N)
	
	keep_sampling = True
	
	while keep_sampling:
		size = idx.size
		print 'Drawing %d...' % size
		
		# Draw DM and Mr
		ret['DM'][idx] = draw_mu(size)
		ret['Mr'][idx] = draw_Mr(size)
		
		# Draw EBV
		ret['EBV'][idx] = 0.
		if EBV_uniform:
			ret['EBV'][idx] += 2. * np.random.random(size=idx.size)
		else:	
			if EBV_of_mu != None:
				ret['EBV'][idx] += EBV_of_mu(ret['DM'][idx]) #+ np.random.normal(scale=EBV_spread, size=size)
			ret['EBV'][idx] += EBV_spread * np.random.chisquare(1., size)
		
		x, y, z = gal_model.Cartesian_coords(ret['DM'][idx], cos_l,
		                                     sin_l, cos_b, sin_b)
		
		# Determine which component stars belong to
		f_halo, f_bulge = gal_model.f_halo_bulge(ret['DM'][idx], cos_l,
		                                         sin_l, cos_b, sin_b)
		tmp_rand = np.random.random(size)
		
		halo = (tmp_rand < f_halo)
		
		bulge = (tmp_rand < f_halo + f_bulge)
		bulge &= ~halo
		
		thin = ~halo & ~bulge & (np.random.random(size) < 0.63)
		thick = ~halo & ~bulge & ~thin
		
		print '  # of bulge stars: %d' % (np.sum(bulge))
		
		# Assign metallicities to halo stars
		while np.any(halo):
			ret['FeH'][idx[halo]] = np.random.normal(-1.46, 0.3, size=np.sum(halo))
			halo &= (ret['FeH'][idx] <= -3.0) | (ret['FeH'][idx] >= 0.5)
		
		# Assign metallicities to thin-disk stars
		while np.any(thin):
			ret['FeH'][idx[thin]] = np.random.normal(gal_model.mu_FeH_D(z[thin])-0.067,
			                                         0.2, size=np.sum(thin))
			thin &= (ret['FeH'][idx] <= -3.0) | (ret['FeH'][idx] >= 0.5)
		
		# Assign metallicities to thick-disk stars
		while np.any(thick):
			ret['FeH'][idx[thick]] = np.random.normal(gal_model.mu_FeH_D(z[thick])-0.067+0.14,
			                                          0.2, size=np.sum(thick))
			thick &= (ret['FeH'][idx] <= -3.0) | (ret['FeH'][idx] >= 0.5)
		
		# Assign metallicities to bulge stars
		while np.any(bulge):
			ret['FeH'][idx[bulge]] = np.random.normal(0., 0.40, size=np.sum(bulge))
			bulge &= (ret['FeH'][idx] <= -3.0) | (ret['FeH'][idx] >= 0.5)
		
		# Calculate absolute stellar magnitudes
		absmags_tmp = stellar_model.absmags(ret['Mr'][idx], ret['FeH'][idx])
		ret['mag'][idx,0] = absmags_tmp['g']
		ret['mag'][idx,1] = absmags_tmp['r']
		ret['mag'][idx,2] = absmags_tmp['i']
		ret['mag'][idx,3] = absmags_tmp['z']
		ret['mag'][idx,4] = absmags_tmp['y']
		
		# Determine errors and apparent magnitudes
		for k in xrange(5):
			ret['mag'][idx,k] += ret['DM'][idx]
			ret['mag'][idx,k] += ret['EBV'][idx] * R[k]
			ret['err'][idx,k] = err_model(ret['mag'][idx][:,k], mag_lim[k], scale=scale)
			#0.02 + 0.3 * np.exp(ret['mag'][idx][:,k] - mag_lim[k])
			#idx_tmp = ret['err'][idx,k] > 1.
			#ret['err'][idx[idx_tmp],k] = 1.
		
		# Calculate observation probability
		p_obs = np.empty((size, 5), dtype='f8')
		
		for k in xrange(5):
			p_obs[:,k] = 1. / (1. + np.exp( (ret['mag'][idx,k] - mag_lim[k] - 0.16) / 0.20 ))
			#p_obs[:,k] = 0.5 - 0.5 * erf((ret['mag'][idx,k] - mag_lim[k] + 0.1) / 0.25)
		
		# Determine which bands stars are observed in
		obs = (p_obs > np.random.random(size=(size, 5)))
		
		# Add in errors to apparent stellar magnitudes
		ret['mag'][idx] += ret['err'][idx] * np.random.normal(size=(size,5))
		
		# Re-estimate errors based on magnitudes
		for k in xrange(5):
			ret['err'][idx,k] = err_model(ret['mag'][idx,k], mag_lim[k], scale=scale)
		
		# Remove observations with errors above 0.2 mags
		obs = obs & (ret['err'][idx] < 0.2)
		
		for k in xrange(5):
			ret['mag'][idx[~obs[:,k]],k] = 0.
			ret['err'][idx[~obs[:,k]],k] = 1.e10
		
		# Require detection in g and at least n_bands-1 other bands
		obs = obs[:,0] & (np.sum(obs, axis=1) >= n_bands)
		#obs = (np.sum(obs, axis=1) >= n_bands)
		
		idx = idx[~obs]
		
		if redraw:
			if idx.size == 0:
				keep_sampling = False
		else:
			keep_sampling = False
			
			ret = ret[obs]
	
	#np.set_printoptions(formatter={'float':lambda x: '%.3f' % x})
	#print ret['err']
	
	#print np.sum(ret['mag'] < 1.)
	
	fig = plt.figure()
	
	for n in xrange(5):
		# Mag histogram
		ax = fig.add_subplot(5,2,1+2*n)
		
		idx = ret['err'][:, n] < 1.e9
		ax.hist(ret['mag'][idx, n], bins=100)
		
		m = np.linspace(10., mag_lim[n] + 0.5, 1000)
		err_curve = err_model(m, mag_lim[n], scale=scale)
		
		ylim = ax.get_ylim()
		err_curve *= 0.9 * ylim[1] / np.max(err_curve)
		
		ax.plot(m, err_curve)
		
		ax.set_xlim(10., 25.)
		
		# Error histogram
		ax = fig.add_subplot(5,2,2+2*n)
		ax.hist(ret['err'][idx, n], bins=100)
		ax.set_xlim(0., 0.2)
		
	
	plt.show()
	
	return ret