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 setup_class(self): """set up some initial values for tests""" u.set_enabled_equivalencies(u.temperature_energy()) self.T_e = 1000 * u.eV self.n_e = 2e13 / u.cm**3 self.ion_particle = 'D +1' self.m_i = particle_mass(self.ion_particle) self.Z = integer_charge(self.ion_particle) self.T_i = self.T_e self.n_i = self.n_e / self.Z self.B = 0.01 * u.T self.coulomb_log_val_ei = 17 self.coulomb_log_val_ii = 17 self.hall_e = None self.hall_i = None self.V_ei = None self.V_ii = None self.mu = m_e / self.m_i self.theta = self.T_e / self.T_i self.model = 'Braginskii' self.field_orientation = 'all' with pytest.warns(RelativityWarning): self.ct = ClassicalTransport( T_e=self.T_e, n_e=self.n_e, T_i=self.T_i, n_i=self.n_i, ion_particle=self.ion_particle, Z=self.Z, B=self.B, model=self.model, field_orientation=self.field_orientation, coulomb_log_ei=self.coulomb_log_val_ei, coulomb_log_ii=self.coulomb_log_val_ii, V_ei=self.V_ei, V_ii=self.V_ii, hall_e=self.hall_e, hall_i=self.hall_i, mu=self.mu, theta=self.theta, ) self.ct_wrapper = ClassicalTransport( T_e=self.T_e, n_e=self.n_e, T_i=self.T_i, n_i=self.n_i, ion_particle=self.ion_particle, Z=self.Z, B=self.B, model=self.model, field_orientation=self.field_orientation, mu=self.mu, theta=self.theta, ) self.all_variables = self.ct.all_variables()
def mean_motion(orbit, tof, **kwargs): r"""Propagates orbit using mean motion .. versionadded:: 0.9.0 Parameters ---------- orbit : ~poliastro.twobody.orbit.Orbit the Orbit object to propagate. tof : float Time of flight (s). Notes ----- This method takes initial :math:`\vec{r}, \vec{v}`, calculates classical orbit parameters, increases mean anomaly and performs inverse transformation to get final :math:`\vec{r}, \vec{v}` The logic is based on formulae (4), (6) and (7) from http://dx.doi.org/10.1007/s10569-013-9476-9 """ k = orbit.attractor.k.to(u.km**3 / u.s**2).value r0 = orbit.r.to(u.km).value v0 = orbit.v.to(u.km / u.s).value # get the initial true anomaly and orbit parameters that are constant over time p, ecc, inc, raan, argp, nu0 = rv2coe(k, r0, v0) # get the initial mean anomaly M0 = nu_to_M(nu0, ecc) # elliptic or hyperbolic orbits if not np.isclose(ecc, 1.0, rtol=1e-06): a = p / (1.0 - ecc**2) # given the initial mean anomaly, calculate mean anomaly # at the end, mean motion (n) equals sqrt(mu / |a^3|) with u.set_enabled_equivalencies(u.dimensionless_angles()): M = M0 + tof * np.sqrt(k / np.abs(a**3)) * u.rad nu = M_to_nu(M, ecc) # parabolic orbit else: q = p / 2.0 # mean motion n = sqrt(mu / 2 q^3) for parabolic orbit with u.set_enabled_equivalencies(u.dimensionless_angles()): M = M0 + tof * np.sqrt(k / (2.0 * q**3)) # using Barker's equation, which is solved analytically # for parabolic orbit, get true anomaly B = 3.0 * M / 2.0 A = (B + np.sqrt(1.0 + B**2))**(2.0 / 3.0) D = 2.0 * A * B / (1.0 + A + A**2) nu = 2.0 * np.arctan(D) with u.set_enabled_equivalencies(u.dimensionless_angles()): return coe2rv(k, p, ecc, inc, raan, argp, nu)
def mean_motion(k, r0, v0, tof, **kwargs): r"""Propagates orbit using mean motion Parameters ---------- k : float Gravitational constant of main attractor (km^3 / s^2). r0 : array Initial position (km). v0 : array Initial velocity (km). ad : function(t0, u, k), optional Non Keplerian acceleration (km/s2), default to None. tof : float Time of flight (s). Notes ----- This method takes initial :math:`\vec{r}, \vec{v}`, calculates classical orbit parameters, increases mean anomaly and performs inverse transformation to get final :math:`\vec{r}, \vec{v}` The logic is based on formulae (4), (6) and (7) from http://dx.doi.org/10.1007/s10569-013-9476-9 """ # get the initial true anomaly and orbit parameters that are constant over time p, ecc, inc, raan, argp, nu0 = rv2coe(k, r0, v0) # get the initial mean anomaly M0 = nu_to_M(nu0, ecc) # elliptic or hyperbolic orbits if not np.isclose(ecc, 1.0, rtol=1e-06): a = p / (1.0 - ecc**2) # given the initial mean anomaly, calculate mean anomaly # at the end, mean motion (n) equals sqrt(mu / |a^3|) with u.set_enabled_equivalencies(u.dimensionless_angles()): M = M0 + tof * np.sqrt(k / np.abs(a**3)) * u.rad nu = M_to_nu(M, ecc) # parabolic orbit else: q = p / 2.0 # mean motion n = sqrt(mu / 2 q^3) for parabolic orbit with u.set_enabled_equivalencies(u.dimensionless_angles()): M = M0 + tof * np.sqrt(k / (2.0 * q**3)) # using Barker's equation, which is solved analytically # for parabolic orbit, get true anomaly B = 3.0 * M / 2.0 A = (B + np.sqrt(1.0 + B**2))**(2.0 / 3.0) D = 2.0 * A * B / (1.0 + A + A**2) nu = 2.0 * np.arctan(D) with u.set_enabled_equivalencies(u.dimensionless_angles()): return coe2rv(k, p, ecc, inc, raan, argp, nu)
def _convert(quantity, dispersion_unit, dispersion, flux_unit): """ Convert the quantity to the spectrum's units, and then we will use the *value* of it in the new unitless-model. """ with u.set_enabled_equivalencies(u.spectral()): if quantity.unit.is_equivalent(dispersion_unit): quantity = quantity.to(dispersion_unit) with u.set_enabled_equivalencies(u.spectral_density(dispersion)): if quantity.unit.is_equivalent(flux_unit): quantity = quantity.to(flux_unit) return quantity
def world_to_pixel(self, world_array): """ Method for performing the world to pixel transformations. """ with u.set_enabled_equivalencies(u.spectral()): world_array = u.Quantity(world_array, unit=self.spectral_axis_unit) return self.axes.spectral.all_world2pix(world_array.value, 0)[0]
def d_spindown_phase_d_delay(self, toas, delay): dt_tzrmjd, dt_pepoch = self.get_dt(toas, delay) fterms = [0.0] + self.get_spin_terms() with u.set_enabled_equivalencies(dimensionless_cycles): d_ptzrmjd_d_delay = taylor_horner_deriv((dt_tzrmjd-dt_pepoch).to( \ u.second), fterms) return -d_ptzrmjd_d_delay.to(u.cycle / u.second)
def test_kkiternanfailure(self): """ Make sure that KK iteration stops instantly when it starts producing NaNs. Producing NaNs means that the iteration has been given input parameters which fail to converge to anything sane. """ testspec = helpers.generate_absspectrum() assert testspec.x.unit == u.kayser assert testspec.y.unit == utils.unit_od testspec.subspectrum(2200., 3900.) freq = testspec.x transmittance = testspec.y.to( utils.unit_transmittance, equivalencies=utils.equivalencies_absorption) m_substrate = 1.74+0.0j # CsI window, like in the Hudgins paper d_ice = 0.5*u.micron m0 = 1.3 + 0.0j with u.set_enabled_equivalencies(u.equivalencies.spectral()): freq_m0 = (250.*u.micron).to(u.kayser).value with pytest.raises(utils.KKError): with warnings.catch_warnings(): warnings.simplefilter("ignore") utils.kramers_kronig( freq, transmittance, m_substrate, d_ice, m0, freq_m0)
def d_phase_d_toa(self, toas, sample_step=None): """Return the derivative of phase wrt TOA Parameter --------- toas : PINT TOAs class The toas when the derivative of phase will be evaluated at. sample_step : float optional Finite difference steps. If not specified, it will take 1/10 of the spin period. """ copy_toas = copy.deepcopy(toas) if sample_step is None: pulse_period = 1.0 / (self.F0.quantity) sample_step = pulse_period * 1000 sample_dt = [-sample_step, 2 * sample_step] sample_phase = [] for dt in sample_dt: dt_array = ([dt.value] * copy_toas.ntoas*dt._unit) deltaT = time.TimeDelta(dt_array) copy_toas.adjust_TOAs(deltaT) phase = self.phase(copy_toas.table) sample_phase.append(phase) #Use finite difference method. # phase'(t) = (phase(t+h)-phase(t-h))/2+ 1/6*F2*h^2 + .. # The error should be near 1/6*F2*h^2 dp = (sample_phase[1] - sample_phase[0]) d_phase_d_toa = dp.int / (2*sample_step) + dp.frac / (2*sample_step) del copy_toas with u.set_enabled_equivalencies(dimensionless_cycles): return d_phase_d_toa.to(u.Hz)
def test_raise_to_power(self, power): """Check that raising LogQuantities to some power is only possible when the physical unit is dimensionless, and that conversion is turned off when the resulting logarithmic unit (say, mag**2) is incompatible.""" lq = u.Magnitude(np.arange(1., 4.)*u.Jy) if power == 0: assert np.all(lq ** power == 1.) elif power == 1: assert np.all(lq ** power == lq) else: with pytest.raises(u.UnitsError): lq ** power # with dimensionless, it works, but falls back to normal quantity # (except for power=1) lq2 = u.Magnitude(np.arange(10.)) t = lq2**power if power == 0: assert t.unit is u.dimensionless_unscaled assert np.all(t.value == 1.) elif power == 1: assert np.all(t == lq2) else: assert not isinstance(t, type(lq2)) assert t.unit == lq2.unit.function_unit ** power with u.set_enabled_equivalencies(u.logarithmic()): with pytest.raises(u.UnitsError): t.to(u.dimensionless_unscaled)
def test_raise_to_power(self, power): """Check that raising LogUnits to some power is only possible when the physical unit is dimensionless, and that conversion is turned off when the resulting logarithmic unit (such as mag**2) is incompatible.""" lu1 = u.mag(u.Jy) if power == 0: assert lu1 ** power == u.dimensionless_unscaled elif power == 1: assert lu1 ** power == lu1 else: with pytest.raises(u.UnitsError): lu1 ** power # With dimensionless, though, it works, but returns a normal unit. lu2 = u.mag(u.dimensionless_unscaled) t = lu2**power if power == 0: assert t == u.dimensionless_unscaled elif power == 1: assert t == lu2 else: assert not isinstance(t, type(lu2)) assert t == lu2.function_unit**power # also check we roundtrip t2 = t**(1./power) assert t2 == lu2.function_unit with u.set_enabled_equivalencies(u.logarithmic()): assert_allclose(t2.to(u.dimensionless_unscaled, np.arange(3.)), lu2.to(lu2.physical_unit, np.arange(3.)))
def glitch_phase(self, toas, delay): """Glitch phase function. delay is the time delay from the TOA to time of pulse emission at the pulsar, in seconds. returns an array of phases in long double """ tbl = toas.table phs = numpy.zeros_like(tbl, dtype=numpy.longdouble) * u.cycle glepnames = [x for x in self.params if x.startswith('GLEP_')] with u.set_enabled_equivalencies(dimensionless_cycles): for glepnm in glepnames: glep = getattr(self, glepnm) eph = glep.value idx = glep.index dphs = getattr(self, "GLPH_%d" % idx).quantity dF0 = getattr(self, "GLF0_%d" % idx).quantity dF1 = getattr(self, "GLF1_%d" % idx).quantity dF2 = getattr(self, "GLF2_%d" % idx).quantity dt = (tbl['tdbld'] - eph) * u.day - delay dt = dt.to(u.second) affected = dt > 0.0 # TOAs affected by glitch # decay term dF0D = getattr(self, "GLF0D_%d" % idx).quantity if dF0D != 0.0: tau = getattr(self, "GLTD_%d" % idx).quantity decayterm = dF0D * tau * (1.0 - numpy.exp(- (dt[affected] / tau).to(u.Unit("")))) else: decayterm = 0.0 phs[affected] += dphs + dt[affected] * \ (dF0 + 0.5 * dt[affected] * dF1 + \ 1./6. * dt[affected]*dt[affected] * dF2) + decayterm return phs.to(u.cycle)
def d_nu_d_T0(self): """dnu/dT0 = dnu/de*de/dT0+dnu/dE*dE/dT0 de/dT0 = -EDOT """ with u.set_enabled_equivalencies(u.dimensionless_angles()): return self.d_nu_d_ecc() * ( -self.EDOT) + self.d_nu_d_E() * self.d_E_d_T0()
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 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_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_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_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 d_delayS_d_par(self, par): if set(self.fit_params) == set(["H3", "H4"]): stigma = self.H4 / self.H3 elif set(self.fit_params) == set(["H3", "STIGMA"]): stigma = self.STIGMA elif set(self.fit_params) == set(["H3"]): stigma = 0.0 else: raise NotImplementedError("ELL1H did not implemented %s parameter" " set yet." % str(self.fit_params)) d_ds_func_name_base = "d_" + self.ds_func.__name__ + "_d_" d_delayS_d_H3_func = getattr(self, d_ds_func_name_base + "H3") d_delayS_d_Phi_func = getattr(self, d_ds_func_name_base + "Phi") d_delayS_d_STIGMA_func = getattr(self, d_ds_func_name_base + "STIGMA") d_delayS_d_H3 = d_delayS_d_H3_func(self.H3, stigma, self.NHARMS) d_delayS_d_Phi = d_delayS_d_Phi_func(self.H3, stigma, self.NHARMS) d_delayS_d_STIGMA = d_delayS_d_STIGMA_func(self.H3, stigma, self.NHARMS) d_H3_d_par = self.prtl_der("H3", par) d_Phi_d_par = self.prtl_der("Phi", par) d_STIGMA_d_par = self.prtl_der("STIGMA", par) with u.set_enabled_equivalencies(u.dimensionless_angles()): d_delayS_d_par = (d_delayS_d_H3 * d_H3_d_par + d_delayS_d_Phi * d_Phi_d_par + d_delayS_d_STIGMA * d_STIGMA_d_par) return d_delayS_d_par
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 __init__(self, wn, m, **kwargs): """ CDESpectrum(wn,m,**kwargs) Constructor for the `CDESpectrum` class. Parameters ---------- wn : `astropy.units.Quantity` or `numpy.ndarray` The absorption spectrum frequency data. If given as `astropy.units.Quantity`, they must either be in kayser (reciprocal wavenumbers) or convertable to kayser. If given as `numpy.ndarray`, they are assumed to be in kayser. m : `numpy.ndarray` The complex refractive index spectrum of the data. **kwargs : Arguments, optional Additional initialisation arguments can be passed to `AbsorptionSpectrum` using this. Note that x and y are defined using the other initialisation parameters of `CDESpectrum`. """ if len(wn) != len(m): raise RuntimeError("Input arrays have different sizes.") if type(wn) != u.quantity.Quantity: wn = wn * u.kayser if wn.unit != u.kayser: with u.set_enabled_equivalencies(u.equivalencies.spectral()): wn = wn.to(u.kayser) self.cabs, self.cabs_vol, self.cscat_vol, self.ctot = utils.cde_correct(wn.value, m) self.m = np.array(m, dtype="float64") od = ( self.cabs_vol * utils.unit_od ) # utils.unit_absorbance).to(utils.unit_od,equivalencies=utils.equivalencies_absorption) AbsorptionSpectrum.__init__(self, wn, od, **kwargs)
def d_spindown_phase_d_delay(self, toas, delay): dt_tzrmjd, dt_pepoch = self.get_dt(toas, delay) fterms = [0.0] + self.get_spin_terms() with u.set_enabled_equivalencies(dimensionless_cycles): d_ptzrmjd_d_delay = taylor_horner_deriv((dt_tzrmjd-dt_pepoch).to( \ u.second), fterms) return -d_ptzrmjd_d_delay.to(u.cycle/u.second)
def glitch_phase(self, toas, delay): """Glitch phase function. delay is the time delay from the TOA to time of pulse emission at the pulsar, in seconds. returns an array of phases in long double """ tbl = toas.table phs = np.zeros_like(tbl, dtype=np.longdouble) * u.cycle glepnames = [x for x in self.params if x.startswith('GLEP_')] with u.set_enabled_equivalencies(dimensionless_cycles): for glepnm in glepnames: glep = getattr(self, glepnm) eph = glep.value idx = glep.index dphs = getattr(self, "GLPH_%d" % idx).quantity dF0 = getattr(self, "GLF0_%d" % idx).quantity dF1 = getattr(self, "GLF1_%d" % idx).quantity dF2 = getattr(self, "GLF2_%d" % idx).quantity dt = (tbl['tdbld'] - eph) * u.day - delay dt = dt.to(u.second) affected = dt > 0.0 # TOAs affected by glitch # decay term dF0D = getattr(self, "GLF0D_%d" % idx).quantity if dF0D != 0.0: tau = getattr(self, "GLTD_%d" % idx).quantity decayterm = dF0D * tau * ( 1.0 - np.exp(-(dt[affected] / tau).to(u.Unit("")))) else: decayterm = 0.0 phs[affected] += dphs + dt[affected] * \ (dF0 + 0.5 * dt[affected] * dF1 + \ 1./6. * dt[affected]*dt[affected] * dF2) + decayterm return phs.to(u.cycle)
def test_dimensionless_redshift(): """Test :func:`astropy.cosmology.units.dimensionless_redshift`.""" z = 3 * cu.redshift val = 3 * u.one # show units not equal assert z.unit == cu.redshift assert z.unit != u.one # test equivalency enabled by default assert z == val # also test that it works for powers assert (3 * cu.redshift ** 3) == val # and in composite units assert (3 * u.km / cu.redshift ** 3) == 3 * u.km # test it also works as an equivalency with u.set_enabled_equivalencies([]): # turn off default equivalencies assert z.to(u.one, equivalencies=cu.dimensionless_redshift()) == val with pytest.raises(ValueError): z.to(u.one) # if this fails, something is really wrong with u.add_enabled_equivalencies(cu.dimensionless_redshift()): assert z == val
def latexForWavelength(unitlike): un = unit(unitlike) with u.set_enabled_equivalencies([]): if un.is_equivalent(unit("m")): return r"$\lambda$" if un.is_equivalent(unit("Hz")): return r"$\nu$" if un.is_equivalent(unit("J")): return r"$E$" return ""
def d_delayS_d_par(self, par): """Derivative for bianry Shaprio delay. Computes:: delayS = -2 * TM2 * np.log(1 - self.SINI * np.sin(Phi)) d_delayS_d_par = d_delayS_d_TM2 * d_TM2_d_par + d_delayS_d_SINI*d_SINI_d_par + d_delayS_d_Phi * d_Phi_d_par """ TM2 = self.TM2() Phi = self.Phi() d_delayS_d_TM2 = -2 * np.log(1 - self.SINI * np.sin(Phi)) d_delayS_d_SINI = ( -2 * TM2 * 1.0 / (1 - self.SINI * np.sin(Phi)) * (-np.sin(Phi)) ) d_delayS_d_Phi = -2 * TM2 * 1.0 / (1 - self.SINI * np.sin(Phi)) * (-self.SINI) d_TM2_d_par = self.prtl_der("TM2", par) d_SINI_d_par = self.prtl_der("SINI", par) d_Phi_d_par = self.prtl_der("Phi", par) with u.set_enabled_equivalencies(u.dimensionless_angles()): d_delayS_d_par = ( d_delayS_d_TM2 * d_TM2_d_par + d_delayS_d_SINI * d_SINI_d_par + d_delayS_d_Phi * d_Phi_d_par ) return d_delayS_d_par
def test_raise_to_power(self, power): """Check that raising LogUnits to some power is only possible when the physical unit is dimensionless, and that conversion is turned off when the resulting logarithmic unit (such as mag**2) is incompatible.""" lu1 = u.mag(u.Jy) if power == 0: assert lu1**power == u.dimensionless_unscaled elif power == 1: assert lu1**power == lu1 else: with pytest.raises(u.UnitsError): lu1**power # With dimensionless, though, it works, but returns a normal unit. lu2 = u.mag(u.dimensionless_unscaled) t = lu2**power if power == 0: assert t == u.dimensionless_unscaled elif power == 1: assert t == lu2 else: assert not isinstance(t, type(lu2)) assert t == lu2.function_unit**power # also check we roundtrip t2 = t**(1. / power) assert t2 == lu2.function_unit with u.set_enabled_equivalencies(u.logarithmic()): assert_allclose(t2.to(u.dimensionless_unscaled, np.arange(3.)), lu2.to(lu2.physical_unit, np.arange(3.)))
def d_phase_d_toa(self, toas, sample_step=None): """Return the derivative of phase wrt TOA. Parameters ---------- toas : PINT TOAs class The toas when the derivative of phase will be evaluated at. sample_step : float optional Finite difference steps. If not specified, it will take 1/10 of the spin period. """ copy_toas = copy.deepcopy(toas) if sample_step is None: pulse_period = 1.0 / (self.F0.quantity) sample_step = pulse_period * 1000 sample_dt = [-sample_step, 2 * sample_step] sample_phase = [] for dt in sample_dt: dt_array = ([dt.value] * copy_toas.ntoas * dt._unit) deltaT = time.TimeDelta(dt_array) copy_toas.adjust_TOAs(deltaT) phase = self.phase(copy_toas) sample_phase.append(phase) #Use finite difference method. # phase'(t) = (phase(t+h)-phase(t-h))/2+ 1/6*F2*h^2 + .. # The error should be near 1/6*F2*h^2 dp = (sample_phase[1] - sample_phase[0]) d_phase_d_toa = dp.int / (2 * sample_step) + dp.frac / (2 * sample_step) del copy_toas with u.set_enabled_equivalencies(dimensionless_cycles): return d_phase_d_toa.to(u.Hz)
def isotropic_w0(N=100): # positions d = np.random.lognormal(mean=np.log(25), sigma=0.5, size=N) phi = np.random.uniform(0, 2 * np.pi, size=N) theta = np.arccos(np.random.uniform(size=N) - 0.5) vr = np.random.normal(150., 40., size=N) * u.km / u.s vt = np.random.normal(100., 40., size=N) vt = np.vstack((vt, np.zeros_like(vt))).T # rotate to be random position angle pa = np.random.uniform(0, 2 * np.pi, size=N) M = np.array([[np.cos(pa), -np.sin(pa)], [np.sin(pa), np.cos(pa)]]).T vt = np.array([vv.dot(MM) for (vv, MM) in zip(vt, M)]) * u.km / u.s vphi, vtheta = vt.T rep = coord.PhysicsSphericalRepresentation(r=d * u.dimensionless_unscaled, phi=phi * u.radian, theta=theta * u.radian) x = rep.represent_as(coord.CartesianRepresentation).xyz.T.value vr = vr.decompose(galactic).value * u.one vphi = vphi.decompose(galactic).value * u.one vtheta = vtheta.decompose(galactic).value * u.one vsph = coord.PhysicsSphericalDifferential(d_phi=vphi / (d * np.sin(theta)), d_theta=vtheta / d, d_r=vr) with u.set_enabled_equivalencies(u.dimensionless_angles()): v = vsph.represent_as(coord.CartesianDifferential, base=rep).d_xyz.value.T return np.hstack((x, v)).T
def d_Drepp_d_par(self, par): """Derivative. Computes:: Drepp = -alpha*cos(E)-(beta+GAMMA)*sin(E) dDrepp = -(beta+gamma)*cos(E)*dE - cos(E)*dalpha +alpha*sin(E)*dE - (dbeta+dgamma)*sin(E) dDrepp/dPar = -cos(E)*dalpha/dPar +(alpha*sin(E)-(beta+gamma)*cos(E))*dE/dPar -(dbeta/dPar+dgamma/dPar)*sin(E) """ sinE = np.sin(self.E()) cosE = np.cos(self.E()) Drepp = self.Drepp() par_obj = getattr(self, par) with u.set_enabled_equivalencies(u.dimensionless_angles()): # first term term1 = -cosE * self.prtl_der("alpha", par) # second term term2 = (self.alpha() * sinE - (self.beta() + self.GAMMA) * cosE) * self.prtl_der( "E", par) # Third term term3 = -sinE * (self.prtl_der("beta", par) + self.prtl_der("GAMMA", par)) return (term1 + term2 + term3).to(Drepp.unit / par_obj.unit)
def d_phase_d_jump(self, toas, jump_param, delay): jpar = getattr(self, jump_param) d_phase_d_j = numpy.zeros(len(toas)) mask = jpar.select_toa_mask(toas) d_phase_d_j[mask] = self.F0.value with u.set_enabled_equivalencies(dimensionless_cycles): return (d_phase_d_j * self.F0.units).to(u.cycle/u.second)
def __new__(cls, arg1, arg2=None): # Assume inputs are numerical, could add an extra # case to parse strings as input. # if it is not a list, convert to a list if not hasattr(arg1, 'unit'): arg1 = arg1 * u.cycle if arg1.shape == (): arg1 = arg1.reshape((1,)) with u.set_enabled_equivalencies(dimensionless_cycles): arg1 = arg1.to(u.Unit("")) # Since modf does not like dimensioned quantity if arg2 is None: ff,ii = numpy.modf(arg1) else: if not hasattr(arg2, 'unit'): arg2 = arg2 * u.cycle if arg2.shape == (): arg2 = arg2.reshape((1,)) arg2 = arg2.to(u.Unit("")) arg1S = numpy.modf(arg1) arg2S = numpy.modf(arg2) ii = arg1S[1]+arg2S[1] ff = arg2S[0] index = numpy.where(ff < -0.5) ff[index] += 1.0 ii[index] -= 1 index = numpy.where(ff > 0.5 ) ff[index] -= 1.0 ii[index] += 1 return super(Phase, cls).__new__(cls, ii.to(u.cycle), ff.to(u.cycle))
def __init__(self, wn, m, **kwargs): """ CDESpectrum(wn, m, **kwargs) Constructor for the `CDESpectrum` class. Parameters ---------- wn : `astropy.units.Quantity` or `numpy.ndarray` The absorption spectrum frequency data. If given as `astropy.units.Quantity`, they must either be in kayser (reciprocal wavenumbers) or convertable to kayser. If given as `numpy.ndarray`, they are assumed to be in kayser. m : `numpy.ndarray` The complex refractive index spectrum of the data. **kwargs : Arguments, optional Additional initialisation arguments can be passed to `AbsorptionSpectrum` using this. Note that x and y are defined using the other initialisation parameters of `CDESpectrum`. """ if len(wn) != len(m): raise RuntimeError('Input arrays have different sizes.') if type(wn) != u.quantity.Quantity: wn = wn * u.kayser if wn.unit != u.kayser: with u.set_enabled_equivalencies(u.equivalencies.spectral()): wn = wn.to(u.kayser) self.cabs, self.cabs_vol, self.cscat_vol, self.ctot = \ utils.cde_correct(wn.value, m) self.m = np.array(m, dtype=complex) od = self.cabs_vol * utils.unit_od AbsorptionSpectrum.__init__(self, wn, od, **kwargs)
def test_raise_to_power(self, power): """Check that raising LogQuantities to some power is only possible when the physical unit is dimensionless, and that conversion is turned off when the resulting logarithmic unit (say, mag**2) is incompatible.""" lq = u.Magnitude(np.arange(1., 4.) * u.Jy) if power == 0: assert np.all(lq**power == 1.) elif power == 1: assert np.all(lq**power == lq) else: with pytest.raises(u.UnitsError): lq**power # with dimensionless, it works, but falls back to normal quantity # (except for power=1) lq2 = u.Magnitude(np.arange(10.)) t = lq2**power if power == 0: assert t.unit is u.dimensionless_unscaled assert np.all(t.value == 1.) elif power == 1: assert np.all(t == lq2) else: assert not isinstance(t, type(lq2)) assert t.unit == lq2.unit.function_unit**power with u.set_enabled_equivalencies(u.logarithmic()): with pytest.raises(u.UnitsError): t.to(u.dimensionless_unscaled)
def d_phase_d_GLTD(self, toas, param, delay): """Calculate the derivative wrt GLF0D """ tbl = toas.table p, ids, idv = split_prefixed_name(param) if p != "GLTD_": raise ValueError( "Can not calculate d_phase_d_GLF0D with respect to %s." % param ) eph = np.longdouble(getattr(self, "GLEP_" + ids).value) par_GLTD = getattr(self, param) if par_GLTD.value == 0.0: return np.zeros(len(tbl), dtype=np.longdouble) * u.cycle / par_GLTD.units glf0d = getattr(self, "GLF0D_" + ids).quantity tau = par_GLTD.quantity dt = (tbl["tdbld"] - eph) * u.day - delay dt = dt.to(u.second) affected = np.where(dt > 0.0)[0] dpdGLTD = np.zeros(len(tbl), dtype=np.longdouble) * u.cycle / par_GLTD.units with u.set_enabled_equivalencies(dimensionless_cycles): dpdGLTD[affected] += glf0d * ( np.longdouble(1.0) - np.exp(-dt[affected] / tau) ) + glf0d * tau * (-np.exp(-dt[affected] / tau)) * dt[affected] / ( tau * tau ) return dpdGLTD
def shklovskii_factor(pmtot: u.mas / u.yr, D: u.kpc): """Compute magnitude of Shklovskii correction factor. Computes the Shklovskii correction factor, as defined in Eq 8.12 of Lorimer & Kramer (2005) [10]_ This is the factor by which :math:`\dot P /P` is increased due to the transverse velocity. Note that this affects both the measured spin period and the orbital period. If we call this Shklovskii acceleration :math:`a_s`, then .. math:: \dot P_{\\rm intrinsic} = \dot P_{\\rm observed} - a_s P Parameters ---------- pmtot : astropy.units.Quantity typically units of u.mas/u.yr Total proper motion of the pulsar :math:`\mu` (system) D : astropy.units.Quantity typically in units of u.kpc or u.pc Distance to the pulsar Returns ------- acceleration : astropy.units.Quantity Shklovskii acceleration Notes ----- .. [10] Lorimer & Kramer, 2008, "The Handbook of Pulsar Astronomy", Eqn. 8.12 """ # This uses the small angle approximation that sin(x) = x, so we need to # make our angle dimensionless. with u.set_enabled_equivalencies(u.dimensionless_angles()): a_s = (D * pmtot**2 / const.c).to(u.s**-1) return a_s
def d_Drep_d_par(self, par): """Derivative computation. Computes:: Drep = d_Dre_d_Phi = a1/c.c*(cos(Phi) + eps1 * sin(Phi) + eps2 * cos(Phi)) d_Drep_d_par = d_a1_d_par /c.c*(cos(Phi) + eps1 * sin(Phi) + eps2 * cos(Phi)) + d_Drep_d_Phi * d_Phi_d_par + d_Drep_d_eps1*d_eps1_d_par + d_Drep_d_eps2*d_eps2_d_par """ a1 = self.a1() Phi = self.Phi() eps1 = self.eps1() eps2 = self.eps2() d_a1_d_par = self.prtl_der("a1", par) d_Drep_d_Phi = self.Drepp() d_Phi_d_par = self.prtl_der("Phi", par) d_Drep_d_eps1 = a1 / c.c * np.sin(2.0 * Phi) d_Drep_d_eps2 = a1 / c.c * np.cos(2.0 * Phi) with u.set_enabled_equivalencies(u.dimensionless_angles()): d_Drep_d_par = ( d_a1_d_par / c.c * (np.cos(Phi) + eps1 * np.sin(2.0 * Phi) + eps2 * np.cos(2.0 * Phi)) + d_Drep_d_Phi * d_Phi_d_par + d_Drep_d_eps1 * self.prtl_der("eps1", par) + d_Drep_d_eps2 * self.prtl_der("eps2", par) ) return d_Drep_d_par
def __new__(cls, arg1, arg2=None): # Assume inputs are numerical, could add an extra # case to parse strings as input. # if it is not a list, convert to a list if not hasattr(arg1, 'unit'): arg1 = arg1 * u.cycle if arg1.shape == (): arg1 = arg1.reshape((1, )) with u.set_enabled_equivalencies(dimensionless_cycles): arg1 = arg1.to(u.Unit("")) # Since modf does not like dimensioned quantity if arg2 is None: ff, ii = numpy.modf(arg1) else: if not hasattr(arg2, 'unit'): arg2 = arg2 * u.cycle if arg2.shape == (): arg2 = arg2.reshape((1, )) arg2 = arg2.to(u.Unit("")) arg1S = numpy.modf(arg1) arg2S = numpy.modf(arg2) ii = arg1S[1] + arg2S[1] ff = arg2S[0] index = numpy.where(ff < -0.5) ff[index] += 1.0 ii[index] -= 1 index = numpy.where(ff > 0.5) ff[index] -= 1.0 ii[index] += 1 return super(Phase, cls).__new__(cls, ii.to(u.cycle), ff.to(u.cycle))
def d_phase_d_jump(self, toas, jump_param, delay): tbl = toas.table jpar = getattr(self, jump_param) d_phase_d_j = numpy.zeros(len(tbl)) mask = jpar.select_toa_mask(toas) d_phase_d_j[mask] = self.F0.value with u.set_enabled_equivalencies(dimensionless_cycles): return (d_phase_d_j * self.F0.units).to(u.cycle/u.second)
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 test_equivalency_context(): with u.set_enabled_equivalencies(u.dimensionless_angles()): phase = u.Quantity(1., u.cycle) assert_allclose(np.exp(1j*phase), 1.) Omega = u.cycle / (1.*u.minute) assert_allclose(np.exp(1j*Omega*60.*u.second), 1.) # ensure we can turn off equivalencies even within the scope with pytest.raises(u.UnitsError): phase.to(1, equivalencies=None) # test the manager also works in the Quantity constructor. q1 = u.Quantity(phase, u.dimensionless_unscaled) assert_allclose(q1.value, u.cycle.to(u.radian)) # and also if we use a class that happens to have a unit attribute. class MyQuantityLookalike(np.ndarray): pass mylookalike = np.array(1.).view(MyQuantityLookalike) mylookalike.unit = 'cycle' # test the manager also works in the Quantity constructor. q2 = u.Quantity(mylookalike, u.dimensionless_unscaled) assert_allclose(q2.value, u.cycle.to(u.radian)) with u.set_enabled_equivalencies(u.spectral()): u.GHz.to(u.cm) eq_on = u.GHz.find_equivalent_units() with pytest.raises(u.UnitsError): u.GHz.to(u.cm, equivalencies=None) # without equivalencies, we should find a smaller (sub)set eq_off = u.GHz.find_equivalent_units() assert all(eq in set(eq_on) for eq in eq_off) assert set(eq_off) < set(eq_on) # Check the equivalency manager also works in ufunc evaluations, # not just using (wrong) scaling. [#2496] l2v = u.doppler_optical(6000 * u.angstrom) l1 = 6010 * u.angstrom assert l1.to(u.km/u.s, equivalencies=l2v) > 100. * u.km / u.s with u.set_enabled_equivalencies(l2v): assert l1 > 100. * u.km / u.s assert abs((l1 - 500. * u.km / u.s).to(u.angstrom)) < 1. * u.km/u.s
def build_table(self): """ Create a human readable table. Returns ------- table : `astropy.table.QTable` """ keywords = ['Start Time', 'End Time', 'Source', 'Instrument', 'Type', 'Wavelength'] record_items = {} for key in keywords: record_items[key] = [] def validate_time(time): # Handle if the time is None when coming back from VSO if time is None: return ['None'] if record.time.start is not None: return [parse_time(time).strftime(TIME_FORMAT)] else: return ['N/A'] for record in self: record_items['Start Time'].append(validate_time(record.time.start)) record_items['End Time'].append(validate_time(record.time.end)) record_items['Source'].append(str(record.source)) record_items['Instrument'].append(str(record.instrument)) record_items['Type'].append(str(record.extent.type) if record.extent.type is not None else ['N/A']) # If we have a start and end Wavelength, make a quantity if hasattr(record, 'wave') and record.wave.wavemin and record.wave.wavemax: unit = record.wave.waveunit # Convert this so astropy units parses it correctly if unit == "kev": unit = "keV" record_items['Wavelength'].append(u.Quantity([float(record.wave.wavemin), float(record.wave.wavemax)], unit=unit)) # If not save None else: record_items['Wavelength'].append(None) # If we have no wavelengths for the whole list, drop the col if all([a is None for a in record_items['Wavelength']]): record_items.pop('Wavelength') keywords.remove('Wavelength') else: # Make whole column a quantity try: with u.set_enabled_equivalencies(u.spectral()): record_items['Wavelength'] = u.Quantity(record_items['Wavelength']) # If we have mixed units or some Nones just represent as strings except (u.UnitConversionError, TypeError): record_items['Wavelength'] = [str(a) for a in record_items['Wavelength']] return Table(record_items)[keywords]
def quantity(self, qty): try: with set_enabled_equivalencies(self.equivalencies): self._value = qty.to(self.unit).value except AttributeError: if self.unit is dimensionless_unscaled or qty == 0: self._value = qty else: raise UnitsError("Can only assign dimensionless values " "to dimensionless quantities " "(unless the value is 0)")
def __init__(self, wn, od, **kwargs): """ AbsorptionSpectrum(wn,od,**kwargs) Constructor for the `AbsorptionSpectrum` class. Parameters ---------- wn : `astropy.units.Quantity` The absorption spectrum frequency data. Unlike `BaseSpectrum`, the initialisation of `AbsorptionSpectrum` requires this to be in the specific units of reciprocal wavenumber. However, if it is in a quantity convertable to kayser, conversion will be attempted while a warning is given to notify the user of this. od : `astropy.units.Quantity` The absorption spectrum optical depth data. Unlike `BaseSpectrum`, the initialisation of `AbsorptionSpectrum` requires this to be in the specific units of optical depth units (from `omnifit.utils.unit_od`). **kwargs : Arguments, optional Additional initialisation arguments can be passed to `BaseSpectrum` using this. Note that x and y are defined using the other initialisation parameters of `AbsorptionSpectrum`. """ if type(wn) != u.quantity.Quantity: raise u.UnitsError("Input wn is not an astropy quantity.") if wn.unit != u.kayser: warnings.warn("Input wn is not in kayser units. Converting...", RuntimeWarning) with u.set_enabled_equivalencies(u.equivalencies.spectral()): wn = wn.to(u.kayser) if type(od) != u.quantity.Quantity: raise u.UnitsError("Input od is not an astropy quantity.") if od.unit != utils.unit_od: raise u.UnitsError("Input od is not in optical depth units.") if len(wn) != len(od): raise RuntimeError("Input arrays have different sizes.") self.wn = wn with u.set_enabled_equivalencies(u.equivalencies.spectral()): self.wl = self.wn.to(u.micron) self.od = od BaseSpectrum.__init__(self, self.wn, self.od, **kwargs)
def d_beta_d_EDOT(self): """dBeta/dEDOT = A1/c*((-(e+dtheta)/sqrt(1-(e+dtheta)**2)*cos(omega)*de/dEDOT- \ (1-eTheta**2)**0.5*sin(omega)*domega/dEDOT de/dEDOT = tt0 """ eTheta = self.eTheta() a1 = (self.a1()).decompose() sinOmg = np.sin(self.omega()) cosOmg = np.cos(self.omega()) with u.set_enabled_equivalencies(u.dimensionless_angles()): return a1/c.c*((-eTheta)/np.sqrt(1-eTheta**2)*cosOmg*self.tt0- \ (1-eTheta**2)**0.5*sinOmg*self.d_omega_d_par('EDOT'))
def d_phase_d_F(self, toas, param, delay): """Calculate the derivative wrt to an spin term.""" par = getattr(self, param) unit = par.units pn, idxf, idxv = split_prefixed_name(param) order = idxv + 1 fterms = [0.0 * u.Unit("")] + self.get_spin_terms() # make the choosen fterms 1 others 0 fterms = [ft * numpy.longdouble(0.0)/unit for ft in fterms] fterms[order] += numpy.longdouble(1.0) dt = self.get_dt(toas, delay) with u.set_enabled_equivalencies(dimensionless_cycles): d_pphs_d_f = taylor_horner(dt.to(u.second), fterms) return d_pphs_d_f.to(u.cycle/unit)
def test_multiplication_division(self): """Check that multiplication/division with other quantities is only possible when the physical unit is dimensionless, and that this turns the result into a normal quantity.""" lq = u.Magnitude(np.arange(1., 11.)*u.Jy) with pytest.raises(u.UnitsError): lq * (1.*u.m) with pytest.raises(u.UnitsError): (1.*u.m) * lq with pytest.raises(u.UnitsError): lq / lq for unit in (u.m, u.mag, u.dex): with pytest.raises(u.UnitsError): lq / unit lq2 = u.Magnitude(np.arange(1, 11.)) with pytest.raises(u.UnitsError): lq2 * lq with pytest.raises(u.UnitsError): lq2 / lq with pytest.raises(u.UnitsError): lq / lq2 # but dimensionless_unscaled can be cancelled r = lq2 / u.Magnitude(2.) assert r.unit == u.dimensionless_unscaled assert np.all(r.value == lq2.value/2.) # with dimensionless, normal units OK, but return normal quantities tf = lq2 * u.m tr = u.m * lq2 for t in (tf, tr): assert not isinstance(t, type(lq2)) assert t.unit == lq2.unit.function_unit * u.m with u.set_enabled_equivalencies(u.logarithmic()): with pytest.raises(u.UnitsError): t.to(lq2.unit.physical_unit) t = tf / (50.*u.cm) # now we essentially have the same quantity but with a prefactor of 2 assert t.unit.is_equivalent(lq2.unit.function_unit) assert_allclose(t.to(lq2.unit.function_unit), lq2._function_view*2)
def d_phase_d_GLF2(self, toas, param, delay): """Calculate the derivative wrt GLF1""" tbl = toas.table p, ids, idv = split_prefixed_name(param) if p != 'GLF2_': raise ValueError("Can not calculate d_phase_d_GLF2 with respect to %s." % param) eph = time_to_longdouble(getattr(self, "GLEP_" + ids).value) par_GLF2 = getattr(self, param) dt = (tbl['tdbld'] - eph) * u.day - delay dt = dt.to(u.second) affected = numpy.where(dt > 0.0)[0] dpdGLF2 = numpy.zeros(len(tbl), dtype=numpy.longdouble) * u.cycle/par_GLF2.units with u.set_enabled_equivalencies(dimensionless_cycles): dpdGLF2[affected] += numpy.longdouble(1.0)/6.0 * dt[affected] * dt[affected] * dt[affected] return dpdGLF2
def spindown_phase(self, toas, delay): """Spindown phase function. delay is the time delay from the TOA to time of pulse emission at the pulsar, in seconds. This routine should implement Eq 120 of the Tempo2 Paper II (2006, MNRAS 372, 1549) returns an array of phases in long double """ dt = self.get_dt(toas, delay) # Add the [0.0] because that is the constant phase term fterms = [0.0 * u.cycle] + self.get_spin_terms() with u.set_enabled_equivalencies(dimensionless_cycles): phs = taylor_horner(dt.to(u.second), fterms) return phs.to(u.cycle)
def test_unitconversions(self): transmittance = 10.0 absorbance = -np.log10(transmittance) opticaldepth = absorbance*np.log(10) transmittance *= utils.unit_transmittance absorbance *= utils.unit_absorbance opticaldepth *= utils.unit_opticaldepth with u.set_enabled_equivalencies(utils.equivalencies_absorption): assert absorbance.to(utils.unit_transmittance).value == transmittance.value assert absorbance.to(utils.unit_opticaldepth).value == opticaldepth.value assert transmittance.to(utils.unit_absorbance).value == absorbance.value assert transmittance.to(utils.unit_opticaldepth).value == opticaldepth.value assert opticaldepth.to(utils.unit_absorbance).value == absorbance.value assert opticaldepth.to(utils.unit_transmittance).value == transmittance.value
def d_nhat_d_par(self,par): """nhat = n/(1-ecc*cos(E)) n = 2*pi/PB # should here be M()? dnhat = -2*pi*dPB/PB^2*(1-ecc*cos(E)) -2*pi*(-cos(E)*decc+ecc*sin(E)*dE)/PB*(1-ecc*cos(E))^2 dnhat/dPar = -2*pi/(PB*(1-ecc*cos(E))*((dPB/dPar)/PB - (-cos(E)*decc/dPar+ecc*sin(E)*dE/dpar)/(1-e*cos(E))) """ sinE = np.sin(self.E()) cosE = np.cos(self.E()) with u.set_enabled_equivalencies(u.dimensionless_angles()): oneMeccTcosE = (1-self.ecc()*cosE) fctr = -2*np.pi/self.pb()/oneMeccTcosE return fctr*(self.prtl_der('PB',par)/self.pb() - \ (cosE*self.prtl_der('ecc',par)- \ self.ecc()*sinE*self.prtl_der('E',par))/oneMeccTcosE)
def convert2(self, newunit): """ convert2(newunit,clone=False) Convert the x axis data to given spectral units. Re-sort the data afterwards. Parameters ---------- newunit : `astropy.units.core.Unit` Desired (spectral) unit the x axis data should be converted to. clone : `bool`, optional If set to True, returns a modified copy of the spectrum instead of operating on the existing spectrum. """ with u.set_enabled_equivalencies(u.equivalencies.spectral()): self.x = self.x.to(newunit) self.__sort()
def test_kkiterfull(self): """ Make sure that a longer KK iteration converges nicely """ testspec = helpers.generate_absspectrum_alt() assert testspec.x.unit == u.kayser assert testspec.y.unit == utils.unit_od testspec.subspectrum(2000.,4500.) freq = testspec.x transmittance = testspec.y.to(utils.unit_transmittance,equivalencies=utils.equivalencies_absorption) m_substrate = 1.74+0.0j #CsI window, like in the original Hudgins paper d_ice = 1.0*u.micron #not probably true, but good enough for testing m0 = 1.3 + 0.0j with u.set_enabled_equivalencies(u.equivalencies.spectral()): freq_m0 = (0.15*u.micron).to(u.kayser).value m_ice = utils.kramers_kronig(freq,transmittance,m_substrate,d_ice,m0,freq_m0) assert m_ice.shape == freq.shape assert np.all(np.logical_not(np.isnan(m_ice.real))) assert np.all(np.logical_not(np.isnan(m_ice.imag)))
def geostationary( cls, attractor, angular_velocity=None, period=None, hill_radius=None ): """Return the geostationary orbit for the given attractor and its rotational speed. Parameters ---------- attractor : Body Main attractor. angular_velocity : ~astropy.units.Quantity Rotational angular velocity of the attractor. period : ~astropy.units.Quantity Attractor's rotational period, ignored if angular_velocity is passed. hill_radius : ~astropy.units.Quantity Radius of Hill sphere of the attractor (optional). Hill sphere radius(in contrast with Laplace's SOI) is used here to validate the stability of the geostationary orbit, that is to make sure that the orbital radius required for the geostationary orbit is not outside of the gravitational sphere of influence of the attractor. Hill SOI of parent(if exists) of the attractor is ignored if hill_radius is not provided. """ if angular_velocity is None and period is None: raise ValueError( "At least one among angular_velocity or period must be passed" ) if angular_velocity is None: angular_velocity = 2 * np.pi / period # Find out geostationary radius using r = cube_root(GM/(angular # velocity)^2) with u.set_enabled_equivalencies(u.dimensionless_angles()): geo_radius = np.cbrt(attractor.k / np.square(angular_velocity.to(1 / u.s))) if hill_radius is not None and geo_radius > hill_radius: raise ValueError( "Geostationary orbit for the given parameters doesn't exist" ) altitude = geo_radius - attractor.R return cls.circular(attractor, altitude)