Coverage for python/lsst/sims/utils/ModifiedJulianDate.py : 21%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from builtins import object
2import warnings
3import numpy as np
4import copy
6from astropy.time import Time
7from astropy.utils.iers.iers import IERSRangeError
9__all__ = ["ModifiedJulianDate", "MJDWarning", "UTCtoUT1Warning"]
12# Filter out ERFA's complaints that we are simulating dates which
13# are in the future
14warnings.filterwarnings("ignore",
15 message='.*taiutc.*dubious.year.*')
18class MJDWarning(Warning):
19 """
20 A sub-class of Warning. All of the warnings raised by ModifiedJulianDate
21 will be of this class (or its sub-classes), so that users can filter them
22 out by creating a simple filter targeted at category=MJDWarning.
23 """
24 pass
27class UTCtoUT1Warning(MJDWarning):
28 """
29 A sub-class of MJDWarning meant for use when astropy.Time cannot interpolate
30 UT1-UTC as a function of UTC because UTC is out of bounds of the data.
31 This class exists so that users can filter these warnings out by creating
32 a simple filter targeted at category=UTCtoUT1Warning.
33 """
34 pass
37class ModifiedJulianDate(object):
39 @classmethod
40 def _get_ut1_from_utc(cls, UTC):
41 """
42 Take a numpy array of UTC values and return a numpy array of UT1 and dut1 values
43 """
45 time_list = Time(UTC, scale='utc', format='mjd')
47 try:
48 dut1_out = time_list.delta_ut1_utc
49 ut1_out = time_list.ut1.mjd
50 except IERSRangeError:
51 ut1_out = np.copy(UTC)
52 dut1_out = np.zeros(len(UTC))
53 warnings.warn("ModifiedJulianData.get_list() was given date values that are outside "
54 "astropy's range of interpolation for converting from UTC to UT1. "
55 "We will treat UT1=UTC for those dates, lacking a better alternative.",
56 category=UTCtoUT1Warning)
57 from astropy.utils.iers import TIME_BEFORE_IERS_RANGE, TIME_BEYOND_IERS_RANGE
58 dut1_test, status = time_list.get_delta_ut1_utc(return_status=True)
59 good_dexes = np.where(np.logical_and(status != TIME_BEFORE_IERS_RANGE,
60 status != TIME_BEYOND_IERS_RANGE))
62 if len(good_dexes[0]) > 0:
63 time_good = Time(UTC[good_dexes], scale='utc', format='mjd')
64 dut1_good = time_good.delta_ut1_utc
65 ut1_good = time_good.ut1.mjd
67 ut1_out[good_dexes] = ut1_good
68 dut1_out[good_dexes] = dut1_good
70 return ut1_out, dut1_out
72 @classmethod
73 def get_list(cls, TAI=None, UTC=None):
74 """
75 Instantiate a list of ModifiedJulianDates from a numpy array of either TAI
76 or UTC values.
78 @param[in] TAI (optional) a numpy array of MJD' in TAI
80 @param[in] UTC (optional) a numpy array of MJDs in UTC
82 @param[out] a list of ModifiedJulianDate instantiations with all of their
83 properties already set (so the code does not waste time converting from TAI
84 to TT, TDB, etc. when those time scales are called for).
85 """
87 if TAI is None and UTC is None:
88 return None
90 if TAI is not None and UTC is not None:
91 raise RuntimeError("You should not specify both TAI and UTC in ModifiedJulianDate.get_list()")
93 if TAI is not None:
94 time_list = Time(TAI, scale='tai', format='mjd')
95 tai_list = TAI
96 utc_list = time_list.utc.mjd
97 elif UTC is not None:
98 time_list = Time(UTC, scale='utc', format='mjd')
99 utc_list = UTC
100 tai_list = time_list.tai.mjd
102 tt_list = time_list.tt.mjd
103 tdb_list = time_list.tdb.mjd
105 ut1_list, dut1_list = cls._get_ut1_from_utc(utc_list)
107 values = np.array([tai_list, utc_list, tt_list, tdb_list,
108 ut1_list, dut1_list]).transpose()
110 output = []
111 for vv in values:
112 mjd = ModifiedJulianDate(TAI=40000.0)
113 mjd._force_values(vv)
114 output.append(mjd)
116 return output
118 def __init__(self, TAI=None, UTC=None):
119 """
120 Must specify either:
122 @param [in] TAI = the International Atomic Time as an MJD
124 or
126 @param [in] UTC = Universal Coordinate Time as an MJD
127 """
129 if TAI is None and UTC is None:
130 raise RuntimeError("You must specify either TAI or UTC to "
131 "instantiate ModifiedJulianDate")
133 if TAI is not None:
134 self._time = Time(TAI, scale='tai', format='mjd')
135 self._tai = TAI
136 self._utc = None
137 self._initialized_with = 'TAI'
138 else:
139 self._time = Time(UTC, scale='utc', format='mjd')
140 self._utc = UTC
141 self._tai = None
142 self._initialized_with = 'UTC'
144 self._tt = None
145 self._tdb = None
146 self._ut1 = None
147 self._dut1 = None
149 def _force_values(self, values):
150 """
151 Force the properties of this ModifiedJulianDate to have specific values.
153 values is a list of [TAI, UTC, TT, TDB, UT1, UT1-UTC] values.
155 This method exists so that, when instantiating lists of ModifiedJulianDates,
156 we can use astropy.time.Time's vectorized methods to quickly perform many
157 conversions at once. Users should not try to use this method by hand.
158 """
159 self._tai = values[0]
160 self._utc = values[1]
161 self._tt = values[2]
162 self._tdb = values[3]
163 self._ut1 = values[4]
164 self._dut1 = values[5]
166 def __eq__(self, other):
167 return self._time == other._time
169 def __ne__(self, other):
170 return not self.__eq__(other)
172 def __deepcopy__(self, memo):
173 if self._initialized_with == 'TAI':
174 new_mjd = ModifiedJulianDate(TAI=self.TAI)
175 else:
176 new_mjd = ModifiedJulianDate(UTC=self.UTC)
178 new_mjd._tai = copy.deepcopy(self._tai, memo)
179 new_mjd._utc = copy.deepcopy(self._utc, memo)
180 new_mjd._tt = copy.deepcopy(self._tt, memo)
181 new_mjd._tdb = copy.deepcopy(self._tdb, memo)
182 new_mjd._ut1 = copy.deepcopy(self._ut1, memo)
183 new_mjd._dut1 = copy.deepcopy(self._dut1, memo)
185 return new_mjd
187 def _warn_utc_out_of_bounds(self, method_name):
188 """
189 Raise a standard warning if UTC is outside of the range that can
190 be interpolated on the IERS tables.
192 method_name is the name of the method that caused this warning.
193 """
194 warnings.warn("UTC is outside of IERS table for UT1-UTC.\n"
195 "Returning UT1 = UTC for lack of a better idea\n"
196 "This warning was caused by calling ModifiedJulianDate.%s\n" % method_name,
197 category=UTCtoUT1Warning)
199 @property
200 def TAI(self):
201 """
202 International Atomic Time as an MJD
203 """
204 if self._tai is None:
205 self._tai = self._time.tai.mjd
207 return self._tai
209 @property
210 def UTC(self):
211 """
212 Universal Coordinate Time as an MJD
213 """
214 if self._utc is None:
215 self._utc = self._time.utc.mjd
217 return self._utc
219 @property
220 def UT1(self):
221 """
222 Universal Time as an MJD
223 """
224 if self._ut1 is None:
225 try:
226 self._ut1 = self._time.ut1.mjd
227 except IERSRangeError:
228 self._warn_utc_out_of_bounds('UT1')
229 self._ut1 = self.UTC
231 return self._ut1
233 @property
234 def dut1(self):
235 """
236 UT1-UTC in seconds
237 """
239 if self._dut1 is None:
240 try:
241 self._dut1 = self._time.delta_ut1_utc
242 except IERSRangeError:
243 self._warn_utc_out_of_bounds('dut1')
244 self._dut1 = 0.0
246 return self._dut1
248 @property
249 def TT(self):
250 """
251 Terrestrial Time (aka Terrestrial Dynamical Time) as an MJD
252 """
253 if self._tt is None:
254 self._tt = self._time.tt.mjd
256 return self._tt
258 @property
259 def TDB(self):
260 """
261 Barycentric Dynamical Time as an MJD
262 """
263 if self._tdb is None:
264 self._tdb = self._time.tdb.mjd
266 return self._tdb