def d_phase_d_param(self, toas, delay, param): """ Return the derivative of phase with respect to the parameter. """ # TODO need to do correct chain rule stuff wrt delay derivs, etc # Is it safe to assume that any param affecting delay only affects # phase indirectly (and vice-versa)?? par = getattr(self, param) result = np.longdouble(np.zeros(len(toas))) * u.cycle/par.units param_phase_derivs = [] phase_derivs = self.phase_deriv_funcs delay_derivs = self.delay_deriv_funcs if param in list(phase_derivs.keys()): for df in phase_derivs[param]: result += df(toas, param, delay).to(result.unit, equivalencies=u.dimensionless_angles()) else: # Apply chain rule for the parameters in the delay. # total_phase = Phase1(delay(param)) + Phase2(delay(param)) # d_total_phase_d_param = d_Phase1/d_delay*d_delay/d_param + # d_Phase2/d_delay*d_delay/d_param # = (d_Phase1/d_delay + d_Phase2/d_delay) * # d_delay_d_param d_delay_d_p = self.d_delay_d_param(toas, param) dpdd_result = np.longdouble(np.zeros(len(toas))) * u.cycle/u.second for dpddf in self.d_phase_d_delay_funcs: dpdd_result += dpddf(toas, delay) result = dpdd_result * d_delay_d_p return result.to(result.unit, equivalencies=u.dimensionless_angles())
def test_lsr_sanity(): # random numbers, but zero velocity in ICRS frame icrs = ICRS(ra=15.1241*u.deg, dec=17.5143*u.deg, distance=150.12*u.pc, pm_ra_cosdec=0*u.mas/u.yr, pm_dec=0*u.mas/u.yr, radial_velocity=0*u.km/u.s) lsr = icrs.transform_to(LSR) lsr_diff = lsr.data.differentials['s'] cart_lsr_vel = lsr_diff.represent_as(CartesianRepresentation, base=lsr.data) lsr_vel = ICRS(cart_lsr_vel) gal_lsr = lsr_vel.transform_to(Galactic).cartesian.xyz assert allclose(gal_lsr.to(u.km/u.s, u.dimensionless_angles()), lsr.v_bary.d_xyz) # moving with LSR velocity lsr = LSR(ra=15.1241*u.deg, dec=17.5143*u.deg, distance=150.12*u.pc, pm_ra_cosdec=0*u.mas/u.yr, pm_dec=0*u.mas/u.yr, radial_velocity=0*u.km/u.s) icrs = lsr.transform_to(ICRS) icrs_diff = icrs.data.differentials['s'] cart_vel = icrs_diff.represent_as(CartesianRepresentation, base=icrs.data) vel = ICRS(cart_vel) gal_icrs = vel.transform_to(Galactic).cartesian.xyz assert allclose(gal_icrs.to(u.km/u.s, u.dimensionless_angles()), -lsr.v_bary.d_xyz)
def get_relevant_wcs_properties(wcs, center, distance): """ This function ... :param wcs: :param center: :param distance: :return: """ # PIXEL SIZE pixels_x = wcs.xsize pixels_y = wcs.ysize # CENTER PIXEL pixel_center = center.to_pixel(wcs) # center = Position(0.5*pixels_x - pixel_center.x - 0.5, 0.5*pixels_y - pixel_center.y - 0.5) # when not convolved ... center = Position(0.5 * pixels_x - pixel_center.x - 1, 0.5 * pixels_y - pixel_center.y - 1) # when convolved ... center_x = center.x * Unit("pix") center_y = center.y * Unit("pix") center_x = (center_x * wcs.pixelscale.x.to("deg/pix") * distance).to("pc", equivalencies=dimensionless_angles()) center_y = (center_y * wcs.pixelscale.y.to("deg/pix") * distance).to("pc", equivalencies=dimensionless_angles()) # FIELD OF VIEW field_x_angular = wcs.pixelscale.x.to("deg/pix") * pixels_x * Unit("pix") field_y_angular = wcs.pixelscale.y.to("deg/pix") * pixels_y * Unit("pix") field_x_physical = (field_x_angular * distance).to("pc", equivalencies=dimensionless_angles()) field_y_physical = (field_y_angular * distance).to("pc", equivalencies=dimensionless_angles()) return pixels_x, pixels_y, center_x, center_y, field_x_physical, field_y_physical
def test_equivalency_context_manager(): base_registry = u.get_current_unit_registry() def just_to_from_units(equivalencies): return [(equiv[0], equiv[1]) for equiv in equivalencies] tf_dimensionless_angles = just_to_from_units(u.dimensionless_angles()) tf_spectral = just_to_from_units(u.spectral()) assert base_registry.equivalencies == [] with u.set_enabled_equivalencies(u.dimensionless_angles()): new_registry = u.get_current_unit_registry() assert (set(just_to_from_units(new_registry.equivalencies)) == set(tf_dimensionless_angles)) assert set(new_registry.all_units) == set(base_registry.all_units) with u.set_enabled_equivalencies(u.spectral()): newer_registry = u.get_current_unit_registry() assert (set(just_to_from_units(newer_registry.equivalencies)) == set(tf_spectral)) assert (set(newer_registry.all_units) == set(base_registry.all_units)) assert (set(just_to_from_units(new_registry.equivalencies)) == set(tf_dimensionless_angles)) assert set(new_registry.all_units) == set(base_registry.all_units) with u.add_enabled_equivalencies(u.spectral()): newer_registry = u.get_current_unit_registry() assert (set(just_to_from_units(newer_registry.equivalencies)) == set(tf_dimensionless_angles) | set(tf_spectral)) assert (set(newer_registry.all_units) == set(base_registry.all_units)) assert base_registry is u.get_current_unit_registry()
def examinalyze(smat): mst = minimum_spanning_tree(smat) connections = np.where(mst.toarray()) lines = [(coords[ii], coords[jj]) for ii,jj in zip(*connections)] plotlines = [((a.ra.deg, b.ra.deg), (a.dec.deg, b.dec.deg)) for a,b in lines] xx,yy = np.array(list(zip(*plotlines))) lines2 = [(coords[ll], coords[rr]) for ll,rr in edges] plotlines2 = [((a.ra.deg, b.ra.deg), (a.dec.deg, b.dec.deg)) for a,b in lines2] xx2,yy2 = np.array(list(zip(*plotlines2))) import pylab as pl pl.clf() pl.plot(coords.ra.deg, coords.dec.deg, '*') pl.plot(xx.T, yy.T, color='b', alpha=0.5, linewidth=2) pl.plot(xx2.T, yy2.T, color='r', alpha=0.5, linewidth=2) inds = np.where(np.tril(smat, -1)) mps = smat[inds].mean() mbl = mst[mst.nonzero()].mean() print("Mean branch length: {0} arcsec {1}".format(mbl*3600, (mbl*u.deg*distance).to(u.pc, u.dimensionless_angles()))) print("Mean point separation: {0} arcsec {1}".format(mps*3600, (mbl*u.deg*distance).to(u.pc, u.dimensionless_angles()))) print("Q parameter: {0}".format(mbl/mps))
def test_hel_gal(): n = 100 l = np.random.uniform(0.,360.,size=n)*u.degree b = np.random.uniform(-90.,90.,size=n)*u.degree d = np.random.uniform(0.,100.,size=n)*u.kpc mul = np.random.normal(0., 300., size=n)*u.km/u.s/d mub = np.random.normal(0., 300., size=n)*u.km/u.s/d vr = np.random.normal(0., 300., size=n)*u.km/u.s mul = mul.to(u.mas/u.yr,equivalencies=u.dimensionless_angles()) mub = mub.to(u.mas/u.yr,equivalencies=u.dimensionless_angles()) xyz,vxyz = sc.hel_to_gal_xyz((l,b,d), pm=(mul,mub), vr=vr, vlsr=[0.,0.,0.]*u.km/u.s) x,y,z = xyz.decompose(usys).value vx,vy,vz = vxyz.decompose(usys).value X = hel_to_gal(np.vstack((l.decompose(usys).value, b.decompose(usys).value, d.decompose(usys).value, mul.decompose(usys).value, mub.decompose(usys).value, vr.decompose(usys).value)).T) x1,y1,z1,vx1,vy1,vz1 = X.T assert np.allclose(x1, x) assert np.allclose(y1, y) assert np.allclose(z1, z) assert np.allclose(vx1, vx) assert np.allclose(vy1, vy) assert np.allclose(vz1, vz)
def test_hel_gal(): """ Note: slight offsets between Astropy / gary transformation and this transformation are expected because this assumes (l,b)=(0,0) is the Galactic center. Astropy uses a more modern measurement of the position of the GC. """ np.random.seed(42) RSUN = 8.*u.kpc VCIRC = 240.*u.km/u.s VLSR = [0,0,0.] * u.km/u.s gc_frame = coord.Galactocentric(z_sun=0.*u.pc, galcen_distance=RSUN) l = np.random.uniform(0.,360.,size=n)*u.degree b = np.random.uniform(-90.,90.,size=n)*u.degree d = np.random.uniform(0.,100.,size=n)*u.kpc mul_cosb = np.random.normal(0., 300., size=n)*u.km/u.s/d * np.cos(b) mub = np.random.normal(0., 300., size=n)*u.km/u.s/d vr = np.random.normal(0., 300., size=n)*u.km/u.s mul_cosb = mul_cosb.to(u.mas/u.yr, equivalencies=u.dimensionless_angles()) mub = mub.to(u.mas/u.yr, equivalencies=u.dimensionless_angles()) c = coord.Galactic(l=l, b=b, distance=d) vxyz = gc.vhel_to_gal(c, (mul_cosb,mub), vr, vcirc=VCIRC, vlsr=VLSR, galactocentric_frame=gc_frame) xyz = c.transform_to(gc_frame).cartesian.xyz x,y,z = xyz.decompose(galactic).value vx,vy,vz = vxyz.decompose(galactic).value X = hel_to_gal(np.vstack((l.decompose(galactic).value, b.decompose(galactic).value, d.decompose(galactic).value, mul_cosb.decompose(galactic).value, mub.decompose(galactic).value, vr.decompose(galactic).value)).T, Vcirc=VCIRC.decompose(galactic).value, Rsun=RSUN.decompose(galactic).value) x1,y1,z1,vx1,vy1,vz1 = X.T assert np.allclose(x1, x, rtol=1E-2) assert np.allclose(y1, y, rtol=1E-2) assert np.allclose(z1, z, rtol=1E-2) assert np.allclose(vx1, vx, rtol=1E-2) assert np.allclose(vy1, vy, rtol=1E-2) assert np.allclose(vz1, vz, rtol=1E-2)
def from_sky(cls, stretch, distance): """ This function ... :param stretch: :param distance: :return: """ # Convert length1 = (stretch.x * distance).to("kpc", equivalencies=dimensionless_angles()) length2 = (stretch.y * distance).to("kpc", equivalencies=dimensionless_angles()) # Create return cls(length1, length2)
def from_physical(cls, stretch, distance): """ Thisn function ... :param stretch: :param distance: :return: """ # Convert x_angular = (stretch.x / distance).to("deg", equivalencies=dimensionless_angles()) y_angular = (stretch.y / distance).to("deg", equivalencies=dimensionless_angles()) # Create and return return cls(x_angular, y_angular)
def __call__(self, duration, samplerate): """Produce simulated voltages for given duration and samplerate Parameters ---------- duration : Quantity Should be time units samplerate : Quantity Rate at which samples should be generated The samples are complex, so the real and imaginary parts can be used as separate time streams, or can be thought of as complex voltages. The total number of samples is duration * samplerate. """ times = (np.arange(0., (duration * samplerate).to(1).value, dtype=np.float32) / samplerate).to(duration.unit) nbins = times.shape[0] spectral_power = (np.arange(0., (duration * samplerate).to(1).value, dtype=np.float32) / (duration * self.nu0)).to(1) ** self.spectral_index spectral_power *= self.fnu0.astype(np.float32) spectral_phase = np.random.uniform(size=nbins) * u.cycle with u.add_enabled_equivalencies(u.dimensionless_angles()): spectrum = np.sqrt(spectral_power) * np.exp(1j * spectral_phase) spectrum[0] = 0. return times, ifft(spectrum, overwrite_x=True)
def create_deprojection_model(self): """ This function ... :return: """ # Inform the user log.info("Calculating the deprojection parameters ...") filename = None hz = None # Get the galaxy distance, the inclination and position angle distance = self.parameters.distance inclination = self.parameters.inclination pa = self.parameters.disk.PA # Get the center pixel pixel_center = self.parameters.center.to_pixel(self.reference_wcs) xc = pixel_center.x yc = pixel_center.y # Get the pixelscale in physical units pixelscale_angular = self.reference_wcs.average_pixelscale * Unit("pix") # in deg pixelscale = (pixelscale_angular * distance).to("pc", equivalencies=dimensionless_angles()) # Get the number of x and y pixels x_size = self.reference_wcs.xsize y_size = self.reference_wcs.ysize # Create the deprojection model self.deprojection = DeprojectionModel(filename, pixelscale, pa, inclination, x_size, y_size, xc, yc, hz)
def _physical_size(self): if not hasattr(self, "_distance"): raise AttributeError("No distance has not been given.") return (self._ang_size * self.distance).to(self.distance.unit, equivalencies=u.dimensionless_angles())
def d_omega_d_par(self,par): """derivative for omega respect to user input Parameter. if par is not 'OM','OMDOT','PB' dOmega/dPar = k*dAe/dPar k = OMDOT/n Parameters ---------- par : string parameter name Return ---------- Derivitve of omega respect to par """ if par not in self.binary_params: errorMesg = par + "is not in binary parameter list." raise ValueError(errorMesg) par_obj = getattr(self, par) PB = self.pb() OMDOT = self.OMDOT OM = self.OM nu = self.nu() k = OMDOT.to(u.rad/u.second)/(2*np.pi*u.rad/PB) if par in ['OM','OMDOT']: dername = 'd_omega_d_' + par return getattr(self,dername)() elif par in self.orbits_cls.orbit_params: d_nu_d_par = self.d_nu_d_par(par) d_pb_d_par = self.d_pb_d_par(par) return d_nu_d_par * k + d_pb_d_par * nu * \ OMDOT.to(u.rad/u.second)/(2*np.pi*u.rad) else: # For parameters only in nu return (k * self.d_nu_d_par(par)).to(OM.unit/par_obj.unit, equivalencies = u.dimensionless_angles())
def d_delta_a1_proper_motion_d_KIN(self): a1 = self.a1_k(False, False) kin = self.kin() d_kin = self.delta_kin_proper_motion() d_delta_a1_proper_motion_d_KIN = - a1 * d_kin / np.sin(kin) ** 2 return d_delta_a1_proper_motion_d_KIN.to(a1.unit/kin.unit, equivalencies=u.dimensionless_angles())
def d_delayI_d_par(self,par): """Derivative on delay inverse. """ e = self.ecc() sE = np.sin(self.E()) cE = np.cos(self.E()) dE_dpar = self.prtl_der('E',par) decc_dpar = self.prtl_der('ecc',par) Dre = self.Dre() Drep = self.Drep() Drepp = self.Drepp() nHat = self.nhat() delayI = self.delayInverse() dDre_dpar = self.d_Dre_d_par(par) dDrep_dpar = self.d_Drep_d_par(par) dDrepp_dpar = self.d_Drepp_d_par(par) dnhat_dpar = self.d_nhat_d_par(par) oneMeccTcosE = (1-e*cE) # 1-e*cos(E) with u.set_enabled_equivalencies(u.dimensionless_angles()): x = -1.0/2.0*e*sE/oneMeccTcosE # -1/2*e*sin(E)/(1-e*cos(E)) dx_dpar = -sE/(2*oneMeccTcosE**2)*decc_dpar+e*(e-cE)/(2*oneMeccTcosE**2)*dE_dpar diDelay_dDre = 1+(Drep*nHat)**2+Dre*Drepp*nHat**2+Drep*nHat*(2*Dre*nHat*x-1) diDelay_dDrep = Dre*nHat*(2*Drep*nHat+Dre*nHat*x-1) diDelay_dDrepp = (Dre*nHat)**2/2 diDelay_dnhat = Dre*(-Drep+2*Drep**2*nHat+nHat*Dre*Drepp+2*x*nHat*Dre*Drep) diDelay_dx = (Dre*nHat)**2*Drep return dDre_dpar*diDelay_dDre + dDrep_dpar*diDelay_dDrep + \ dDrepp_dpar*diDelay_dDrepp + dx_dpar*diDelay_dx+ \ dnhat_dpar*diDelay_dnhat
def d_delayS_d_par(self,par): """dsDelay/dPar = dsDelay/dTM2*dTM2/dPar+ dsDelay/decc*decc/dPar+ dsDelay/dE*dE/dPar+ dsDelay/domega*domega/dPar+ dsDelay/dSINI*dSINI/dPar """ e = self.ecc() cE = np.cos(self.E()) sE = np.sin(self.E()) sOmega = np.sin(self.omega()) cOmega = np.cos(self.omega()) TM2 = self.M2.value*Tsun logNum = 1-e*cE-self.SINI*(sOmega*(cE-e)+ (1-e**2)**0.5*cOmega*sE) with u.set_enabled_equivalencies(u.dimensionless_angles()): dTM2_dpar = self.prtl_der('TM2',par) dsDelay_dTM2 = -2*np.log(logNum) decc_dpar = self.prtl_der('ecc',par) dsDelay_decc = -2*TM2/logNum*(-cE-self.SINI*(-e*cOmega*sE/np.sqrt(1-e**2)-sOmega)) dE_dpar = self.prtl_der('E',par) dsDelay_dE = -2*TM2/logNum*(e*sE-self.SINI*(np.sqrt(1-e**2)*cE*cOmega-sE*sOmega)) domega_dpar = self.prtl_der('omega',par) dsDelay_domega = 2*TM2/logNum*self.SINI*((cE-e)*cOmega-np.sqrt(1-e**2)*sE*sOmega) dSINI_dpar = self.prtl_der('SINI',par) dsDelay_dSINI = -2*TM2/logNum*(-(1-e**2)**0.5*cOmega*sE-(cE-e)*sOmega) return dTM2_dpar*dsDelay_dTM2 + decc_dpar*dsDelay_decc + \ dE_dpar*dsDelay_dE +domega_dpar*dsDelay_domega + \ dSINI_dpar*dsDelay_dSINI
def d_beta_d_par(self,par): """beta = A1/c*(1-eTheta**2)**0.5*cos(omega) eTheta = ecc+Dth ?? dBeta/dA1 = 1.0/c*(1-eTheta**2)**0.5*cos(omega) dBeta/dECC = A1/c*((-(e+dr)/sqrt(1-(e+dr)**2)*cos(omega)*de/dECC- (1-eTheta**2)**0.5*sin(omega)*domega/dECC dBeta/dEDOT = A1/c*((-(e+dr)/sqrt(1-(e+dr)**2)*cos(omega)*de/dEDOT- (1-eTheta**2)**0.5*sin(omega)*domega/dEDOT dBeta/dDth = A1/c*(-(e+dr)/sqrt(1-(e+dr)**2)*cos(omega) Other parameters dBeta/dPar = -A1/c*(1-eTheta**2)**0.5*sin(omega)*dOmega/dPar """ if par not in self.binary_params: errorMesg = par + "is not in binary parameter list." raise ValueError(errorMesg) par_obj = getattr(self, par) beta = self.beta() eTheta = self.eTheta() a1 = self.a1() omega = self.omega() sinOmg = np.sin(omega) cosOmg = np.cos(omega) d_a1_d_par = self.d_a1_d_par(par) d_omega_d_par = self.d_omega_d_par(par) d_eTheta_d_par = self.d_eTheta_d_par(par) d_beta_d_a1 = 1.0/c.c*(1-eTheta**2)**0.5*cosOmg d_beta_d_omega = -a1/c.c*(1-eTheta**2)**0.5*sinOmg d_beta_d_eTheta = a1/c.c*(-eTheta)/np.sqrt(1-eTheta**2)*cosOmg with u.set_enabled_equivalencies(u.dimensionless_angles()): return (d_beta_d_a1 * d_a1_d_par + d_beta_d_omega * d_omega_d_par + \ d_beta_d_eTheta * d_eTheta_d_par).to(beta.unit/par_obj.unit)
def d_Dre_d_par(self,par): """Dre = alpha*(cos(E)-er)+(beta+gamma)*sin(E) dDre = alpha*(-der-dE*sin(E)) + (cos[E]-er)*dalpha + (dBeta+dGamma)*sin(E) + (beta+gamma)*cos(E)*dE dDre/dpar = alpha*(-der/dpar-dE/dpar*sin(E)) + (cos[E]-er)*dalpha/dpar + (dBeta/dpar+dGamma/dpar)*sin(E) + (beta+gamma)*cos(E)*dE/dpar er = e + Dr """ Dre = self.Dre() par_obj = getattr(self, par) sinE = np.sin(self.E()) cosE = np.cos(self.E()) with u.set_enabled_equivalencies(u.dimensionless_angles()): # First term term1 = self.alpha()*(-self.prtl_der('er',par)-self.prtl_der('E',par)*sinE) # Second term term2 = (cosE-self.er())*self.prtl_der('alpha',par) # Third term term3 = (self.prtl_der('beta',par)+self.prtl_der('GAMMA',par))*sinE # Fourth term term4 = (self.beta()+self.GAMMA)*cosE*self.prtl_der('E',par) return (term1 + term2 + term3 +term4).to(Dre.unit/par_obj.unit)
def d_alpha_d_par(self,par): """T. Damour and N. Deruelle(1986)equation [46] alpha = a1/c*sin(omega) dAlpha/dpar = d_a1_d_par /c * sin(omega) + a1/c*cos(omega)*dOmega/dPar """ if par not in self.binary_params: errorMesg = par + "is not in binary parameter list." raise ValueError(errorMesg) par_obj = getattr(self, par) alpha = self.alpha() sinOmg = np.sin(self.omega()) cosOmg = np.cos(self.omega()) a1 = self.a1() d_a1_d_par = self.d_a1_d_par(par) d_omega_d_par = self.d_omega_d_par(par) with u.set_enabled_equivalencies(u.dimensionless_angles()): dAlpha_dpar = d_a1_d_par / c.c * sinOmg + a1 / c.c * cosOmg * d_omega_d_par # # if par in ['A1','A1DOT']: # dername = 'd_alpha_d_'+par # return getattr(self,dername)() # # else: # dername = 'd_omega_d_'+par # For parameters only in Ae # if hasattr(self,dername): # cosOmg=np.cos(self.omega()) # return self.a1()/c.c*cosOmg*getattr(self,dername)() # else: # return np.longdouble(np.zeros(len(self.tt0))) return dAlpha_dpar.to(alpha.unit/par_obj.unit)
def approximate_primary_beam_sizes(frequency_support_str): freq_ranges = parse_frequency_support(frequency_support_str) beam_sizes = [(1.22*fr.mean().to(u.m, u.spectral())/(12*u.m)).to(u.arcsec, u.dimensionless_angles()) for fr in freq_ranges] return u.Quantity(beam_sizes)
def test_dimensionless_angles(): # test that the angles_dimensionless option allows one to change # by any order in radian in the unit (#1161) rad1 = u.dimensionless_angles() assert u.radian.to(1, equivalencies=rad1) == 1. assert u.deg.to(1, equivalencies=rad1) == u.deg.to(u.rad) assert u.steradian.to(1, equivalencies=rad1) == 1. assert u.dimensionless_unscaled.to(u.steradian, equivalencies=rad1) == 1. # now quantities assert (1.*u.radian).to_value(1, equivalencies=rad1) == 1. assert (1.*u.deg).to_value(1, equivalencies=rad1) == u.deg.to(u.rad) assert (1.*u.steradian).to_value(1, equivalencies=rad1) == 1. # more complicated example I = 1.e45 * u.g * u.cm**2 Omega = u.cycle / (1.*u.s) Erot = 0.5 * I * Omega**2 # check that equivalency makes this work Erot_in_erg1 = Erot.to(u.erg, equivalencies=rad1) # and check that value is correct assert_allclose(Erot_in_erg1.value, (Erot/u.radian**2).to_value(u.erg)) # test build-in equivalency in subclass class MyRad1(u.Quantity): _equivalencies = rad1 phase = MyRad1(1., u.cycle) assert phase.to_value(1) == u.cycle.to(u.radian)
def d_delta_omega_proper_motion_d_KIN(self): kin = self.kin() sin_kin = np.sin(kin) cos_kin = np.cos(kin) d_omega_dot = -cos_kin/sin_kin **2 * (self.PMRA_DDK * self.cos_KOM + self.PMDEC_DDK * self.sin_KOM) return (d_omega_dot * self.tt0).to(self.OM.unit/self.KIN.unit, equivalencies=u.dimensionless_angles())
def _angle_to_length(self, arc, **kwargs): """ Approximate a surface length from the observed arc length. Uses the small angle approximation. """ r = self.map_boundary_data.dsun - self.map_boundary_data.rsun_meters length = (r * arc.to(u.radian)) return length.to(u.m, equivalencies=u.dimensionless_angles())
def convolve_to_beam(fitsfilename, beam=radio_beam.Beam(0.04*u.arcsec), distance=5400*u.pc): hdr = fits.getheader(fitsfilename) pix_area = (hdr['CDELT1']*u.cm)**2 pix_area_arcsec = (pix_area/distance**2).to(u.arcsec**2, u.dimensionless_angles()) kernel = beam.as_kernel(pix_area_arcsec**0.5) data = fits.getdata(fitsfilename) smoothed = convolve_fft(data, kernel) return fits.PrimaryHDU(data=smoothed, header=hdr)
def test_vgal_to_hel_single(self): # test a single entry row = self.data[0] c = coord.SkyCoord(ra=row['ra']*u.deg, dec=row['dec']*u.deg, distance=row['dist']*u.pc) pm = [row['pml'],row['pmb']]*u.mas/u.yr rv = row['rv']*u.km/u.s true_pmrv = (pm[0], pm[1], rv) vxyz = [row['U'],row['V'],row['W']]*u.km/u.s pmrv = vgal_to_hel(c.galactic, vxyz=vxyz, vcirc=0.*u.km/u.s, vlsr=[0.,0,0]*u.km/u.s) for i in range(3): np.testing.assert_allclose(pmrv[i].to(true_pmrv[i].unit).value, true_pmrv[i].value, atol=1.) # some sanity checks - first, some convenience definitions g = coord.Galactic(l=0*u.deg, b=0*u.deg).transform_to(coord.ICRS) frargs = dict(galcen_ra=g.ra, galcen_dec=g.dec, z_sun=0*u.kpc, galcen_distance=8*u.kpc) galcen_frame = coord.Galactocentric(**frargs) # -------------------------------------------------------------------- # l = 0 # without LSR and circular velocity # c = coord.Galactocentric([6,0,0]*u.kpc,**frargs) c = coord.SkyCoord(l=0*u.deg, b=0*u.deg, distance=2*u.kpc, frame=coord.Galactic) vxyz = [20.,0,0]*u.km/u.s pmv = vgal_to_hel(c.galactic, vxyz, vcirc=0*u.km/u.s, vlsr=[0.,0,0]*u.km/u.s, galactocentric_frame=galcen_frame) np.testing.assert_allclose(pmv[0].to(u.mas/u.yr).value, 0., atol=1E-12) np.testing.assert_allclose(pmv[1].to(u.mas/u.yr).value, 0., atol=1E-12) np.testing.assert_allclose(pmv[2].to(u.km/u.s).value, 20., atol=1E-12) # with LSR and circular velocity c = coord.SkyCoord(l=0*u.deg, b=0*u.deg, distance=2*u.kpc, frame=coord.Galactic) vxyz = [20.,0,0]*u.km/u.s pmv = vgal_to_hel(c.galactic, vxyz, vcirc=-200*u.km/u.s, vlsr=[0.,0,10]*u.km/u.s, galactocentric_frame=galcen_frame) with u.set_enabled_equivalencies(u.dimensionless_angles()): np.testing.assert_allclose(pmv[0].to(u.mas/u.yr).value, ((200.*u.km/u.s)/(2*u.kpc)).to(u.mas/u.yr).value, atol=1E-12) np.testing.assert_allclose(pmv[1].to(u.mas/u.yr).value, ((-10.*u.km/u.s)/(2*u.kpc)).to(u.mas/u.yr).value, atol=1E-4) np.testing.assert_allclose(pmv[2].to(u.km/u.s).value, 20., atol=1E-12)
def d_delta_omega_parallax_d_KIN(self): kin = self.kin() sin_kin = np.sin(kin) cos_kin = np.cos(kin) PX_kpc= self.PX.to(u.kpc, equivalencies=u.parallax()) kom_projection = self.delta_I0() * self.cos_KOM + \ self.delta_J0() * self.sin_KOM d_delta_omega_d_KIN = cos_kin / sin_kin ** 2 / PX_kpc * kom_projection return d_delta_omega_d_KIN.to(self.OM.unit / kin.unit, equivalencies=u.dimensionless_angles())
def approximate_primary_beam_sizes(frequency_support_str): """ Given a frequency support string, return the approximate 12m array beam size using 1.22 lambda / D """ freq_ranges = parse_frequency_support(frequency_support_str) beam_sizes = [(1.22 * fr.mean().to(u.m, u.spectral()) / (12 * u.m)).to(u.arcsec, u.dimensionless_angles()) for fr in freq_ranges] return u.Quantity(beam_sizes)
def delta_omega_parallax(self): """ Reference: (Kopeikin 1995 Eq 19) """ kin = self.kin() sin_kin = np.sin(kin) PX_kpc= self.PX.to(u.kpc, equivalencies=u.parallax()) delta_omega = -1.0 / sin_kin / PX_kpc * (self.delta_I0() * self.cos_KOM + \ self.delta_J0() * self.sin_KOM) return delta_omega.to(self.OM.unit, equivalencies=u.dimensionless_angles())
def create_one_dust_grid_for_galaxy_from_deprojection(grid_type, deprojection, distance, sky_ellipse, min_level, max_mass_fraction, max_ndivisions_per_pixel=2, nscaleheights=10.): """ This function ... :param grid_type: :param deprojection: :param distance: :param sky_ellipse: :param min_level: :param max_mass_fraction: :param max_ndivisions_per_pixel: :param nscaleheights: :return: """ if sky_ellipse is not None: # Calculate the major radius of the truncation ellipse in physical coordinates (pc) semimajor_angular = sky_ellipse.semimajor # semimajor axis length of the sky ellipse radius_physical = (semimajor_angular * distance).to("pc", equivalencies=dimensionless_angles()) else: x_radius_physical = deprojection.x_range.radius y_radius_physical = deprojection.y_range.radius radius_physical = max(x_radius_physical, y_radius_physical) # Get properties average_pixelscale = deprojection.pixelscale scaleheight = deprojection.scale_height # Get the pixelscale in physical units if types.is_angle(average_pixelscale): pixelscale_angular = average_pixelscale.to("deg") # pixelscale_angular = self.reference_wcs.average_pixelscale.to("deg") # in deg pixelscale = (pixelscale_angular * distance).to("pc", equivalencies=dimensionless_angles()) elif types.is_length_quantity(average_pixelscale): pixelscale = average_pixelscale.to("pc") # normally it should be this case (deprojections should have their pixelscale defined in physical units) else: raise ValueError("Pixelscale should be an angle or a length quantity") # Determine the minimum physical scale min_scale = pixelscale / float(max_ndivisions_per_pixel) # Create the dust grid return create_one_dust_grid_for_galaxy(grid_type, radius_physical, scaleheight, min_scale, min_level, max_mass_fraction, nscaleheights=nscaleheights)
def integ_to_lum(fn): hdr = fits.getheader(fn) img = fits.getdata(fn) * u.Unit(hdr["BUNIT"]) w = wcs.WCS(hdr) pixscale = np.abs(w.wcs.get_cdelt()[0]) * u.deg pixarea_cm = ((pixscale * distance) ** 2).to(u.cm ** 2, u.dimensionless_angles()) lum = pixarea_cm * img return lum.to(u.L_sun) * 4 * np.pi
def d_kin_proper_motion_d_KOM(self): d_DKIN_d_KOM = (-self.PMRA_DDK * self.cos_KOM - self.PMDEC_DDK * self.sin_KOM) * self.tt0 return d_DKIN_d_KOM.to(self.KIN.unit / self.KOM.unit, equivalencies=u.dimensionless_angles())
def circle(n_points=61): with u.add_enabled_equivalencies(u.dimensionless_angles()): circle = np.exp(np.linspace(0., 1., n_points) * u.cycle * 1j) return circle.real, circle.imag
"""Kopeikin corrected DD model.""" from __future__ import absolute_import, division, print_function import astropy.constants as c import astropy.units as u import numpy as np from astropy import log from pint import GMsun, Tsun, ls from .DD_model import DDmodel u.set_enabled_equivalencies(u.dimensionless_angles()) class DDKmodel(DDmodel): """DDK model, a Kopeikin method corrected DD model. The main difference is that DDK model considers the annual parallax of earth and the proper motion of the pulsar. effects on the pulsar binary parameters. Speical parameters are: KIN the inclination angle KOM the longitude of the ascending node, Kopeikin (1995) Eq 9. OMEGA """ def __init__(self, t=None, input_params=None): super(DDKmodel, self).__init__() self.binary_name = "DDK"
def astrometric_excess_noise(t, P, m1, m2, f1=None, f2=None, e=0, t0=None, omega=0 * u.deg, i=0 * u.deg, Omega=0 * u.deg, origin=None, **kwargs): """ Calculate the astrometric excess noise for a binary system with given properties that was observed at certain times from the given origin position. # TODO: There are a number of assumptions that we look over here :param t: The times that the system was observed. :param P: The period of the binary system. :param m1: The mass of the primary body. :param m2: The mass of the secondary body. :param f1: [optional] The flux of the primary body. If `None` is given then $M_1^{3.5}$ will be assumed. :param f2: [optional] The flux of the secondary body. If `None` is given then $M_2^{3.5}$ will be assumed. :param e: [optional] The eccentricity of the system (default: 0). # TODO: more docs pls """ # TODO: Re-factor this behemoth by applying the weights after calculating positions? if f1 is None: f1 = m1.to(u.solMass).value**3.5 if f2 is None: f2 = m2.to(u.solMass).value**3.5 if t0 is None: t0 = Time('J2015.5') N = t.size # Compute orbital positions. with u.set_enabled_equivalencies(u.dimensionless_angles()): M1 = (2 * np.pi * (t.tcb - t0.tcb) / P).to(u.radian) # Set secondary to have opposite phase. M2 = (2 * np.pi * (t.tcb - t0.tcb) / P - np.pi).to(u.radian) # eccentric anomaly E1 = twobody.eccentric_anomaly_from_mean_anomaly(M1, e) E2 = twobody.eccentric_anomaly_from_mean_anomaly(M2, e) # mean anomaly F1 = twobody.true_anomaly_from_eccentric_anomaly(E1, e) F2 = twobody.true_anomaly_from_eccentric_anomaly(E2, e) # Calc a1/a2. m_total = m1 + m2 a = twobody.P_m_to_a(P, m_total) a1 = m2 * a / m_total a2 = m1 * a / m_total r1 = (a1 * (1. - e * np.cos(E1))).to(u.au).value r2 = (a2 * (1. - e * np.cos(E2))).to(u.au).value # Calculate xy positions in orbital plane. x = np.vstack([ r1 * np.cos(F1), r2 * np.cos(F2), ]).value y = np.vstack([r1 * np.sin(F1), r2 * np.sin(F2)]).value # Calculate photocenter in orbital plane. w = np.atleast_2d([f1, f2]) / (f1 + f2) x, y = np.vstack([w @ x, w @ y]) z = np.zeros_like(x) # Calculate photocenter velocities in orbital plane. fac = (2 * np.pi * a / P / np.sqrt(1 - e**2)).to(u.au / u.s).value vx = np.vstack([-fac * np.sin(F1), -fac * np.sin(F2)]).value vy = np.vstack([fac * (np.cos(F1) + e), fac * (np.cos(F2) + e)]).value vx, vy = np.vstack([w @ vx, w @ vy]) vz = np.zeros_like(vx) # TODO: handle units better w/ dot product x, y, z = (x * u.au, y * u.au, z * u.au) vx, vy, vz = (vx * u.au / u.s, vy * u.au / u.s, vz * u.au / u.s) xyz = coord.CartesianRepresentation(x=x, y=y, z=z) vxyz = coord.CartesianDifferential(d_x=vx, d_y=vy, d_z=vz) xyz = xyz.with_differentials(vxyz) vxyz = xyz.differentials["s"] xyz = xyz.without_differentials() # Construct rotation matrix from orbital plane system to reference plane system. R1 = rotation_matrix(-omega, axis='z') R2 = rotation_matrix(i, axis='x') R3 = rotation_matrix(Omega, axis='z') Rot = matrix_product(R3, R2, R1) # Rotate photocenters to the reference plane system. XYZ = coord.CartesianRepresentation(matrix_product(Rot, xyz.xyz)) VXYZ = coord.CartesianDifferential(matrix_product(Rot, vxyz.d_xyz)) XYZ = XYZ.with_differentials(VXYZ) barycenter = twobody.Barycenter(origin=origin, t0=t0) kw = dict(origin=barycenter.origin) rp = twobody.ReferencePlaneFrame(XYZ, **kw) # Calculate the ICRS positions. icrs_cart = rp.transform_to(coord.ICRS).cartesian icrs_pos = icrs_cart.without_differentials() icrs_vel = icrs_cart.differentials["s"] bary_cart = barycenter.origin.cartesian bary_vel = bary_cart.differentials["s"] dt = t - barycenter.t0 dx = (bary_vel * dt).to_cartesian() pos = icrs_pos + dx vel = icrs_vel + bary_vel icrs = coord.ICRS(pos.with_differentials(vel)) positions = np.array([icrs.ra.deg, icrs.dec.deg]) mean_position = np.mean(positions, axis=1) assert mean_position.size == 2 ''' __intrinsic_ra_error = 0.029 # mas __intrinsic_dec_error = 0.026 # mas __intrinsic_ra_error /= 10 __intrinsic_dec_error /= 10 chi2 = N * rms_in_mas.to(u.mas).value**2 / np.sqrt(__intrinsic_ra_error**2 + __intrinsic_dec_error**2) approx_ruwe = np.sqrt(chi2/(N - 2)) ''' # Calculate on sky RMS. astrometric_rms = np.sqrt(np.sum((positions.T - mean_position)**2) / N) astrometric_rms *= u.deg diff = ((positions.T - mean_position) * u.deg).to(u.mas) #chi2 = diff**2 / (__intrinsic_ra_error**2 + __intrinsic_dec_error**2) ruwe = np.sqrt( np.sum((diff.T[0] / __intrinsic_ra_error)**2 + (diff.T[1] / __intrinsic_dec_error)**2) / (N - 2)).value meta = dict() return (ruwe, meta)
def approximate_ruwe(t, P, m1, m2, distance, f1=None, f2=None, t0=None, i=0 * u.deg, **kwargs): """ Approximate the on-sky astrometric excess noise for a binary system with the given system parameters at a certain distance. This approximating function ignores the following effects: (1) The distortions that arise due to sky projections. (2) Inclination effects. (3) Omega effects. In part it also assumes: (1) The times were observed pseudo-randomly. (2) The orbit is fully sampled. :param t: The times that the system was observed. :param P: The period of the binary system. :param m1: The mass of the primary star. :param m2: The mass of the secondary system. :param distance: The distance from the observer to the center of mass of the binary system. :param f1: [optional] The flux of the primary star. If `None` is given then this is assumed to be $m_1^{3.5}$. :param f2: [optional] The flux of the secondary. If `None` is given then this is assumed to be $m_2^{3.5}$. :returns: A two-part tuple containing the root-mean-squared deviations in on-sky position (in units of milliarcseconds), and a dictionary containing meta information about the binary system. """ if f1 is None: f1 = m1.to(u.solMass).value**3.5 if f2 is None: f2 = m2.to(u.solMass).value**3.5 if t0 is None: t0 = Time('J2015.5') m_total = m1 + m2 w = np.array([f1, f2]) / (f1 + f2) a = twobody.P_m_to_a(P, m_total).to(u.AU).value a1 = m2 * a / m_total a2 = m1 * a / m_total w1, w2 = (w[0], w[1]) # TODO: replace this with integral! dt = (t - t0).to(u.day) phi = (2 * np.pi * dt / P).value N = phi.size dx = a1 * w1 * np.cos(phi) + a2 * w2 * np.cos(phi + np.pi) dy = a1 * w1 * np.sin(phi) + a2 * w2 * np.sin(phi + np.pi) planar_rms_in_au = np.sqrt( np.sum((dx - np.mean(dx))**2 + (dy - np.mean(dy))**2) / N).value # Need some corrections for when the period is longer than the observing timespan, and the # inclination angle is non-zero. # For this it really depends on what t0/Omega is: if you see half the orbit in one phase or # another... # TODO: this requires a thinko. """ Approximate given some inclination angle. At zero inclination, assume circle on sky such that: rms = sqrt(ds^2 + ds^2) = sqrt(2ds^2) and ds = np.sqrt(0.5 * rms^2) Now when inclined (even at 90) we still get ds + contribution: rms_new = sqrt(ds^2 + (cos(i) * ds)^2) """ ds = np.sqrt(0.5 * planar_rms_in_au**2) rms_in_au = np.sqrt(ds**2 + (np.cos(i) * ds)**2) rms_in_mas = (rms_in_au * u.au / distance).to( u.mas, equivalencies=u.dimensionless_angles()) intrinsic_ra_error = kwargs.get("intrinsic_ra_error", __intrinsic_ra_error) intrinsic_dec_error = kwargs.get("intrinsic_dec_error", __intrinsic_dec_error) chi2 = N * rms_in_mas.to( u.mas).value**2 / (intrinsic_ra_error**2 + intrinsic_dec_error**2) # sqrt(2) from approximating rms in one dimension instead of 2 approx_ruwe = np.sqrt(2) * np.sqrt(chi2 / (N - 2)) meta = dict(weights=w, a=a, a1=a1, a2=a2, w1=w1, w2=w2, phi=phi, dx=dx, dy=dy, rms_in_au=rms_in_au) return (approx_ruwe, meta)
def get_parameters_from_table(self): """ This function ... :return: """ # Inform the user log.info( "Getting the structural galaxy parameters from the S4G catalog ..." ) # Inform the user log.info("Parsing S4G table 8 to get the decomposition parameters ...") #The table columns are: # (1) the running number (1-2352), # (2) the galaxy name, # (3) the type of final decomposition model, # (4) the number N of components in the final model , and # (5) the quality flag Q. # 6- 8 for unresolved central component ('psf'; phys, frel, mag), # 9-15 for inner sersic-component ('sersic1'; phys, frel, mag, re, ar, pa, n), # 16-22 for inner disk-component ('expo1'; phys, frel, mag, hr, ar, pa, mu0), # 23-28 for inner ferrers-component ('ferrers1'; phys, frel, mu0, rout, ar, pa), # 29-34 for inner edge-on disk component ('edgedisk1'; phys, frel, mu0, rs, hs, pa ). # 35-41 for outer sersic-component ('sersic2'; phys, frel, mag, re, ar, pa, n), # 42-49 for outer disk-component ('expo2'; phys, frel, mag, hr, ar, pa, mu0), # 50-55 for outer ferrers-component ('ferrers2'; phys, frel, mu0, rout, ar, pa), # 56-61 for outer edge-on disk component ('edgedisk2'; phys, frel, mu0, rs, hs, pa ). sersic_1_index = 8 disk_1_index = 15 edgedisk_1_index = 28 #For each function: #the first entry stands for the physical intepretation of the component: #'N' for a central source (or unresolved bulge), 'B' for a bulge (or elliptical), 'D' for a disk, 'BAR' for a bar, and 'Z' for an edge-on disk. # 'rel = the relative contribution of the component to the total model flux, # mag = the component's total 3.6 micron AB magnitude, # mu0 = the central surface brightness (mag/arcsec^2; de-projected central surface brightness for expdisk and edgedisk, and # sky-plane central surface brightness for ferrer) # ar = axial ratio # pa = position angle (degrees ccw from North) # n = sersic index # hr = exponential scale lenght (arcsec) # rs = radial scale lenght (arcsec) # hs = vertical scale height (arcsec) # rout = bar outer truncation radius (arcsec) # SERSIC: phys, frel, mag, re, ar, pa, n # DISK: phys, frel, mag, hr, ar, pa, mu0 # EDGEDISK: phys frel mu0 rs hs pa with open(local_table_path, 'r') as s4g_table: for line in s4g_table: splitted = line.split() if len(splitted) < 2: continue name = splitted[1] #print(list(name)) # Only look at the line corresponding to the galaxy if name != self.ngc_name_nospaces: continue #if self.ngc_name_nospaces not in name: continue #self.parameters.model_type = splitted[2] #self.parameters.number_of_components = splitted[3] #self.parameters.quality = splitted[4] ## BULGE #self.parameters.bulge.interpretation = splitted[sersic_1_index].split("|")[1] bulge_f = float(splitted[sersic_1_index + 1]) mag = float(splitted[sersic_1_index + 2]) bulge_fluxdensity = unitconversion.ab_to_jansky(mag) * u("Jy") # Effective radius in pc re_arcsec = float(splitted[sersic_1_index + 3]) * u("arcsec") bulge_re = (self.properties.distance * re_arcsec).to( "pc", equivalencies=dimensionless_angles()) bulge_q = float(splitted[sersic_1_index + 4]) bulge_pa = Angle( float(splitted[sersic_1_index + 5]) - 90., "deg") bulge_n = float(splitted[sersic_1_index + 6]) # Create the bulge component bulge = SersicModel2D(rel_contribution=bulge_f, fluxdensity=bulge_fluxdensity, effective_radius=bulge_re, axial_ratio=bulge_q, position_angle=bulge_pa, index=bulge_n) # Add the bulge to the components dictionary self.components["bulge"] = bulge ## DISK if splitted[disk_1_index + 1] != "-": #self.parameters.disk.interpretation = splitted[disk_1_index].split("|")[1] disk_f = float(splitted[disk_1_index + 1]) mag = float(splitted[disk_1_index + 2]) disk_fluxdensity = unitconversion.ab_to_jansky(mag) * u( "Jy") # Scale length in pc hr_arcsec = float(splitted[disk_1_index + 3]) * u("arcsec") disk_hr = (self.properties.distance * hr_arcsec).to( "pc", equivalencies=dimensionless_angles()) disk_q = float(splitted[disk_1_index + 4]) # axial ratio disk_pa = Angle( float(splitted[disk_1_index + 5]) - 90., "deg") disk_mu0 = float( splitted[disk_1_index + 6]) * u("mag/arcsec2") # Create the disk component disk = ExponentialDiskModel2D(rel_contribution=disk_f, fluxdensity=disk_fluxdensity, scalelength=disk_hr, axial_ratio=disk_q, position_angle=disk_pa, mu0=disk_mu0) else: # phys frel mu0 rs hs pa disk_f = float(splitted[edgedisk_1_index + 1]) mag = None fluxdensity = None # frel mu0 rs hs pa #print(disk_f) disk_mu0 = float( splitted[edgedisk_1_index + 2]) * u("mag/arcsec2") # rs hs pa # rs = radial scale lenght (arcsec) # hs = vertical scale height (arcsec) rs = float(splitted[edgedisk_1_index + 3]) hs = float(splitted[edgedisk_1_index + 4]) #print(rs) #print(hs) disk_pa = Angle( float(splitted[edgedisk_1_index + 5]) - 90., "deg") #print(disk_pa) disk_q = hs / rs # Create the disk component disk = ExponentialDiskModel2D(rel_contribution=disk_f, scalelength=rs, axial_ratio=disk_q, position_angle=disk_pa, mu0=disk_mu0) # Add the disk to the components dictionary self.components["disk"] = disk # Set the disk position angle self.disk_pa = self.components["disk"].position_angle
from astropy import units as u from astropy import constants from masscalc import distance, centerfreq as freq im = fits.getdata(paths.dpath('W51_te_continuum_best.fits')) hd = fits.getheader(paths.dpath('W51_te_continuum_best.fits')) beam = radio_beam.Beam.from_fits_header(hd) e2e_peak_flux = im[1350:1400, 850:867].max() * u.Jy e2e_peak_tb = e2e_peak_flux.to(u.K, beam.jtok_equiv(freq)) print("e2e peak brightness: {0}".format(e2e_peak_tb)) e2e_luminosity = (constants.sigma_sb * (e2e_peak_tb)**4 * (4 * np.pi * (beam.major * distance) * (beam.minor * distance) / (8 * np.log(2)))).to( u.L_sun, u.dimensionless_angles()) print("e2e luminosity: {0}".format(e2e_luminosity)) e2e_dustmass = dust_emissivity.dust.massofsnu(freq, e2e_peak_flux, distance=distance, temperature=e2e_peak_tb) print("e2e dust mass: {0}".format(e2e_dustmass)) e2e_peak_column = dust_emissivity.dust.colofsnu(freq, e2e_peak_flux, beamomega=beam, temperature=100 * u.K).to( u.cm**-2, u.dimensionless_angles()) print("e2e peak column (T=100K): {0}".format(e2e_peak_column))
def d_DDdelay_d_par(self, par): """Full DD model delay derivtive""" with u.set_enabled_equivalencies(u.dimensionless_angles()): return (self.d_delayI_d_par(par) + self.d_delayS_d_par(par) + self.d_delayA_d_par(par))
def test_gyroradius(): r"""Test the gyroradius function in parameters.py.""" assert gyroradius(B, T_i=T_e).unit.is_equivalent(u.m) assert gyroradius(B, Vperp=25 * u.m / u.s).unit.is_equivalent(u.m) Vperp = 1e6 * u.m / u.s Bmag = 1 * u.T omega_ce = gyrofrequency(Bmag) analytical_result = (Vperp / omega_ce).to( u.m, equivalencies=u.dimensionless_angles() ) assert gyroradius(Bmag, Vperp=Vperp) == analytical_result with pytest.raises(TypeError): gyroradius(u.T) with pytest.raises(u.UnitTypeError): gyroradius(5 * u.A, Vperp=8 * u.m / u.s) with pytest.raises(u.UnitTypeError): gyroradius(5 * u.T, Vperp=8 * u.m) with pytest.raises(ValueError): gyroradius(np.array([5, 6]) * u.T, Vperp=np.array([5, 6, 7]) * u.m / u.s) assert np.isnan(gyroradius(np.nan * u.T, Vperp=1 * u.m / u.s)) with pytest.raises(ValueError): gyroradius(3.14159 * u.T, T_i=-1 * u.K) with pytest.warns(u.UnitsWarning): assert gyroradius(1.0, Vperp=1.0) == gyroradius( 1.0 * u.T, Vperp=1.0 * u.m / u.s ) with pytest.warns(u.UnitsWarning): assert gyroradius(1.1, T_i=1.2) == gyroradius(1.1 * u.T, T_i=1.2 * u.K) with pytest.raises(ValueError): gyroradius(1.1 * u.T, Vperp=1 * u.m / u.s, T_i=1.2 * u.K) with pytest.raises(u.UnitTypeError): gyroradius(1.1 * u.T, Vperp=1.1 * u.m, T_i=1.2 * u.K) assert gyroradius(B, particle="p", T_i=T_i).unit.is_equivalent(u.m) assert gyroradius(B, particle="p", Vperp=25 * u.m / u.s).unit.is_equivalent(u.m) # Case when Z=1 is assumed assert np.isclose( gyroradius(B, particle="p", T_i=T_i), gyroradius(B, particle="H+", T_i=T_i), atol=1e-6 * u.m, ) gyroPos = gyroradius(B, particle="p", Vperp=V) gyroNeg = gyroradius(B, particle="p", Vperp=-V) assert gyroPos == gyroNeg Vperp = 1e6 * u.m / u.s Bmag = 1 * u.T omega_ci = gyrofrequency(Bmag, particle="p") analytical_result = (Vperp / omega_ci).to( u.m, equivalencies=u.dimensionless_angles() ) assert gyroradius(Bmag, particle="p", Vperp=Vperp) == analytical_result T2 = 1.2 * u.MK B2 = 123 * u.G particle2 = "alpha" Vperp2 = thermal_speed(T2, particle=particle2) gyro_by_vperp = gyroradius(B2, particle="alpha", Vperp=Vperp2) assert gyro_by_vperp == gyroradius(B2, particle="alpha", T_i=T2) explicit_positron_gyro = gyroradius(1 * u.T, particle="positron", T_i=1 * u.MK) assert explicit_positron_gyro == gyroradius(1 * u.T, T_i=1 * u.MK) with pytest.raises(TypeError): gyroradius(u.T, particle="p", Vperp=8 * u.m / u.s) with pytest.raises(ValueError): gyroradius(B, particle="p", T_i=-1 * u.K) with pytest.warns(u.UnitsWarning): gyro_without_units = gyroradius(1.0, particle="p", Vperp=1.0) gyro_with_units = gyroradius(1.0 * u.T, particle="p", Vperp=1.0 * u.m / u.s) assert gyro_without_units == gyro_with_units with pytest.warns(u.UnitsWarning): gyro_t_without_units = gyroradius(1.1, particle="p", T_i=1.2) gyro_t_with_units = gyroradius(1.1 * u.T, particle="p", T_i=1.2 * u.K) assert gyro_t_with_units == gyro_t_without_units with pytest.raises(ValueError): gyroradius(1.1 * u.T, particle="p", Vperp=1 * u.m / u.s, T_i=1.2 * u.K) with pytest.raises(u.UnitTypeError): gyroradius(1.1 * u.T, particle="p", Vperp=1.1 * u.m, T_i=1.2 * u.K) with pytest.raises(u.UnitTypeError): gyroradius(1.1 * u.T, particle="p", Vperp=1.2 * u.m, T_i=1.1 * u.K)
elif 'HERACOMING' in galaxy_ratios['SURVEY_PAIR']: idx = galaxy_ratios['SURVEY_PAIR'] == 'HERACOMING' r21_gal = 10**galaxy_ratios[idx]['LOGRAT'][0] elif 'HERANROATLAS' in galaxy_ratios['SURVEY_PAIR']: idx = galaxy_ratios['SURVEY_PAIR'] == 'HERANROATLAS' r21_gal = 10**galaxy_ratios[idx]['LOGRAT'][0] else: print('No galaxy-specific R21 value found for ' + galaxy['NAME'] + ". Using fiducial value.") r21_gal = r21 # convert Re to to kpc # The input map is in Mstar/yr/kpc^2, so I think I want the output unit to be Mstar/yr/kpc^2. re_kpc = ((galaxy['RE_ARCSEC'] * u.arcsec) * (galaxy['DIST_MPC'] * u.Mpc)).to( u.kpc, equivalencies=u.dimensionless_angles()) # now do more complex spatial scaling sfrR21scale(cube, sigmaSFR, galaxy=galaxy['NAME'], re=re_kpc.value, sfr=10**galaxy['LOGSFR'], r21=r21_gal, r21_ref=r21_ref) sfrR21scale(mom0, sigmaSFR, galaxy=galaxy['NAME'], re=re_kpc.value, sfr=10**galaxy['LOGSFR'],
def test_gyroradius(): r"""Test the gyroradius function in parameters.py.""" assert gyroradius(B, T_e).unit == u.m assert gyroradius(B, 25 * u.m / u.s).unit == u.m assert gyroradius(T_e, B) == gyroradius(B, T_e) assert gyroradius(V, B) == gyroradius(B, V) assert gyroradius(B, V) == gyroradius(B, -V) Vperp = 1e6 * u.m / u.s Bmag = 1 * u.T omega_ce = gyrofrequency(Bmag) assert gyroradius(Bmag, Vperp) == \ (Vperp / omega_ce).to(u.m, equivalencies=u.dimensionless_angles()) with pytest.raises(TypeError): gyroradius(u.T, 8 * u.m / u.s) with pytest.raises(u.UnitConversionError): gyroradius(5 * u.A, 8 * u.m / u.s) with pytest.raises(u.UnitConversionError): gyroradius(5 * u.T, 8 * u.m) with pytest.raises(ValueError): gyroradius(np.array([5, 6]) * u.T, np.array([5, 6, 7]) * u.m / u.s) with pytest.raises(ValueError): gyroradius(np.nan * u.T, 1 * u.m / u.s) with pytest.raises(ValueError): gyroradius(3.14159 * u.T, -1 * u.K) with pytest.raises(UserWarning): assert gyroradius(1.0, Vperp=1.0) == \ gyroradius(1.0 * u.T, Vperp=1.0 * u.m / u.s) with pytest.raises(UserWarning): assert gyroradius(1.1, T_i=1.2) == \ gyroradius(1.1 * u.T, T_i=1.2 * u.K) with pytest.raises(ValueError): gyroradius(1.1 * u.T, T_i=1.2 * u.K, Vperp=1 * u.m / u.s) with pytest.raises(ValueError): gyroradius(1.1 * u.T, 1.2 * u.K, 1.1 * u.m) assert gyroradius(B, T_i, particle="p").unit == u.m assert gyroradius(B, 25 * u.m / u.s, particle="p").unit == u.m # Case when Z=1 is assumed assert gyroradius(B, T_i, particle='p') == \ gyroradius(B, T_i, particle='H-1') assert gyroradius(T_i, B, particle="p") == gyroradius(B, T_i, particle="p") assert gyroradius(V, B, particle="p") == gyroradius(B, V, particle="p") assert gyroradius(B, V, particle="p") == gyroradius(B, -V, particle="p") Vperp = 1e6 * u.m / u.s Bmag = 1 * u.T omega_ci = gyrofrequency(Bmag, particle='p') assert gyroradius(Bmag, Vperp, particle="p") == \ (Vperp / omega_ci).to(u.m, equivalencies=u.dimensionless_angles()) T2 = 1.2 * u.MK B2 = 123 * u.G particle2 = 'alpha' Vperp2 = thermal_speed(T2, particle=particle2) assert gyroradius(B2, Vperp=Vperp2, particle='alpha') == \ gyroradius(B2, T_i=T2, particle='alpha') assert gyroradius(1 * u.T, 1 * u.MK, particle='positron') == \ gyroradius(1 * u.T, 1 * u.MK) with pytest.raises(TypeError): gyroradius(u.T, 8 * u.m / u.s, particle="p") with pytest.raises(ValueError): gyroradius(B, T_i, particle='asfdas') with pytest.raises(ValueError): gyroradius(B, -1 * u.K, particle='p') with pytest.raises(UserWarning): assert gyroradius(1.0, Vperp=1.0, particle="p") == \ gyroradius(1.0 * u.T, Vperp=1.0 * u.m / u.s, particle="p") with pytest.raises(UserWarning): assert gyroradius(1.1, T_i=1.2, particle="p") == \ gyroradius(1.1 * u.T, T_i=1.2 * u.K, particle="p") with pytest.raises(ValueError): gyroradius(1.1 * u.T, T_i=1.2 * u.K, Vperp=1 * u.m / u.s, particle="p") with pytest.raises(ValueError): gyroradius(1.1 * u.T, 1.2 * u.K, 1.1 * u.m, particle="p") with pytest.raises(ValueError): gyroradius(1.1 * u.T, 1.2 * u.m, 1.1 * u.K, particle="p")
def gyroradius(B: u.T, particle='e-', *, Vperp: u.m / u.s = np.nan * u.m / u.s, T_i: u.K = np.nan * u.K): r"""Return the particle gyroradius. Parameters ---------- B : ~astropy.units.Quantity The magnetic field magnitude in units convertible to tesla. particle : str, optional Representation of the particle species (e.g., `'p'` for protons, `'D+'` for deuterium, or `'He-4 +1'` for singly ionized helium-4), which defaults to electrons. If no charge state information is provided, then the particles are assumed to be singly charged. Vperp : ~astropy.units.Quantity, optional The component of particle velocity that is perpendicular to the magnetic field in units convertible to meters per second. Must be input as a keyword argument. T_i : ~astropy.units.Quantity, optional The particle temperature in units convertible to kelvin. Must be input as a keyword argument. Returns ------- r_Li : ~astropy.units.Quantity The particle gyroradius in units of meters. This ~astropy.units.Quantity will be based on either the perpendicular component of particle velocity as inputted, or the most probable speed for an particle within a Maxwellian distribution for the particle temperature. Raises ------ TypeError The arguments are of an incorrect type ~astropy.units.UnitConversionError The arguments do not have appropriate units ValueError If any argument contains invalid values Warns ----- ~astropy.units.UnitsWarning If units are not provided, SI units are assumed Notes ----- One but not both of `Vperp` and `T_i` must be inputted. If any of `B`, `Vperp`, or `T_i` is a number rather than a `~astropy.units.Quantity`, then SI units will be assumed and a warning will be raised. The particle gyroradius is also known as the particle Larmor radius and is given by .. math:: r_{Li} = \frac{V_{\perp}}{omega_{ci}} where :math:`V_{\perp}` is the component of particle velocity that is perpendicular to the magnetic field and :math:`\omega_{ci}` is the particle gyrofrequency. If a temperature is provided, then :math:`V_\perp` will be the most probable thermal velocity of an particle at that temperature. Examples -------- >>> from astropy import units as u >>> gyroradius(0.2*u.T,particle='p+',T_i=1e5*u.K) <Quantity 0.00212087 m> >>> gyroradius(0.2*u.T,particle='p+',T_i=1e5*u.K) <Quantity 0.00212087 m> >>> gyroradius(5*u.uG,particle='alpha',T_i=1*u.eV) <Quantity 288002.38837768 m> >>> gyroradius(400*u.G,particle='Fe+++',Vperp=1e7*u.m/u.s) <Quantity 48.23129811 m> >>> gyroradius(B=0.01*u.T,T_i=1e6*u.K) <Quantity 0.00313033 m> >>> gyroradius(B=0.01*u.T,Vperp=1e6*u.m/u.s) <Quantity 0.00056856 m> >>> gyroradius(0.2*u.T,T_i=1e5*u.K) <Quantity 4.94949252e-05 m> >>> gyroradius(5*u.uG,T_i=1*u.eV) <Quantity 6744.2598183 m> >>> gyroradius(400*u.G,Vperp=1e7*u.m/u.s) <Quantity 0.00142141 m> """ isfinite_Ti = np.isfinite(T_i) isfinite_Vperp = np.isfinite(Vperp) # check 1: ensure either Vperp or T_i invalid, keeping in mind that # the underlying values of the astropy quantity may be numpy arrays if np.any(np.logical_not(np.logical_xor(isfinite_Vperp, isfinite_Ti))): raise ValueError( "Must give Vperp or T_i, but not both, as arguments to gyroradius") # check 2: get Vperp as the thermal speed if is not already a valid input if np.isscalar(Vperp.value) and np.isscalar( T_i.value): # both T_i and Vperp are scalars # we know exactly one of them is nan from check 1 if isfinite_Ti: # T_i is valid, so use it to determine Vperp Vperp = thermal_speed(T_i, particle=particle) # else: Vperp is alread valid, do nothing elif np.isscalar(Vperp.value): # only T_i is an array # this means either Vperp must be nan, or T_i must be array of all nan, # or else we couldn't have gotten through check 1 if isfinite_Vperp: # Vperp is valid, T_i is a vector that is all nan # uh... Vperp = np.repeat(Vperp, len(T_i)) else: # normal case where Vperp is scalar nan and T_i is valid array Vperp = thermal_speed(T_i, particle=particle) elif np.isscalar(T_i.value): # only Vperp is an array # this means either T_i must be nan, or V_perp must be array of all nan, # or else we couldn't have gotten through check 1 if isfinite_Ti: # T_i is valid, V_perp is an array of all nan # uh... Vperp = thermal_speed(np.repeat(T_i, len(Vperp)), particle=particle) # else: normal case where T_i is scalar nan and Vperp is already a valid array # so, do nothing else: # both T_i and Vperp are arrays # we know all the elementwise combinations have one nan and one finite, due to check 1 # use the valid Vperps, and replace the others with those calculated from T_i Vperp = Vperp.copy() # avoid changing Vperp's value outside function Vperp[isfinite_Ti] = thermal_speed(T_i[isfinite_Ti], particle=particle) omega_ci = gyrofrequency(B, particle) r_Li = np.abs(Vperp) / omega_ci return r_Li.to(u.m, equivalencies=u.dimensionless_angles())
def fold(fh, comm, samplerate, fedge, fedge_at_top, nchan, nt, ntint, ngate, ntbin, ntw, dm, fref, phasepol, dedisperse='incoherent', do_waterfall=True, do_foldspec=True, verbose=True, progress_interval=100, rfi_filter_raw=None, rfi_filter_power=None, return_fits=False): """ FFT data, fold by phase/time and make a waterfall series Folding is done from the position the file is currently in Parameters ---------- fh : file handle handle to file holding voltage timeseries comm: MPI communicator or None will use size, rank attributes samplerate : Quantity rate at which samples were originally taken and thus double the band width (frequency units) fedge : float edge of the frequency band (frequency units) fedge_at_top: bool whether edge is at top (True) or bottom (False) nchan : int number of frequency channels for FFT nt, ntint : int total number nt of sets, each containing ntint samples in each file hence, total # of samples is nt*ntint, with each sample containing a single polarisation ngate, ntbin : int number of phase and time bins to use for folded spectrum ntbin should be an integer fraction of nt ntw : int number of time samples to combine for waterfall (does not have to be integer fraction of nt) dm : float dispersion measure of pulsar, used to correct for ism delay (column number density) fref: float reference frequency for dispersion measure phasepol : callable function that returns the pulsar phase for time in seconds relative to start of the file that is read. dedisperse : None or string (default: incoherent). None, 'incoherent', 'coherent', 'by-channel'. Note: None really does nothing do_waterfall, do_foldspec : bool whether to construct waterfall, folded spectrum (default: True) verbose : bool or int whether to give some progress information (default: True) progress_interval : int Ping every progress_interval sets return_fits : bool (default: False) return a subint fits table for rank == 0 (None otherwise) """ assert dedisperse in (None, 'incoherent', 'by-channel', 'coherent') assert nchan % fh.nchan == 0 if dedisperse == 'by-channel': oversample = nchan // fh.nchan assert ntint % oversample == 0 else: oversample = 1 if dedisperse == 'coherent' and fh.nchan > 1: raise ValueError("For coherent dedispersion, data must be " "unchannelized before folding.") if comm is None: mpi_rank = 0 mpi_size = 1 else: mpi_rank = comm.rank mpi_size = comm.size npol = getattr(fh, 'npol', 1) assert npol == 1 or npol == 2 if verbose > 1 and mpi_rank == 0: print("Number of polarisations={}".format(npol)) # initialize folded spectrum and waterfall # TODO: use estimated number of points to set dtype if do_foldspec: foldspec = np.zeros((ntbin, nchan, ngate, npol**2), dtype=np.float32) icount = np.zeros((ntbin, nchan, ngate), dtype=np.int32) else: foldspec = None icount = None if do_waterfall: nwsize = nt * ntint // ntw waterfall = np.zeros((nwsize, nchan, npol**2), dtype=np.float64) else: waterfall = None if verbose and mpi_rank == 0: print('Reading from {}'.format(fh)) nskip = fh.tell() / fh.blocksize if nskip > 0: if verbose and mpi_rank == 0: print('Starting {0} blocks = {1} bytes out from start.'.format( nskip, nskip * fh.blocksize)) dt1 = (1. / samplerate).to(u.s) # need 2*nchan real-valued samples for each FFT if fh.telescope == 'lofar': dtsample = fh.dtsample else: dtsample = nchan // oversample * 2 * dt1 tstart = dtsample * ntint * nskip # pre-calculate time delay due to dispersion in coarse channels # for channelized data, frequencies are known if fh.nchan == 1: if getattr(fh, 'data_is_complex', False): # for complex data, really each complex sample consists of # 2 real ones, so multiply dt1 by 2. if fedge_at_top: freq = fedge - fftfreq(nchan, 2. * dt1.value) * u.Hz else: freq = fedge + fftfreq(nchan, 2. * dt1.value) * u.Hz else: if fedge_at_top: freq = fedge - rfftfreq(nchan * 2, dt1.value)[::2] * u.Hz else: freq = fedge + rfftfreq(nchan * 2, dt1.value)[::2] * u.Hz freq_in = freq else: # input frequencies may not be the ones going out freq_in = fh.frequencies if oversample == 1: freq = freq_in else: if fedge_at_top: freq = (freq_in[:, np.newaxis] - u.Hz * fftfreq(oversample, dtsample.value)) else: freq = (freq_in[:, np.newaxis] + u.Hz * fftfreq(oversample, dtsample.value)) ifreq = freq.ravel().argsort() # pre-calculate time offsets in (input) channelized streams dt = dispersion_delay_constant * dm * (1. / freq_in**2 - 1. / fref**2) if dedisperse in ['coherent', 'by-channel']: # pre-calculate required turns due to dispersion if fedge_at_top: fcoh = (freq_in[np.newaxis, :] - u.Hz * fftfreq(ntint, dtsample.value)[:, np.newaxis]) else: fcoh = (freq_in[np.newaxis, :] + u.Hz * fftfreq(ntint, dtsample.value)[:, np.newaxis]) # set frequency relative to which dispersion is coherently corrected if dedisperse == 'coherent': _fref = fref else: _fref = freq_in[np.newaxis, :] # (check via eq. 5.21 and following in # Lorimer & Kramer, Handbook of Pulsar Astronomy dang = (dispersion_delay_constant * dm * fcoh * (1. / _fref - 1. / fcoh)**2) * u.cycle with u.set_enabled_equivalencies(u.dimensionless_angles()): dd_coh = np.exp(dang * 1j).conj().astype(np.complex64) # add dimension for polarisation dd_coh = dd_coh[..., np.newaxis] # Calculate the part of the whole file this node should handle. size_per_node = (nt - 1) // mpi_size + 1 start_block = mpi_rank * size_per_node end_block = min((mpi_rank + 1) * size_per_node, nt) for j in range(start_block, end_block): if verbose and j % progress_interval == 0: print('#{:4d}/{:4d} is doing {:6d}/{:6d} [={:6d}/{:6d}]; ' 'time={:18.12f}'.format( mpi_rank, mpi_size, j + 1, nt, j - start_block + 1, end_block - start_block, (tstart + dtsample * j * ntint).value)) # time since start # Just in case numbers were set wrong -- break if file ends; # better keep at least the work done. try: raw = fh.seek_record_read(int((nskip + j) * fh.blocksize), fh.blocksize) except (EOFError, IOError) as exc: print("Hit {0!r}; writing data collected.".format(exc)) break if verbose >= 2: print("#{:4d}/{:4d} read {} items".format(mpi_rank, mpi_size, raw.size), end="") if npol == 2: # multiple polarisations raw = raw.view(raw.dtype.fields.values()[0][0]) if fh.nchan == 1: # raw.shape=(ntint*npol) raw = raw.reshape(-1, npol) else: # raw.shape=(ntint, nchan*npol) raw = raw.reshape(-1, fh.nchan, npol) if rfi_filter_raw is not None: raw, ok = rfi_filter_raw(raw) if verbose >= 2: print("... raw RFI (zap {0}/{1})".format( np.count_nonzero(~ok), ok.size), end="") if np.can_cast(raw.dtype, np.float32): vals = raw.astype(np.float32) else: assert raw.dtype.kind == 'c' vals = raw if fh.nchan == 1: # have real-valued time stream of complex baseband # if we need some coherentdedispersion, do FT of whole thing, # otherwise to output channels if raw.dtype.kind == 'c': ftchan = nchan if dedisperse == 'incoherent' else len(vals) vals = fft(vals.reshape(-1, ftchan, npol), axis=1, overwrite_x=True, **_fftargs) else: # real data ftchan = nchan if dedisperse == 'incoherent' else len( vals) // 2 vals = rfft(vals.reshape(-1, ftchan * 2, npol), axis=1, overwrite_x=True, **_fftargs) # rfft: Re[0], Re[1], Im[1], ..., Re[n/2-1], Im[n/2-1], Re[n/2] # re-order to normal fft format (like Numerical Recipes): # Re[0], Re[n], Re[1], Im[1], .... (channel 0 is junk anyway) vals = np.hstack( (vals[:, 0], vals[:, -1], vals[:, 1:-1])).view(np.complex64) # for incoherent, vals.shape=(ntint, nchan, npol) -> OK # for others, have (1, ntint*nchan, npol) # reshape(nchan, ntint) gives rough as slowly varying -> .T if dedisperse != 'incoherent': fine = vals.reshape(nchan, -1, npol).transpose(1, 0, 2) # now have fine.shape=(ntint, nchan, npol) else: # data already channelized if dedisperse == 'by-channel': fine = fft(vals, axis=0, overwrite_x=True, **_fftargs) # have fine.shape=(ntint, fh.nchan, npol) if dedisperse in ['coherent', 'by-channel']: fine *= dd_coh # rechannelize to output channels if oversample > 1 and dedisperse == 'by-channel': # fine.shape=(ntint*oversample, chan_in, npol) # =(coarse,fine,fh.chan, npol) # -> reshape(oversample, ntint, fh.nchan, npol) # want (ntint=fine, fh.nchan, oversample, npol) -> .transpose fine = (fine.reshape(oversample, -1, fh.nchan, npol).transpose( 1, 2, 0, 3).reshape(-1, nchan, npol)) # now, for both, fine.shape=(ntint, nchan, npol) vals = ifft(fine, axis=0, overwrite_x=True, **_fftargs) # vals[time, chan, pol] if verbose >= 2: print("... dedispersed", end="") if npol == 1: power = vals.real**2 + vals.imag**2 else: p0 = vals[..., 0] p1 = vals[..., 1] power = np.empty(vals.shape[:-1] + (4, ), np.float32) power[..., 0] = p0.real**2 + p0.imag**2 power[..., 1] = p0.real * p1.real + p0.imag * p1.imag power[..., 2] = p0.imag * p1.real - p0.real * p1.imag power[..., 3] = p1.real**2 + p1.imag**2 if verbose >= 2: print("... power", end="") if rfi_filter_power is not None: power = rfi_filter_power(power) print("... power RFI", end="") # current sample positions in stream isr = j * (ntint // oversample) + np.arange(ntint // oversample) if do_waterfall: # loop over corresponding positions in waterfall for iw in xrange(isr[0] // ntw, isr[-1] // ntw + 1): if iw < nwsize: # add sum of corresponding samples waterfall[iw, :] += np.sum(power[isr // ntw == iw], axis=0)[ifreq] if verbose >= 2: print("... waterfall", end="") if do_foldspec: ibin = (j * ntbin) // nt # bin in the time series: 0..ntbin-1 # times since start tsample = (tstart + isr * dtsample * oversample)[:, np.newaxis] # correct for delay if needed if dedisperse in ['incoherent', 'by-channel']: # tsample.shape=(ntint/oversample, nchan_in) tsample = tsample - dt phase = (phasepol(tsample.to(u.s).value.ravel()).reshape( tsample.shape)) # corresponding PSR phases iphase = np.remainder(phase * ngate, ngate).astype(np.int) for k, kfreq in enumerate(ifreq): # sort in frequency while at it iph = iphase[:, (0 if iphase.shape[1] == 1 else kfreq // oversample)] # sum and count samples by phase bin for ipow in xrange(npol**2): foldspec[ibin, k, :, ipow] += np.bincount(iph, power[:, kfreq, ipow], ngate) icount[ibin, k, :] += np.bincount(iph, power[:, kfreq, 0] != 0., ngate) if verbose >= 2: print("... folded", end="") if verbose >= 2: print("... done") #Commented out as workaround, this was causing "Referenced before assignment" errors with JB data #if verbose >= 2 or verbose and mpi_rank == 0: # print('#{:4d}/{:4d} read {:6d} out of {:6d}' # .format(mpi_rank, mpi_size, j+1, nt)) if npol == 1: if do_foldspec: foldspec = foldspec.reshape(foldspec.shape[:-1]) if do_waterfall: waterfall = waterfall.reshape(waterfall.shape[:-1]) return foldspec, icount, waterfall
def ELL1_om(self): # arctan(om) om = np.arctan2(self.eps1(), self.eps2()) return om.to(u.deg, equivalencies=u.dimensionless_angles())
def d_kin_proper_motion_d_T0(self): d_DKIN_d_T0 = -1 * (-self.PMRA_DDK * self.sin_KOM + self.PMDEC_DDK * self.cos_KOM) return d_DKIN_d_T0.to(self.KIN.unit / self.T0.unit, equivalencies=u.dimensionless_angles())
def test_vgal_to_hel_single(self): for row in self.data: # test one entry at a time c = coord.SkyCoord(ra=row['ra'] * u.deg, dec=row['dec'] * u.deg, distance=row['dist'] * u.pc) gal = c.galactic vxyz = [row['U'], row['V'], row['W']] * u.km / u.s vhel = vgal_to_hel(gal, vxyz, vcirc=0. * u.km / u.s, vlsr=[0., 0, 0] * u.km / u.s, galactocentric_frame=self.galcen_frame) # tolerance set by the catalog rounded numbers assert quantity_allclose(vhel[0], row['pml'] * u.mas / u.yr, rtol=1E-2) assert quantity_allclose(vhel[1], row['pmb'] * u.mas / u.yr, rtol=1E-2) assert quantity_allclose(vhel[2], row['rv'] * u.km / u.s, rtol=1E-2) # -------------------------------------------------------------------- # l = 0 # without LSR and circular velocity c = coord.SkyCoord(l=0 * u.deg, b=0 * u.deg, distance=2 * u.kpc, frame=coord.Galactic) vxyz = [20., 0, 0] * u.km / u.s vhel = vgal_to_hel(c.galactic, vxyz, vcirc=0 * u.km / u.s, vlsr=[0., 0, 0] * u.km / u.s, galactocentric_frame=self.galcen_frame) assert np.allclose(vhel[0].value, 0., atol=1E-12) assert np.allclose(vhel[1].value, 0., atol=1E-12) assert np.allclose(vhel[2].to(u.km / u.s).value, 20., atol=1E-12) vxyz = [20., 0, 50] * u.km / u.s vhel = vgal_to_hel(c.galactic, vxyz, vcirc=0 * u.km / u.s, vlsr=[0., 0, 0] * u.km / u.s, galactocentric_frame=self.galcen_frame) assert np.allclose(vhel[0].value, 0., atol=2E-5) # TODO: astropy precision issues with u.set_enabled_equivalencies(u.dimensionless_angles()): assert quantity_allclose(vhel[1], 50 * u.km / u.s / (2 * u.kpc), atol=1E-10 * u.mas / u.yr) assert quantity_allclose(vhel[2].to(u.km / u.s), vxyz[0], atol=1E-10 * u.km / u.s) # with LSR and circular velocity vxyz = [20., 0, 50] * u.km / u.s vhel = vgal_to_hel(c.galactic, vxyz, vcirc=-200 * u.km / u.s, vlsr=[0., 0, 10] * u.km / u.s, galactocentric_frame=self.galcen_frame) with u.set_enabled_equivalencies(u.dimensionless_angles()): assert quantity_allclose(vhel[0], (200. * u.km / u.s) / (2 * u.kpc), atol=1E-10 * u.mas / u.yr) assert quantity_allclose(vhel[1], (40. * u.km / u.s) / (2 * u.kpc), atol=1E-6 * u.mas / u.yr) assert quantity_allclose(vhel[2], 20. * u.km / u.s, atol=1E-10 * u.km / u.s)
def virial_mass_estimator(cluster): """ Calculates virial masses of galaxy clusters with the virial mass formula Parameters ---------- cluster: Cluster object Returns ------- mass: float Mass of cluster in M_sun cluster_vel_disp: float Estimated velocity dispersion of cluster in (km/s)**2 cluster_radius: float Estimated radius of cluster in Mpc """ member_galaxies = cluster.galaxies # velocity dispersion cluster_size = len(member_galaxies) z_arr = member_galaxies[:, 2] average_redshift = np.mean(z_arr) cluster_vel = redshift_to_velocity(z_arr, average_redshift) cluster_vel_err = velocity_error(cluster, z_arr, average_redshift, cluster_vel) cluster_vel_disp = velocity_dispersion(cluster_vel, cluster_size) bootfunc = lambda x: (velocity_dispersion(x, len(x))) bootstrap_disp = np.std(bootstrapping(cluster_vel.value, bootfunc)) harmonic_disp = (1 / cluster_size) * (sum(1 / (cluster_vel_err**2)))**(-1) combined_disp_err = (np.sqrt(harmonic_disp.value**2 + (bootstrap_disp)**2)) * (u.km**2 / u.s**2) # galaxy separations c = SkyCoord(ra=member_galaxies[:, 0] * u.degree, dec=member_galaxies[:, 1] * u.degree) # array of coordinates pairs_idx = np.asarray( list((i, j) for ((i, _), (j, _)) in itertools.combinations( enumerate(c), 2))) # index of galaxies in combinations photoz_pairs = np.take( z_arr, pairs_idx) # photoz values of galaxies from the indices d_A = cosmo.angular_diameter_distance( z=average_redshift) # angular diameter distance pairs = c[pairs_idx] projected_sep = pairs[:, 0].separation( pairs[:, 1]) # projected separation (in deg) actual_sep = (projected_sep * d_A).to( u.Mpc, u.dimensionless_angles()) # convert projected separation to Mpc actual_sep = ((actual_sep * ((cosmo.H0 / 100).value)) / u.littleh).to( u.Mpc / u.littleh) # include littleh scaling total_separation = sum(1 / actual_sep) cluster_radius = projected_radius(cluster_size, total_separation) mass = cluster_mass(cluster_vel_disp, cluster_radius) mass_err = mass_error(mass, cluster_vel_disp, combined_disp_err) return ( mass, mass_err, cluster_vel_disp, combined_disp_err, cluster_radius, ) # M_sun/littleh, km^2/s^2, Mpc/littleh
def ELL1_T0(self): return self.TASC + self.pb() / (2 * np.pi) * (np.arctan( self.eps1() / self.eps2())).to( u.Unit(""), equivalencies=u.dimensionless_angles())
def create_dust_grids(self): """ This function ... :return: """ # Inform the user log.info("Creating the grids ...") # Calculate the major radius of the truncation ellipse in physical coordinates (pc) semimajor_angular = self.truncation_ellipse.semimajor # semimajor axis length of the sky ellipse radius_physical = (semimajor_angular * self.galaxy_distance).to( "pc", equivalencies=dimensionless_angles()) # Get the pixelscale in physical units pixelscale_angular = self.definition.basic_maps_minimum_average_pixelscale.to( "deg") #pixelscale_angular = self.reference_wcs.average_pixelscale.to("deg") # in deg pixelscale = (pixelscale_angular * self.galaxy_distance).to( "pc", equivalencies=dimensionless_angles()) # BINTREE: (smallest_cell_pixels, min_level, max_mass_fraction) # Low-resolution: 10., 6, 1e-5 # High-resolution: 0.5, 9, 0.5e-6 # OCTTREE: # Low-resolution: 10., 2, 1e-5 # High-resolution: 0.5, 3, 0.5e-6 # Because we (currently) can't position the grid exactly as the 2D pixels (rotation etc.), # take half of the pixel size to avoid too much interpolation min_scale = self.config.dg.scale_range.min * pixelscale max_scale = self.config.dg.scale_range.max * pixelscale scale_range = QuantityRange(min_scale, max_scale, invert=True) # The range of the max mass fraction mass_fraction_range = RealRange(self.config.dg.mass_fraction_range.min, self.config.dg.mass_fraction_range.max, invert=True) # must be inverted # Set fixed grid properties self.dg_generator.grid_type = self.config.dg.grid_type # set grid type self.dg_generator.x_radius = radius_physical self.dg_generator.y_radius = radius_physical self.dg_generator.z_radius = self.definition.dust_scaleheight * self.config.dg.scale_heights # Set options self.dg_generator.show = False self.dg_generator.write = False # Set the range of the minimum tree level if self.config.dg.grid_type == "bintree": level_range = self.config.dg.bintree_level_range # 6 to 9 elif self.config.dg.grid_type == "octtree": level_range = self.config.dg.octtree_level_range # 2 to 3 else: level_range = None # Generate the dust grids self.dg_generator.run(scale_range=scale_range, level_range=level_range, mass_fraction_range=mass_fraction_range, ngrids=self.config.nrepresentations)
vmax=vmax, norm=ImageNormalize(stretch=stretch)) (x1, y1), (x2, y2) = mywcs.wcs_world2pix(coord_limits, 0) ax.set_xlim(x1, x2) ax.set_ylim(y1, y2) ra = ax.coords['ra'] ra.set_major_formatter('hh:mm:ss.s') ra.set_ticks(spacing=spacing * u.deg) dec = ax.coords['dec'] ra.set_axislabel("RA (J2000)") dec.set_axislabel("Dec (J2000)") cb = fig.colorbar(im) cb.set_label("mJy/beam") sblength_deg = (sblength / distance).to(u.degree, u.dimensionless_angles()) sblength_pix = sblength_deg.value / pixscale if name in scalebarpos and scalebarpos[name] == 'right': ax.plot([ x2 - np.abs(x2 - x1) * 0.05, x2 - np.abs(x2 - x1) * 0.05 - sblength_pix ], [y1 + np.abs(y2 - y1) * 0.05] * 2, linewidth=3, color='black') ax.text(np.mean([ x2 - np.abs(x2 - x1) * 0.05, x2 - np.abs(x2 - x1) * 0.05 - sblength_pix ]), y1 + np.abs(y2 - y1) * 0.10, horizontalalignment='center', s=str(sblength))
Theta0 = 220 * u.km / u.s # We will assume a flat rotation curve: not the best but probably OK b = m.coords_as_GAL().b l = m.coords_as_GAL().l beta = (d / R0) * np.cos(b) - np.cos(l) # Nice & Taylor (1995), Eqn. 5 # https://ui.adsabs.harvard.edu/abs/1995ApJ...441..429N/abstract a_dot_n = ( -np.cos(b) * (Theta0 ** 2 / R0) * (np.cos(l) + beta / (np.sin(l) ** 2 + beta ** 2)) ) # Galactic acceleration contribution to PBDOT PBDOT_gal = (fit.model.PB.quantity * a_dot_n / c.c).decompose() # Shklovskii contribution PBDOT_shk = (fit.model.PB.quantity * pint.utils.pmtot(m) ** 2 * d / c.c).to( u.s / u.s, equivalencies=u.dimensionless_angles() ) # the uncertainty from the Galactic acceleration isn't included # but it's much smaller than the Shklovskii term so we'll ignore it PBDOT_err = (fit.model.PB.quantity * pint.utils.pmtot(m) ** 2 * d_err / c.c).to( u.s / u.s, equivalencies=u.dimensionless_angles() ) print(f"PBDOT_gal = {PBDOT_gal:.2e}, PBDOT_shk = {PBDOT_shk:.2e} +/- {PBDOT_err:.2e}") # %% # make a dense grid of Mp,Mc values to compute all of the PK parameters mp = np.linspace(1, 2, 500) * u.Msun mc = np.linspace(1, 2, 400) * u.Msun Mp, Mc = np.meshgrid(mp, mc) omdot_pred = pint.derived_quantities.omdot( Mp, Mc, fit.model.PB.quantity, fit.model.ECC.quantity
__all__ = [ 'EarthLocation', 'BaseGeodeticRepresentation', 'WGS84GeodeticRepresentation', 'WGS72GeodeticRepresentation', 'GRS80GeodeticRepresentation' ] GeodeticLocation = collections.namedtuple('GeodeticLocation', ['lon', 'lat', 'height']) ELLIPSOIDS = {} """Available ellipsoids (defined in erfam.h, with numbers exposed in erfa).""" # Note: they get filled by the creation of the geodetic classes. OMEGA_EARTH = ((1.002_737_811_911_354_48 * u.cycle / u.day).to( 1 / u.s, u.dimensionless_angles())) """ Rotational velocity of Earth, following SOFA's pvtob. In UT1 seconds, this would be 2 pi / (24 * 3600), but we need the value in SI seconds, so multiply by the ratio of stellar to solar day. See Explanatory Supplement to the Astronomical Almanac, ed. P. Kenneth Seidelmann (1992), University Science Books. The constant is the conventional, exact one (IERS conventions 2003); see http://hpiers.obspm.fr/eop-pc/index.php?index=constants. """ def _check_ellipsoid(ellipsoid=None, default='WGS84'): if ellipsoid is None: ellipsoid = default
############################################################################## # We can then transform to this frame instead, with our custom parameters: gc2 = c1.transform_to(gc_frame) print(gc2.v_x, gc2.v_y, gc2.v_z) ############################################################################## # It's sometimes useful to specify the solar motion using the `proper motion # of Sgr A* <https://arxiv.org/abs/astro-ph/0408107>`_ instead of Cartesian # velocity components. With an assumed distance, we can convert proper motion # components to Cartesian velocity components using `astropy.units`: galcen_distance = 8*u.kpc pm_gal_sgrA = [-6.379, -0.202] * u.mas/u.yr # from Reid & Brunthaler 2004 vy, vz = -(galcen_distance * pm_gal_sgrA).to(u.km/u.s, u.dimensionless_angles()) ############################################################################## # We still have to assume a line-of-sight velocity for the Galactic center, # which we will again take to be 11 km/s: vx = 11.1 * u.km/u.s gc_frame2 = coord.Galactocentric(galcen_distance=galcen_distance, galcen_v_sun=coord.CartesianDifferential(vx, vy, vz), z_sun=0*u.pc) gc3 = c1.transform_to(gc_frame2) print(gc3.v_x, gc3.v_y, gc3.v_z) ############################################################################## # The transformations also work in the opposite direction. This can be useful # for transforming simulated or theoretical data to observable quantities. As
interpolation='nearest', origin='lower', norm=asinh_norm.AsinhNorm()) tr_fk5 = ax.get_transform("fk5") #(x1,y1),(x2,y2) = (1200,434),(2142,1743) # wrong (x1,y1),(x2,y2) = tr_fk5.transform_point([bottomleft.ra.deg, bottomleft.dec.deg]),tr_fk5.transform_point([topright.ra.deg, topright.dec.deg]) (x1, y1), (x2, y2) = (toplevel_wcs.wcs_world2pix( [[bottomleft.ra.deg, bottomleft.dec.deg]], 0)[0], toplevel_wcs.wcs_world2pix( [[topright.ra.deg, topright.dec.deg]], 0)[0]) make_scalebar( ax, scalebarpos, length=(0.5 * u.pc / distance).to(u.arcsec, u.dimensionless_angles()), color='k', label='0.5 pc', text_offset=1.0 * u.arcsec, ) if regionname in ('full', 'fullN'): markersize = 1 coredots = plotcores(ax, alpha=1, transform=ax.get_transform('fk5'), markerfacecolor='none', markersize=markersize, zorder=50)
def _physical_size(self): if not hasattr(self, "_distance"): raise AttributeError("No distance has not been given.") return (self._ang_size * self.distance).to( self.distance.unit, equivalencies=u.dimensionless_angles())
proj_coldens_padded = np.pad(proj_coldens, padder, mode='constant', constant_values=np.NaN) proj_coldens_padded[np.isnan(proj_coldens_padded)] = 0. im = ax.imshow(proj_coldens_padded, cmap=cmap, origin='lower', interpolation='nearest', norm=ImageNormalize(vmin=0.0, vmax=vmax_scale, stretch=AsinhStretch()),) ax.set_xticks([]) ax.set_yticks([]) length = (1000. * u.pc / fitinfo_dict[gal]['distance']).to(u.deg, u.dimensionless_angles()) length_pix = length.value / np.abs(hdu_coldens[0].header['CDELT2']) x_vals = [0.1 * proj_coldens_padded.shape[0]] * 2 y_val1 = 0.1 * proj_coldens_padded.shape[0] y_val2 = 0.1 * proj_coldens_padded.shape[0] + length_pix ax.plot(x_vals, [y_val1, y_val2], 'k', linewidth=2) ax.text(x_vals[0], 0.07 * proj_coldens_padded.shape[0], "1 kpc", color='k', va='top', ha='center') # Galaxy name if gal == 'M33' or gal == 'M31': y_val_gal = 0.9 * proj_coldens_padded.shape[0] else:
def prtl_der(self, y, x): """Find the partial derivatives in binary model pdy/pdx Parameters ---------- y : str Name of variable to be differentiated x : str Name of variable the derivative respect to Returns ------- np.array The derivatives pdy/pdx """ if y not in self.binary_params + self.inter_vars: errorMesg = y + " is not in binary parameter and variables list." raise ValueError(errorMesg) if x not in self.inter_vars + self.binary_params: errorMesg = x + " is not in binary parameters and variables list." raise ValueError(errorMesg) # derivative to itself if x == y: return np.longdouble(np.ones(len(self.tt0))) * u.Unit("") # Get the unit right yAttr = getattr(self, y) xAttr = getattr(self, x) U = [None, None] for i, attr in enumerate([yAttr, xAttr]): if hasattr(attr, "units"): # If attr is a PINT Parameter class type U[i] = attr.units elif hasattr(attr, "unit"): # If attr is a Quantity type U[i] = attr.unit elif hasattr(attr, "__call__"): # If attr is a method U[i] = attr().unit else: raise TypeError(type(attr) + "can not get unit") # U[i] = 1*U[i] # commonU = list(set(U[i].unit.bases).intersection([u.rad,u.deg])) # if commonU != []: # strU = U[i].unit.to_string() # for cu in commonU: # scu = cu.to_string() # strU = strU.replace(scu,'1') # U[i] = U[i].to(strU, equivalencies=u.dimensionless_angles()).unit yU = U[0] xU = U[1] # Call derivtive functions derU = yU / xU if hasattr(self, "d_" + y + "_d_" + x): dername = "d_" + y + "_d_" + x result = getattr(self, dername)() elif hasattr(self, "d_" + y + "_d_par"): dername = "d_" + y + "_d_par" result = getattr(self, dername)(x) else: result = np.longdouble(np.zeros(len(self.tt0))) if hasattr(result, "unit"): return result.to(derU, equivalencies=u.dimensionless_angles()) else: return result * derU
def simulate_bulge2d(self): """ :return: """ # Inform the user log.info("Creating ski file to simulate the bulge image ...") # Load the bulge ski file template bulge_template_path = fs.join(template_path, "bulge.ski") ski = SkiFile(bulge_template_path) # Set the number of photon packages ski.setpackages(self.config.bulge_packages) # Change the ski file parameters # component_id, index, radius, y_flattening=1, z_flattening=1 ski.set_stellar_component_sersic_geometry( 0, self.components["bulge"].index, self.components["bulge"].effective_radius, y_flattening=self.components["bulge"].axial_ratio) # Remove all existing instruments ski.remove_all_instruments() # Create the instrument distance = self.galaxy_properties.distance inclination = Angle(0.0, "deg") #inclination = 0.0 # doesn't matter, also works! (thanks to intelligent parse_quantity) azimuth = Angle(90., "deg") #position_angle = self.parameters.bulge.PA + Angle(90., "deg") # + 90° because we can only do y_flattening and not x_flattening position_angle = self.components["bulge"].position_angle pixels_x = self.wcs.xsize pixels_y = self.wcs.ysize pixel_center = self.galaxy_properties.center.to_pixel(self.wcs) center = Position(0.5 * pixels_x - pixel_center.x - 0.5, 0.5 * pixels_y - pixel_center.y - 0.5) center_x = center.x center_y = center.y center_x = (center_x * self.wcs.pixelscale.x.to("deg") * distance).to( "pc", equivalencies=dimensionless_angles()) center_y = (center_y * self.wcs.pixelscale.y.to("deg") * distance).to( "pc", equivalencies=dimensionless_angles()) field_x_angular = self.wcs.pixelscale.x.to("deg") * pixels_x field_y_angular = self.wcs.pixelscale.y.to("deg") * pixels_y field_x_physical = (field_x_angular * distance).to( "pc", equivalencies=dimensionless_angles()) field_y_physical = (field_y_angular * distance).to( "pc", equivalencies=dimensionless_angles()) fake = SimpleInstrument(distance=distance, inclination=inclination, azimuth=azimuth, position_angle=position_angle, field_x=field_x_physical, field_y=field_y_physical, pixels_x=pixels_x, pixels_y=pixels_y, center_x=center_x, center_y=center_y) # Add the instrument ski.add_instrument(instrument_name, fake) # Determine the path to the ski file ski_path = fs.join(self.images_bulge2d_path, "bulge.ski") # Save the ski file to the new path ski.saveto(ski_path) # Determine the path to the simulation output directory out_path = fs.create_directory_in(self.images_bulge2d_path, "out") # Inform the user log.info("Running the bulge 2D simulation ...") # Simulate the bulge image fluxdensity = self.components["bulge"].fluxdensity self.bulge2d_image = self.launcher.run(ski_path, out_path, self.wcs, fluxdensity, self.psf, progress_bar=True) # Check WCS if self.bulge2d_image.wcs != self.wcs: raise RuntimeError( "Something went wrong setting the coordinate system")
def get_p4(self): """ This function ... :return: """ #http://vizier.cfa.harvard.edu/viz-bin/VizieR?-source=J/ApJS/219/4 # J/ApJS/219/4: S4G pipeline 4: multi-component decompositions (Salo+, 2015) # - J/ApJS/219/4/galaxies: Parameters of the galaxies; center, outer orientation, sky background; and 1-component Sersic fits (tables 1 and 6) (2352 rows) # - J/ApJS/219/4/table7: *Parameters of final multicomponent decompositions (Note) (4629 rows) # Inform the user log.info("Querying the S4G pipeline 4 catalog ...") # Get the "galaxies" table result = self.vizier.query_object(self.config.galaxy_name, catalog=["J/ApJS/219/4/galaxies"]) table = result[0] # PA: [0.2/180] Outer isophote position angle # e_PA: [0/63] Standard deviation in PA # Ell: [0.008/1] Outer isophote ellipticity # e_Ell: [0/0.3] Standard deviation in Ell pa = Angle(table["PA"][0] - 90., "deg") pa_error = Angle(table["e_PA"][0], "deg") ellipticity = table["Ell"][0] ellipticity_error = table["e_Ell"][0] # Get the "table7" table result = self.vizier.get_catalogs("J/ApJS/219/4/table7") table = result[0] # Name: Galaxy name # Mod: Type of final decomposition model # Nc: [1/4] Number of components in the model (1-4) # Q: [3/5] Quality flag, 5=most reliable # C: Physical interpretation of the component # Fn: The GALFIT function used for the component (sersic, edgedisk, expdisk, ferrer2 or psf) # f1: [0.006/1] "sersic" fraction of the total model flux # mag1: [7/19.4] "sersic" total 3.6um AB magnitude # q1: [0.1/1] "sersic" axis ratio # PA1: [0.08/180] "sersic" position angle [deg] # Re: [0.004/430] "sersic" effective radius (Re) [arcsec] # n: [0.01/20] "sersic" parameter n # f2: [0.02/1] "edgedisk" fraction of the total model flux # mu02: [11.8/24.6] "edgedisk" central surface face-on brightness (µ0) [mag/arcsec2] # PA2: [-90/90] "edgedisk" PA [deg] # hr2: [1/153] "edgedisk" exponential scale length (hr) [arcsec] # hz2: [0.003/39] "edgedisk" z-scale hz [arcsec] # f3: [0.02/1] "expdisk" fraction of the total model flux # mag3: [6.5/18.1] "expdisk" total 3.6um AB magnitude [mag] # q3: [0.1/1] "expdisk" axis ratio # PA3: [-90/90] "expdisk" position angle [deg] # hr3: [0.7/332] "expdisk" exponential scale length (hr) [arcsec] # mu03: [16.4/25.3] "expdisk" central surface face-on brightness (µ0) [mag/arcsec2] # f4: [0.003/0.6] "ferrer2" fraction of the total model flux # mu04: [16/24.8] "ferrer2" central surface sky brightness (µ0) [mag/arcsec2] # q4: [0.01/1] "ferrer2" axis ratio # PA4: [-90/90] "ferrer2" position angle [deg] # Rbar: [3.7/232.5] "ferrer2" outer truncation radius of the bar (Rbar) [arcsec] # f5: [0.001/0.4] "psf" fraction of the total model flux # mag5: [11.5/21.1] "psf" total 3.6um AB magnitude [mag] indices = tables.find_indices(table, self.ngc_name_nospaces, "Name") labels = { "sersic": 1, "edgedisk": 2, "expdisk": 3, "ferrer2": 4, "psf": 5 } #units = {"f": None, "mag": "mag", "q": None, "PA": "deg", } # Loop over the indices for index in indices: model_type = table["Mod"][index] number_of_components = table["Nc"][index] quality = table["Q"][index] interpretation = table["C"][index] functionname = table["Fn"][index] component_parameters = Map() if self.parameters.model_type is not None: assert model_type == self.parameters.model_type if self.parameters.number_of_components is not None: assert number_of_components == self.parameters.number_of_components if self.parameters.quality is not None: assert quality == self.parameters.quality self.parameters.model_type = model_type self.parameters.number_of_components = number_of_components self.parameters.quality = quality for key in table.colnames: if not key.endswith(str(labels[functionname])): continue parameter = key[:-1] value = table[key][index] if parameter == "PA": value = Angle(value + 90., "deg") if quadrant(value) == 2: value = value - Angle(180., "deg") elif quadrant(value) == 3: value = value + Angle(180., "deg") if value.to("deg").value > 180.: value = value - Angle(360., "deg") elif value.to("deg").value < -180.: value = value + Angle(360., "deg") elif parameter == "mag": parameter = "fluxdensity" value = unitconversion.ab_to_jansky(value) * u("Jy") elif parameter == "mu0": value = value * u("mag/arcsec2") elif parameter == "hr": value = value * u("arcsec") value = (self.parameters.distance * value).to( "pc", equivalencies=dimensionless_angles()) elif parameter == "hz": value = value * u("arcsec") value = (self.parameters.distance * value).to( "pc", equivalencies=dimensionless_angles()) component_parameters[parameter] = value if functionname == "sersic": re = table["Re"][index] * u("arcsec") component_parameters["Re"] = ( self.parameters.distance * re).to( "pc", equivalencies=dimensionless_angles()) component_parameters["n"] = table["n"][index] elif functionname == "ferrer2": rbar = table["Rbar"][index] * u("arcsec") component_parameters["Rbar"] = ( self.parameters.distance * rbar).to( "pc", equivalencies=dimensionless_angles()) if interpretation == "B": # bulge self.parameters.bulge = component_parameters elif interpretation == "D": # disk self.parameters.disk = component_parameters else: raise RuntimeError("Unrecognized component: " + interpretation)
output=rgb_cube_png, vmax_g=0.017, vmax_b=6.5, vmax_r=7.0, vmin_g=0.0001, stretch_g='arcsinh', embed_avm_tags=True) pl.rcParams['font.size'] = 18 fig1 = pl.figure(1) fig1.clf() F = aplpy.FITSFigure(rgb_cube_png, figure=fig1) F.show_rgb(rgb_cube_png) F.recenter(290.93315, 14.509584, radius=0.0005) F.add_scalebar((0.025 * u.pc / (5400 * u.pc)).to(u.deg, u.dimensionless_angles())) F.scalebar.set_label('5000 au / 0.025 pc') F.scalebar.set_color('w') F.save(paths.fpath("W51e2_cycle3green_outflows_aplpy.png")) F.save(paths.fpath("W51e2_cycle3green_outflows_aplpy.pdf")) e8_rgb_cube_png = rgb_cube_fits[:-5] + "_asinhgreen.png" rgb_im = aplpy.make_rgb_image(data=rgb_cube_fits, output=e8_rgb_cube_png, vmax_g=0.017, vmin_g=-0.0001, vmax_b=1.5, vmin_b=-0.05, vmax_r=4.0, vmin_r=-0.05, stretch_g='arcsinh',