def remove_shreds_with_highz(base): """ Remove shereded objects that are within 1.25 R of each high-z (beyond NSA redshift cutoff) object in the base catalog. Set REMOVE to 4. Because this function uses objects with specs, it should be applied to base catalog after the specs are cleaned (i.e., after `clean_sdss_spectra`) `base` is modified in-place. Parameters ---------- base : astropy.table.Table Returns ------- base : astropy.table.Table """ highz_spec_cut = Query('SPEC_Z > 0.05', 'ZQUALITY >= 3', 'PETRORADERR_R > 0', 'PETRORAD_R > 2.0*PETRORADERR_R', 'REMOVE == -1') highz_spec_indices = np.flatnonzero(highz_spec_cut.mask(base)) for idx in highz_spec_indices: if base['REMOVE'][idx] != -1: continue nearby_obj_mask = (base['coord'].separation(base['coord'][idx]).arcsec < 1.25 * base['PETRORAD_R'][idx]) nearby_obj_mask &= (base['REMOVE'] == -1) assert nearby_obj_mask[idx] nearby_obj_mask[idx] = False nearby_obj_count = np.count_nonzero(nearby_obj_mask) if not nearby_obj_count: continue if nearby_obj_count > 25: logging.warning( 'In SAGA.objects.build.remove_shreds_with_highz()\n Too many (> 25) shreds around high-z object {} ({}, {})' .format(base['OBJID'][idx], base['RA'][idx], base['DEC'][idx])) base['REMOVE'][nearby_obj_mask] = 4 return base
def remove_shreds_near_spec_obj(base, nsa=None): has_nsa = Query('OBJ_NSAID > -1') is_high_z = Query('SPEC_Z > 0.05', 'ZQUALITY >= 3', 'is_galaxy', 'radius > abs(radius_err) * 2.0', ~has_nsa) has_nsa_indices = np.flatnonzero(has_nsa.mask(base)) has_nsa_indices = has_nsa_indices[base['r_mag'][has_nsa_indices].argsort()] is_high_z_indices = np.flatnonzero(is_high_z.mask(base)) is_high_z_indices = is_high_z_indices[base['r_mag'] [is_high_z_indices].argsort()] for idx in chain(has_nsa_indices, is_high_z_indices): obj_this = base[idx] if nsa is not None and obj_this['OBJ_NSAID'] > -1: nsa_obj = Query('NSAID == {}'.format( obj_this['OBJ_NSAID'])).filter(nsa)[0] ellipse_calculation = dict() ellipse_calculation['a'] = nsa_obj['PETRO_TH90'] * 2.0 / 3600.0 ellipse_calculation[ 'b'] = ellipse_calculation['a'] * nsa_obj['PETRO_BA90'] ellipse_calculation['t'] = np.deg2rad(nsa_obj['PETRO_PHI90'] + 270.0) ellipse_calculation['s'] = np.sin(ellipse_calculation['t']) ellipse_calculation['c'] = np.cos(ellipse_calculation['t']) ellipse_calculation['x'] = base['RA'] - nsa_obj['RA'] ellipse_calculation['y'] = base['DEC'] - nsa_obj['DEC'] nearby_obj_mask = ne.evaluate( '((x*c - y*s)/a)**2.0 + ((x*s + y*c)/b)**2.0 < 1.0', local_dict=ellipse_calculation, global_dict={}) no_spec_z_or_close = Query('ZQUALITY < 3') no_spec_z_or_close |= Query( (lambda z: np.fabs(z - nsa_obj['Z']) < 200.0 / _SPEED_OF_LIGHT, 'SPEC_Z')) no_spec_z_or_close |= Query((lambda z: np.fabs(z - obj_this[ 'SPEC_Z']) < 200.0 / _SPEED_OF_LIGHT, 'SPEC_Z')) nearby_obj_mask &= no_spec_z_or_close.mask(base) remove_flag = 21 values_to_rewrite = { 'OBJID': nsa_obj['NSAID'], 'REMOVE': 0, 'is_galaxy': (nsa_obj['PETRO_TH50'] > 1), 'RA': nsa_obj['RA'], 'DEC': nsa_obj['DEC'], 'radius': nsa_obj['PETRO_TH50'], 'radius_err': 0, 'survey': 'NSA', } invalid_mag = (nsa_obj['SERSIC_FLUX'] <= 0) nsa_sersic_flux = np.array(nsa_obj['SERSIC_FLUX']) nsa_sersic_flux[invalid_mag] = 1.0 mag = 22.5 - 2.5 * np.log10(nsa_sersic_flux) mag_err = np.fabs((2.5 / np.log(10.0)) / nsa_sersic_flux / np.sqrt(nsa_obj['SERSIC_FLUX_IVAR'])) mag[invalid_mag] = 99.0 mag_err[invalid_mag] = 99.0 for i, b in enumerate(get_sdss_bands()): values_to_rewrite['{}_mag'.format( b)] = mag[i + 2] - nsa_obj['EXTINCTION'][i + 2] values_to_rewrite['{}_err'.format(b)] = mag_err[i + 2] for k, v in values_to_rewrite.items(): base[k][idx] = v elif obj_this['REMOVE'] > 0: continue else: remove_radius = 2.0 * obj_this['radius'] nearby_obj_mask = (base['coord'].separation( obj_this['coord']).arcsec < remove_radius) remove_flag = 22 nearby_obj_mask[idx] = False nearby_obj_count = np.count_nonzero(nearby_obj_mask) if not nearby_obj_count: continue if nearby_obj_count > 25 and remove_flag == 22: logging.warning( 'More than 25 photo obj within ~ {:.3f}" of {} spec obj {} ({}, {})' .format(remove_radius, obj_this['TELNAME'], obj_this['OBJID'], obj_this['RA'], obj_this['DEC'])) base['REMOVE'][nearby_obj_mask] += (1 << remove_flag) return base
def prepare_aat_catalog( target_catalog, write_to=None, verbose=True, flux_star_removal_threshold=20.0 * u.arcsec, flux_star_r_range=(17, 17.7), flux_star_gr_range=(0.1, 0.4), sky_fiber_void_radius=10.0 * u.arcsec, sky_fiber_needed=100, sky_fiber_max=1.1 * u.deg, sky_fiber_host_rvir_threshold=0.7 * u.deg, sky_fiber_radial_adjustment=2.0, targeting_score_threshold=900, seed=123, ): """ Prepare AAT target catalog. If the host's radius is less than `sky_fiber_host_rvir_threshold`, all sky fiber will be distributed between `sky_fiber_max` and host's radius. Otherwise, first fill the annulus between `sky_fiber_max` and host's radius, then distribute the rest within the host (but prefer outer region, as controlled by `sky_fiber_radial_adjustment`) Format needed: # TargetName(unique for header) RA(h m s) Dec(d m s) TargetType(Program,Fiducial,Sky) Priority(9 is highest) Magnitude 0 Notes 1237648721248518305 14 42 17.79 -0 12 05.95 P 2 22.03 0 magcol=fiber2mag_r, model_r=20.69 1237648721786045341 14 48 37.16 +0 21 33.81 P 1 21.56 0 magcol=fiber2mag_r, model_r=20.55 """ # pylint: disable=no-member if 'TARGETING_SCORE' not in target_catalog.colnames: return KeyError( '`target_catalog` does not have column "TARGETING_SCORE".' 'Have you run `compile_target_list` or `assign_targeting_score`?') if not isinstance(flux_star_removal_threshold, u.Quantity): flux_star_removal_threshold = flux_star_removal_threshold * u.arcsec if not isinstance(sky_fiber_void_radius, u.Quantity): sky_fiber_void_radius = sky_fiber_void_radius * u.arcsec if not isinstance(sky_fiber_max, u.Quantity): sky_fiber_max = sky_fiber_max * u.deg if not isinstance(sky_fiber_host_rvir_threshold, u.Quantity): sky_fiber_host_rvir_threshold = sky_fiber_host_rvir_threshold * u.deg host_ra = target_catalog['HOST_RA'][0] * u.deg host_dec = target_catalog['HOST_DEC'][0] * u.deg host_dist = target_catalog['HOST_DIST'][0] host_rvir = np.arcsin(0.3 / host_dist) * u.rad annulus_actual = (sky_fiber_max**2.0 - host_rvir**2.0) annulus_wanted = (sky_fiber_max**2.0 - sky_fiber_host_rvir_threshold**2.0) if annulus_actual < 0: raise ValueError( '`sky_fiber_max` too small, this host is larger than that!') if annulus_wanted < 0: raise ValueError( '`sky_fiber_max` must be larger than `sky_fiber_host_rvir_threshold`!' ) def _gen_dist_rand(seed_this, size): U = np.random.RandomState(seed_this).rand(size) return np.sqrt(U * annulus_actual + host_rvir**2.0) if annulus_actual < annulus_wanted: def gen_dist_rand(seed_this, size): size_out = int(np.around(size * annulus_actual / annulus_wanted)) size_in = size - size_out dist_rand_out = _gen_dist_rand(seed_this, size_out) index = (1.0 / (sky_fiber_radial_adjustment + 2.0)) dist_rand_in = (np.random.RandomState(seed_this + 1).rand(size_in) **index) * host_rvir return np.concatenate( [dist_rand_out.to_value("deg"), dist_rand_in.to_value("deg")]) * u.deg else: gen_dist_rand = _gen_dist_rand n_needed = sky_fiber_needed ra_sky = [] dec_sky = [] base_sc = SkyCoord(target_catalog['RA'], target_catalog['DEC'], unit='deg') while n_needed > 0: n_rand = int(np.ceil(n_needed * 1.1)) dist_rand = gen_dist_rand(seed, n_rand) theta_rand = np.random.RandomState(seed + 1).rand(n_rand) * (2.0 * np.pi) ra_rand = np.remainder(host_ra + dist_rand * np.cos(theta_rand), 360.0 * u.deg) dec_rand = host_dec + dist_rand * np.sin(theta_rand) ok_mask = (dec_rand >= -90.0 * u.deg) & (dec_rand <= 90.0 * u.deg) ra_rand = ra_rand[ok_mask] dec_rand = dec_rand[ok_mask] sky_sc = SkyCoord(ra_rand, dec_rand) sep = sky_sc.match_to_catalog_sky(base_sc)[1] ok_mask = (sep > sky_fiber_void_radius) n_needed -= np.count_nonzero(ok_mask) ra_sky.append(ra_rand[ok_mask].to_value("deg")) dec_sky.append(dec_rand[ok_mask].to_value("deg")) seed += np.random.RandomState(seed + 2).randint(100, 200) del ra_rand, dec_rand, sky_sc, sep, ok_mask del base_sc ra_sky = np.concatenate(ra_sky)[:sky_fiber_needed] dec_sky = np.concatenate(dec_sky)[:sky_fiber_needed] is_target = Query('TARGETING_SCORE >= 0', 'TARGETING_SCORE < {}'.format(targeting_score_threshold)) is_des = Query((lambda s: s == 'des', 'survey')) is_star = Query('morphology_info == 0', is_des) | Query( ~is_des, ~Query('is_galaxy')) is_flux_star = Query(is_star, 'r_mag >= {}'.format(flux_star_r_range[0]), 'r_mag < {}'.format(flux_star_r_range[1])) is_flux_star &= Query('gr >= {}'.format(flux_star_gr_range[0]), 'gr < {}'.format(flux_star_gr_range[1])) target_catalog = (is_target | is_flux_star).filter(target_catalog) target_catalog['Priority'] = target_catalog['TARGETING_SCORE'] // 100 target_catalog['Priority'][Query('Priority < 1').mask(target_catalog)] = 1 target_catalog['Priority'][Query('Priority > 8').mask(target_catalog)] = 8 target_catalog['Priority'] = 9 - target_catalog['Priority'] target_catalog['Priority'][is_flux_star.mask(target_catalog)] = 9 flux_star_indices = np.flatnonzero(is_flux_star.mask(target_catalog)) flux_star_sc = SkyCoord(*target_catalog[['RA', 'DEC' ]][flux_star_indices].itercols(), unit='deg') target_sc = SkyCoord(*is_target.filter(target_catalog)[['RA', 'DEC']].itercols(), unit='deg') sep = flux_star_sc.match_to_catalog_sky(target_sc)[1] target_catalog['Priority'][flux_star_indices[ sep < flux_star_removal_threshold]] = 0 target_catalog = Query('Priority > 0').filter(target_catalog) n_flux_star = Query('Priority == 9').count(target_catalog) del flux_star_indices, flux_star_sc, target_sc, sep target_catalog['TargetType'] = 'P' target_catalog['0'] = 0 target_catalog['Notes'] = 'targets' target_catalog['Notes'][is_flux_star.mask(target_catalog)] = 'flux' target_catalog.rename_column('DEC', 'Dec') target_catalog.rename_column('OBJID', 'TargetName') target_catalog.rename_column('r_mag', 'Magnitude') target_catalog.sort(['TARGETING_SCORE', 'Magnitude']) target_catalog = target_catalog[[ 'TargetName', 'RA', 'Dec', 'TargetType', 'Priority', 'Magnitude', '0', 'Notes' ]] sky_catalog = Table({ 'TargetName': np.arange(len(ra_sky)), 'RA': ra_sky, 'Dec': dec_sky, 'TargetType': np.repeat('S', len(ra_sky)), 'Priority': np.repeat(9, len(ra_sky)), 'Magnitude': np.repeat(99.0, len(ra_sky)), '0': np.repeat(0, len(ra_sky)), 'Notes': np.repeat('sky', len(ra_sky)), }) target_catalog = vstack([target_catalog, sky_catalog]) if verbose: print('# of flux stars =', n_flux_star) print('# of sky fibers =', len(sky_catalog)) for rank in range(1, 10): print('# of Priority={} targets ='.format(rank), Query('Priority == {}'.format(rank)).count(target_catalog)) if write_to: if verbose: print('Writing to {}'.format(write_to)) target_catalog.write( write_to, delimiter=' ', quotechar='"', format='ascii.fast_commented_header', overwrite=True, formats={ 'RA': lambda x: Angle(x, 'deg').wrap_at(360 * u.deg).to_string( 'hr', sep=' ', precision=2), # pylint: disable=E1101 'Dec': lambda x: Angle(x, 'deg').to_string( 'deg', sep=' ', precision=2), 'Magnitude': '%.2f', }) with open(write_to) as fh: content = fh.read() with open(write_to, 'w') as fh: fh.write(content.replace('"', '')) return target_catalog
def add_stellar_mass(base, cosmology=WMAP9): """ Calculate stellar mass based only on gi colors and redshift and add to base catalog. Based on GAMA data using Taylor et al (2011). `base` is modified in-place. Parameters ---------- base : astropy.table.Table Returns ------- base : astropy.table.Table """ base['log_sm'] = np.nan global _HAS_KCORRECT if not _HAS_KCORRECT: logging.warn('No kcorrect module. Stellar mass not calculated!') return base if 'OBJID_sdss' in base.colnames: # version 2 base catalog with SDSS postfix = '_sdss' to_calc_query = Query(C.has_spec, 'OBJID_sdss != -1') elif 'EXTINCTION_U' in base.colnames: # version 1 base catalog postfix = '' to_calc_query = C.has_spec for b in get_sdss_bands(): base['{}_mag'.format(b)] = base[b] - base['EXTINCTION_{}'.format( b.upper())] else: # version 2 base catalog without SDSS logging.warn('No SDSS bands! Stellar mass not calculated!') return base to_calc_mask = to_calc_query.mask(base) if not to_calc_mask.any(): logging.warn('Stellar mass not calculated because no valid entry!') return base if _HAS_KCORRECT != 'LOADED': kcorrect.load_templates() kcorrect.load_filters() _HAS_KCORRECT = 'LOADED' mag = view_table_as_2d_array(base, ('{}_mag{}'.format(b, postfix) for b in get_sdss_bands()), to_calc_mask, np.float32) mag_err = view_table_as_2d_array(base, ('{}_err{}'.format(b, postfix) for b in get_sdss_bands()), to_calc_mask, np.float32) redshift = view_table_as_2d_array(base, ['SPEC_Z'], to_calc_mask, np.float32) # CONVERT SDSS MAGNITUDES INTO MAGGIES mgy = 10.0**(-0.4 * mag) mgy_ivar = (0.4 * np.log(10.0) * mgy * mag_err)**-2.0 kcorrect_input = np.hstack((redshift, mgy, mgy_ivar)) # USE ASTROPY TO CALCULATE LUMINOSITY DISTANCE lf_distmod = cosmology.distmod(redshift.ravel()).value # RUN KCORRECT FIT, AND CALCULATE STELLAR MASS tmremain = np.array([0.601525, 0.941511, 0.607033, 0.523732, 0.763937]) sm_not_normed = np.fromiter((np.dot(kcorrect.fit_coeffs(x)[1:], tmremain) for x in kcorrect_input), np.float64, len(kcorrect_input)) base['log_sm'][to_calc_mask] = np.log10(sm_not_normed) + (0.4 * lf_distmod) return base