def work(self, snum): # # Random seed at each iteration # np.random.seed() # # Resample parallax and R0 # if self.resample: plx_sample = np.random.normal(loc=self.plx, scale=self.plx_err) R0_sample = np.random.normal(loc=self.R0, scale=self.R0_err) else: plx_sample = self.plx R0_sample = self.R0 # # Compute distances from parallax, catch large distances # distance = 1.0 / plx_sample # kpc distance[distance > self.dist_max] = np.nan distance[distance < 0.0] = np.nan # # Compute Galactocentric radius # Rgal = kd_utils.calc_Rgal(self.glong, distance, R0=R0_sample) return (distance, Rgal)
def parallax(glong, plx, dist_max=30., R0=8.34): """ Compute parallax distance and Galactocentric radius for a given Galactic longitude and parallax. Parameters: glong : scalar or 1-D array Galactic longitude (deg). If it is an array, it must have the same size as plx. plx : scalar or 1-D array Parallax (milli-arcseconds). If it is an array, it must have the same size as glong. dist_max : scalar (optional) The maximum parallax distance to compute (kpc) R0 : scalar (optional) Solar Galactocentric radius (kpc) Returns: output output["Rgal"] : scalar or 1-D array Galactocentric radius (kpc). output["distance"] : scalar or 1-D array Parallax distance (kpc). If glong and plx are scalars, each of these is a scalar. Otherwise they have shape (glong.size). Raises: ValueError : if glong and plx are not 1-D; or if glong and plx are arrays and not the same size """ # # check inputs # # convert scalar to array if necessary glong_inp, plx_inp = np.atleast_1d(glong, plx) # check shape of inputs if glong_inp.ndim != 1 or plx_inp.ndim != 1: raise ValueError("glong and plx must be 1-D") if glong_inp.size != plx_inp.size: raise ValueError("glong and plx must have same size") # ensure range [0,360) degrees fix_glong = glong_inp % 360. # # Compute distances # distance = np.array(1./plx_inp) # kpc # remove large distances distance[distance > dist_max] = np.nan # # Compute Galactocentric radius # Rgal = np.array([kd_utils.calc_Rgal(l,d,R0=R0) for l,d in zip(fix_glong, distance)]) # # Convert back to scalars if necessary # if len(fix_glong) == 1: return {"Rgal":Rgal[0], "distance":distance[0]} else: return {"Rgal":Rgal, "distance":distance}
def calc_gcen_coords(glong, glat, dist, R0=__R0, Zsun=__Zsun, roll=__roll, use_Zsunroll=True): """ Calculate galactocentric Cartesian coordinates from galactic longitudes, latitudes, and distances from the Sun. Parameters: glong, glat :: scalar or array of scalars Galactic longitude and latitude (deg) dist :: scalar or array of scalars line-of-sight distance (kpc) R0 :: scalar or array of scalars (optional) Galactocentric radius of the Sun Zsun :: scalar (optional) Height of Sun above galactic midplane (pc) roll :: scalar (optional) Roll angle relative to b=0 (deg) use_Zsunroll :: boolean (optional) If True, include Zsun and roll into calculation Returns: x, y, Rgal, cos_az, sin_az x, y :: scalars or arrays of scalars Galactocentric Cartesian positions (kpc). Sun is on -x-axis. Rgal :: scalar or array of scalars Galactocentric cylindrical radius (kpc) cos_az, sin_az :: scalar or array of scalars Cosine and sine of Galactocentric azimuth (rad) """ glong, glat, dist = np.atleast_1d(glong, glat, dist) if np.shape(glong) != np.shape(dist): glong = np.array([glong, ] * len(dist)) Rgal = kd_utils.calc_Rgal( glong, glat, dist, R0=R0, Zsun=Zsun, roll=roll, use_Zsunroll=use_Zsunroll) Rgal[Rgal < 1.0e-6] = 1.0e-6 # Catch small Rgal az = kd_utils.calc_az( glong, glat, dist, R0=R0, Zsun=Zsun, roll=roll, use_Zsunroll=use_Zsunroll) cos_az = np.cos(np.deg2rad(az)) sin_az = np.sin(np.deg2rad(az)) x = Rgal * -cos_az y = Rgal * sin_az return x, y, Rgal, cos_az, sin_az
def calc_vlsr(glong, glat, dist, a1=__a1, a2=__a2, a3=__a3, R0=__R0, theta0=__theta0): """ Return the LSR velocity at a given Galactic longitude and line-of-sight distance. Parameters: glong, glat :: scalar or array of scalars Galactic longitude and latitude (deg). dist :: scalar or array of scalars line-of-sight distance (kpc). a1, a2, a3 :: scalars (optional) Brand rotation curve parameters R0, theta0 :: scalars (optional) Solar Galactocentric radius (kpc) and circular orbit speed at R0 (km/s) Returns: vlsr vlsr :: scalar or array of scalars LSR velocity (km/s). """ input_scalar = np.isscalar(glong) and np.isscalar(glat) and np.isscalar( dist) glong, glat, dist = np.atleast_1d(glong, glat, dist) # # Convert distance to Galactocentric radius, catch small Rgal # Rgal = kd_utils.calc_Rgal(glong, glat, dist, R0=R0) Rgal[Rgal < 1.0e-6] = 1.0e-6 # # Rotation curve circular velocity # theta = calc_theta(Rgal, a1=a1, a2=a2, a3=a3, R0=R0, theta0=theta0) # # Now take circular velocity and convert to LSR velocity # vlsr = R0 * np.sin(np.deg2rad(glong)) vlsr = vlsr * ((theta / Rgal) - theta0 / R0) if input_scalar: return vlsr[0] return vlsr
def work(self, snum): # # Random seed at each iteration # np.random.seed() # # Resample parallax, R0, Zsun, and roll # if self.resample: plx_sample = np.random.normal(loc=self.plx, scale=self.plx_err) R0_sample = np.random.normal(loc=self.R0, scale=self.R0_err) Zsun_sample = np.random.normal(loc=self.Zsun, scale=self.Zsun_err) roll_sample = np.random.normal(loc=self.roll, scale=self.roll_err) else: plx_sample = self.plx R0_sample = self.R0 Zsun_sample = self.Zsun roll_sample = self.roll # # Compute distances from parallax, catch large distances # distance = 1. / plx_sample # kpc distance[distance > self.dist_max] = np.nan distance[distance < 0.] = np.nan # # Compute Galactocentric radius # ? Are we assuming latitude of zero? # Rgal = kd_utils.calc_Rgal(self.glong, 0., distance, R0=R0_sample, Zsun=Zsun_sample, roll=roll_sample, use_Zsunroll=True) return (distance, Rgal)
def work(self, snum): # # Random seed at each iteration # np.random.seed() # # Resample velocity and rotation curve parameters # if self.resample: params = self.rotcurve_module.resample_params(size=len(self.glong)) velo_sample = np.random.normal(loc=self.velo, scale=self.velo_err) else: params = self.nominal_params velo_sample = self.velo # # Calculate LSR velocity at each (glong, distance) point # grid_vlsrs = self.rotcurve_module.calc_vlsr(self.glong_grid, self.glat, self.dist_grid, **params) # # Get index of tangent point along each direction # tan_idxs = np.array([ np.argmax(vlsr) if gl < 90.0 else np.argmin(vlsr) if gl > 270.0 else -1 for gl, vlsr in zip(self.glong, grid_vlsrs.T) ]) # # Get index of near and far distances along each direction # near_idxs = np.array([ np.argmin(np.abs(vlsr[:tan_idx] - v)) if (tan_idx != -1 and np.min(np.abs(vlsr[:tan_idx] - v)) < self.velo_tol) else -1 for v, vlsr, tan_idx in zip(velo_sample, grid_vlsrs.T, tan_idxs) ]) far_idxs = np.array([ tan_idx + np.argmin(np.abs(vlsr[tan_idx:] - v)) if (tan_idx != -1 and np.min(np.abs(vlsr[tan_idx:] - v)) < self.velo_tol) else np.argmin(np.abs(vlsr - v)) if (tan_idx == -1 and np.min(np.abs(vlsr - v)) < self.velo_tol) else -1 for v, vlsr, tan_idx in zip(velo_sample, grid_vlsrs.T, tan_idxs) ]) # # Get VLSR of tangent point # vlsr_tan = np.array([ vlsr[tan_idx] if tan_idx != -1 else np.nan for vlsr, tan_idx in zip(grid_vlsrs.T, tan_idxs) ]) # # Get distances # tan_dists = self.dists[tan_idxs] tan_dists[tan_idxs == -1] = np.nan near_dists = self.dists[near_idxs] near_dists[near_idxs == -1] = np.nan far_dists = self.dists[far_idxs] far_dists[far_idxs == -1] = np.nan Rgal = kd_utils.calc_Rgal(self.glong, self.glat, far_dists.T, R0=params["R0"]).T Rtan = kd_utils.calc_Rgal(self.glong, self.glat, tan_dists.T, R0=params["R0"]).T return (tan_dists, near_dists, far_dists, vlsr_tan, Rgal, Rtan)
def calc_vlsr(glong, glat, dist, R0=__R0, Usun=__Usun, Vsun=__Vsun, Wsun=__Wsun, Upec=__Upec, Vpec=__Vpec, a2=__a2, a3=__a3, Zsun=__Zsun, roll=__roll, peculiar=False): """ Return the IAU-LSR velocity at a given Galactic longitude and line-of-sight distance. Parameters: glong, glat :: scalars or arrays of scalars Galactic longitude and latitude (deg). dist :: scalar or array of scalars line-of-sight distance (kpc). R0 :: scalar (optional) Solar Galactocentric radius (kpc) Usun, Vsun, Wsun, Upec, Vpec, a2, a3 :: scalars (optional) Reid+2019 rotation curve parameters Zsun :: scalar (optional) Height of sun above Galactic midplane (pc) roll :: scalar (optional) Roll of Galactic midplane relative to b=0 (deg) peculiar :: boolean (optional) If True, include HMSFR peculiar motion component Returns: vlsr vlsr :: scalar or array of scalars LSR velocity (km/s). """ input_scalar = np.isscalar(glong) and np.isscalar(glat) and np.isscalar( dist) glong, glat, dist = np.atleast_1d(glong, glat, dist) cos_glong = np.cos(np.deg2rad(glong)) sin_glong = np.sin(np.deg2rad(glong)) cos_glat = np.cos(np.deg2rad(glat)) sin_glat = np.sin(np.deg2rad(glat)) # # Convert distance to Galactocentric, catch small Rgal # Rgal = kd_utils.calc_Rgal(glong, glat, dist, R0=R0) Rgal[Rgal < 1.e-6] = 1.e-6 az = kd_utils.calc_az(glong, glat, dist, R0=R0) cos_az = np.cos(np.deg2rad(az)) sin_az = np.sin(np.deg2rad(az)) # # Rotation curve circular velocity # theta = calc_theta(Rgal, a2=a2, a3=a3, R0=R0) theta0 = calc_theta(R0, a2=a2, a3=a3, R0=R0) # # Add HMSFR peculiar motion # if peculiar: vR = -Upec vAz = theta + Vpec vZ = 0.0 else: vR = 0.0 vAz = theta vZ = 0.0 vXg = -vR * cos_az + vAz * sin_az vYg = vR * sin_az + vAz * cos_az vZg = vZ # # Convert to barycentric # X = dist * cos_glat * cos_glong Y = dist * cos_glat * sin_glong Z = dist * sin_glat # useful constants sin_tilt = Zsun / 1000. / R0 cos_tilt = np.cos(np.arcsin(sin_tilt)) sin_roll = np.sin(np.deg2rad(roll)) cos_roll = np.cos(np.deg2rad(roll)) # solar peculiar motion vXg = vXg - Usun vYg = vYg - theta0 - Vsun vZg = vZg - Wsun # correct tilt and roll of Galactic midplane vXg1 = vXg * cos_tilt - vZg * sin_tilt vYg1 = vYg vZg1 = vXg * sin_tilt + vZg * cos_tilt vXh = vXg1 vYh = vYg1 * cos_roll + vZg1 * sin_roll vZh = -vYg1 * sin_roll + vZg1 * cos_roll vbary = (X * vXh + Y * vYh + Z * vZh) / dist # # Convert to IAU-LSR # vlsr = vbary + (__Ustd * cos_glong + __Vstd * sin_glong) * cos_glat + __Wstd * sin_glat if input_scalar: return vlsr[0] return vlsr
def calc_vlsr(glong, dist, resample=False): """ Return the LSR velocity at a given Galactic longitude and line-of-sight distance. If requested, resample rotation curve parameters and R0 within uncertainties assuming Gaussian errors. Parameters: glong : scalar or 1-D array Galactic longitude (deg). If it is an array, it must have the same size as dist. dist : scalar or 1-D array line-of-sight distance (kpc). If it is an array, it must have the same size as glong or glong must be a scalar. resample : bool (optional) if True, resample rotation curve parameters within uncertainties Returns: vlsr, params vlsr : scalar or 1-D array LSR velocity (km/s). If dist is a scalar, it is a scalar. Otherwise it has shape (dist.size). params : dict of scalars parameters used to calculate vlsr (useful if resample is True) params["R0"] : R0 used in calculation params["a1"] : a1 used in calculation params["a2"] : a2 used in calculation params["a3"] : a3 used in calculation Raises: ValueError : if glong or dist are not 1-D; or if glong and dist are arrays and not the same size """ # # check inputs # # convert scalar to array if necessary glong_inp, dist_inp = np.atleast_1d(glong, dist) # check shape of inputs if glong_inp.ndim != 1 or dist_inp.ndim != 1: raise ValueError("glong and dist must be 1-D") if glong_inp.size != 1 and glong_inp.size != dist_inp.size: raise ValueError("glong and dist must have same size, " "or glong must be a scalar") # # Resample rotation curve parameters if necessary # if resample: # resample fit parameters within uncertainty a1 = np.random.normal(loc=__a1,scale=__a1_err) a2 = np.random.normal(loc=__a2,scale=__a2_err) a3 = np.random.normal(loc=__a3,scale=__a3_err) R0 = np.random.normal(loc=__R0,scale=__R0_err) else: a1 = __a1 a2 = __a2 a3 = __a3 R0 = __R0 params = {"R0":R0,"a1":a1,"a2":a2,"a3":a3} # # Convert distance to Galactocentric radius, catch places where # R = 0. # Rgal = kd_utils.calc_Rgal(glong_inp,dist_inp,R0=R0) Rgal = np.atleast_1d(Rgal) Rgal[Rgal < 1.e-6] = 1.e-6 # # Reid rotation curve circular velocity # theta = calc_theta(Rgal,a1=a1,a2=a2,a3=a3,R0=R0) # # Now take circular velocity and convert to LSR velocity # vlsr = R0 * np.sin(np.deg2rad(glong_inp)) vlsr = vlsr * ((theta/Rgal) - a1/R0) # # Convert back to scalar if necessary # if dist_inp.size == 1: return vlsr[0],params else: return vlsr,params
def rotcurve_kd(glong, velo, velo_tol=1.e-1, rotcurve='reid14_rotcurve', dist_res=1.e-2, dist_min=0.01, dist_max=30., resample=False): """ Return the kinematic near, far, and tanget distance for a given Galactic longitude and LSR velocity assuming a given rotation curve. Parameters: glong : scalar or 1-D array Galactic longitude (deg). If it is an array, it must have the same size as velo. velo : scalar or 1-D array LSR velocity (km/s). If it is an array, it must have the same size as glong. velo_tol : scalar (optional) LSR velocity tolerance to consider a match between velo and rotation curve velocity rotcurve : string (optional) rotation curve model dist_res : scalar (optional) line-of-sight distance resolution when calculating kinematic distance (kpc) dist_min : scalar (optional) minimum line-of-sight distance when calculating kinematic distance (kpc) dist_max : scalar (optional) maximum line-of-sight distance when calculating kinematic distance (kpc) resample : bool (optional) if True, resample rotation curve parameters within uncertainties Returns: output output["Rgal"] : scalar or 1-D array Galactocentric radius (kpc). output["Rtan"] : scalar or 1-D array Galactocentric radius of tangent point (kpc). output["near"] : scalar or 1-D array kinematic near distance (kpc) output["far"] : scalar or 1-D array kinematic far distance (kpc) output["tangent"] : scalar or 1-D array kinematic tangent distance (kpc) output["vlsr_tangent"] : scalar or 1-D array LSR velocity of tangent point (km/s) If glong and velo are scalars, each of these is a scalar. Otherwise they have shape (velo.size). Raises: ValueError : if glong and velo are not 1-D; or if glong and velo are arrays and not the same size """ # # check inputs # # convert scalar to array if necessary glong_inp, velo_inp = np.atleast_1d(glong, velo) # check shape of inputs if glong_inp.ndim != 1 or velo_inp.ndim != 1: raise ValueError("glong and velo must be 1-D") if glong_inp.size != velo_inp.size: raise ValueError("glong and velo must be same size") # ensure range [0,360) degrees fix_glong = glong_inp % 360. # # Create array of distances # dists = np.arange(dist_min, dist_max, dist_res) # # Calculate LSR velocity at each (glong,distance) point using # rotation curve # rotcurve_module = importlib.import_module('kd.' + rotcurve) vlsrs = np.zeros((fix_glong.size, dists.size)) params = [None] * fix_glong.size for ind, l in enumerate(fix_glong): vlsr,param = \ rotcurve_module.calc_vlsr(l,dists,resample=resample) vlsrs[ind] = vlsr params[ind] = param # # Storage for kinematic distance indicies # near_ind = np.ma.masked_all(velo_inp.size, dtype=np.int) far_ind = np.ma.masked_all(velo_inp.size, dtype=np.int) tan_ind = np.ma.masked_all(fix_glong.size, dtype=np.int) # # Find kinematic distance indicies # for i, (l, v) in enumerate(zip(fix_glong, velo_inp)): # # 2nd or 3rd quadrants # if (90. <= l <= 270.): # # far distance indicies # velo_diff = np.min(np.abs(vlsrs[i] - v)) best_ind = np.argmin(np.abs(vlsrs[i] - v)) if velo_diff < velo_tol: far_ind[i] = best_ind # # 1st or 4th quadrants # else: # # tangent distance indicies # if l <= 90.: tan_ind[i] = np.argmax(vlsrs[i]) if l >= 270.: tan_ind[i] = np.argmin(vlsrs[i]) # mask if tangent distance is zero if tan_ind[i] == 0: tan_ind.mask[i] = True continue # # near distance indicies # velo_diff = np.min(np.abs(vlsrs[i, 0:tan_ind[i]] - v)) best_ind = np.argmin(np.abs(vlsrs[i, 0:tan_ind[i]] - v)) if velo_diff < velo_tol: near_ind[i] = best_ind # # far distance indicies # velo_diff = np.min(np.abs(vlsrs[i, tan_ind[i]:] - v)) best_ind = np.argmin(np.abs(vlsrs[i, tan_ind[i]:] - v)) best_ind += tan_ind[i] if velo_diff < velo_tol: far_ind[i] = best_ind # # Assign distances from indicies, mask where appropriate # near_dist = np.array([ dists[ind] if ind is not np.ma.masked else np.nan for ind in near_ind ]) far_dist = np.array( [dists[ind] if ind is not np.ma.masked else np.nan for ind in far_ind]) Rgal = np.array([ kd_utils.calc_Rgal(l, d, R0=params[ind]["R0"]) for ind, (l, d) in enumerate(zip(fix_glong, far_dist)) ]) tan_dist = np.array( [dists[ind] if ind is not np.ma.masked else np.nan for ind in tan_ind]) Rtan = np.array([ kd_utils.calc_Rgal(l, d, R0=params[ind]["R0"]) for ind, (l, d) in enumerate(zip(fix_glong, tan_dist)) ]) # # Assign tangent point velocities # vlsr_tan = np.array([ vlsrs[i][t] if t is not np.ma.masked else np.nan for i, t in enumerate(tan_ind) ]) # # Convert back to scalars if necessary # if len(fix_glong) == 1: return { "Rgal": Rgal[0], "Rtan": Rtan[0], "near": near_dist[0], "far": far_dist[0], "tangent": tan_dist[0], "vlsr_tangent": vlsr_tan[0] } else: return { "Rgal": Rgal, "Rtan": Rtan, "near": near_dist, "far": far_dist, "tangent": tan_dist, "vlsr_tangent": vlsr_tan }
def work(self, snum): # # Random seed at each iteration # np.random.seed() # # Resample velocity and rotation curve parameters # if self.resample: if self.rotcurve == "wc21_rotcurve": params = self.rotcurve_module.resample_params( self.kde, size=len(self.glong), nom_params=self.nominal_params, use_kriging=self.use_kriging) else: params = self.rotcurve_module.resample_params( size=len(self.glong)) velo_sample = np.random.normal(loc=self.velo, scale=self.velo_err) else: params = self.nominal_params velo_sample = self.velo # # Calculate LSR velocity at each (glong, distance) point # if self.rotcurve == "wc21_rotcurve" or self.rotcurve == "reid19_rotcurve": grid_vlsrs = self.rotcurve_module.calc_vlsr(self.glong_grid, self.glat, self.dist_grid, peculiar=self.peculiar, **params) else: grid_vlsrs = self.rotcurve_module.calc_vlsr( self.glong_grid, self.glat, self.dist_grid, **params) # # Get index of tangent point along each direction # tan_idxs = np.array([ np.argmax(vlsr) if l < 90. else np.argmin(vlsr) if l > 270. else -1 for l, vlsr in zip(self.glong, grid_vlsrs.T) ]) # # Get index of near and far distances along each direction # near_idxs = np.array([ np.argmin(np.abs(vlsr[:tan_idx] - v)) if (tan_idx > 0 and np.min(np.abs(vlsr[:tan_idx] - v)) < self.velo_tol) else -1 for v, vlsr, tan_idx in zip(velo_sample, grid_vlsrs.T, tan_idxs) ]) far_idxs = np.array([ tan_idx + np.argmin(np.abs(vlsr[tan_idx:] - v)) if (tan_idx > 0 and np.min(np.abs(vlsr[tan_idx:] - v)) < self.velo_tol) else np.argmin(np.abs(vlsr - v)) if (tan_idx == -1 and np.min(np.abs(vlsr - v)) < self.velo_tol) else -1 for v, vlsr, tan_idx in zip(velo_sample, grid_vlsrs.T, tan_idxs) ]) # # Get VLSR of tangent point # vlsr_tan = np.array([ vlsr[tan_idx] if tan_idx > 0 else np.nan for vlsr, tan_idx in zip(grid_vlsrs.T, tan_idxs) ]) # # Get distances # tan_dists = self.dists[tan_idxs] tan_dists[tan_idxs == -1] = np.nan near_dists = self.dists[near_idxs] near_dists[near_idxs == -1] = np.nan far_dists = self.dists[far_idxs] far_dists[far_idxs == -1] = np.nan if self.rotcurve == "wc21_rotcurve": # Include (potentially resampled) Zsun and roll values Rgal = kd_utils.calc_Rgal(self.glong, self.glat, far_dists.T, R0=params['R0'], Zsun=params['Zsun'], roll=params['roll'], use_Zsunroll=True).T Rtan = kd_utils.calc_Rgal(self.glong, self.glat, tan_dists.T, R0=params['R0'], Zsun=params['Zsun'], roll=params['roll'], use_Zsunroll=True).T else: Rgal = kd_utils.calc_Rgal(self.glong, self.glat, far_dists.T, R0=params['R0']).T Rtan = kd_utils.calc_Rgal(self.glong, self.glat, tan_dists.T, R0=params['R0']).T return (tan_dists, near_dists, far_dists, vlsr_tan, Rgal, Rtan)