Coverage for tests/test_alertDataGenerator.py : 11%

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
1import unittest
2import os
3import numpy as np
4import tempfile
5import sqlite3
6import shutil
7import numbers
8import gc
9import lsst.utils.tests
11from lsst.utils import getPackageDir
12import lsst.obs.lsst.phosim as obs_lsst_phosim
13from lsst.sims.utils.CodeUtilities import sims_clean_up
14from lsst.sims.catalogs.decorators import register_method
15from lsst.sims.catalogs.db import CatalogDBObject
16from lsst.sims.catalogs.db import DBObject
17from lsst.sims.catUtils.utils import ObservationMetaDataGenerator
18from lsst.sims.catUtils.utils import AlertStellarVariabilityCatalog
19from lsst.sims.catUtils.utils import AlertDataGenerator
20from lsst.sims.catUtils.utils import StellarAlertDBObjMixin
22from lsst.sims.utils import applyProperMotion
23from lsst.sims.utils import ModifiedJulianDate
24from lsst.sims.utils import findHtmid
25from lsst.sims.utils import angularSeparation
26from lsst.sims.photUtils import Sed
27from lsst.sims.coordUtils import chipNameFromRaDec
28from lsst.sims.coordUtils import pixelCoordsFromRaDec
29from lsst.sims.photUtils import calcSNR_m5, BandpassDict
30from lsst.sims.photUtils import PhotometricParameters
31from lsst.sims.catUtils.mixins import CameraCoords
32from lsst.sims.catUtils.mixins import AstrometryStars
33from lsst.sims.catUtils.mixins import Variability
34from lsst.sims.catalogs.definitions import InstanceCatalog
35from lsst.sims.catalogs.decorators import compound, cached
38ROOT = os.path.abspath(os.path.dirname(__file__))
41def setup_module(module):
42 lsst.utils.tests.init()
45class AlertDataGeneratorTestCase(unittest.TestCase):
47 longMessage = True
49 @classmethod
50 def setUpClass(cls):
51 print('setting up %s' % sims_clean_up.targets)
53 cls.camera = obs_lsst_phosim.PhosimMapper().camera
55 # These represent the dimmest magnitudes at which objects
56 # are considered visible in each of the LSST filters
57 # (taken from Table 2 of the overview paper)
58 cls.obs_mag_cutoff = (23.68, 24.89, 24.43, 24.0, 24.45, 22.60)
60 cls.opsim_db = os.path.join(getPackageDir('sims_data'),
61 'OpSimData',
62 'opsimblitz1_1133_sqlite.db')
64 rng = np.random.RandomState(8123)
66 obs_gen = ObservationMetaDataGenerator(database=cls.opsim_db)
67 cls.obs_list = obs_gen.getObservationMetaData(night=(0, 2))
68 cls.obs_list = rng.choice(cls.obs_list, 10, replace=False)
69 fieldid_list = []
70 for obs in cls.obs_list:
71 fieldid_list.append(obs.OpsimMetaData['fieldID'])
73 # make sure we have selected observations such that the
74 # same field is revisited more than once
75 assert len(np.unique(fieldid_list)) < len(fieldid_list)
77 cls.input_dir = tempfile.mkdtemp(prefix='alertDataGen',
78 dir=ROOT)
80 cls.star_db_name = tempfile.mktemp(prefix='alertDataGen_star_db',
81 dir=cls.input_dir,
82 suffix='.db')
84 conn = sqlite3.connect(cls.star_db_name)
85 cursor = conn.cursor()
86 cursor.execute('''CREATE TABLE stars
87 (simobjid int, htmid int, ra real, dec real,
88 umag real, gmag real, rmag real,
89 imag real, zmag real, ymag real,
90 px real, pmra real, pmdec real,
91 vrad real, varParamStr text)''')
92 conn.commit()
94 n_stars = 10
96 cls.ra_truth = np.zeros(n_stars*len(cls.obs_list), dtype=float)
97 cls.dec_truth = np.zeros(n_stars*len(cls.obs_list), dtype=float)
98 u_truth = np.zeros(n_stars*len(cls.obs_list), dtype=float)
99 g_truth = np.zeros(n_stars*len(cls.obs_list), dtype=float)
100 r_truth = np.zeros(n_stars*len(cls.obs_list), dtype=float)
101 i_truth = np.zeros(n_stars*len(cls.obs_list), dtype=float)
102 z_truth = np.zeros(n_stars*len(cls.obs_list), dtype=float)
103 y_truth = np.zeros(n_stars*len(cls.obs_list), dtype=float)
104 cls.px_truth = np.zeros(n_stars*len(cls.obs_list), dtype=float)
105 cls.pmra_truth = np.zeros(n_stars*len(cls.obs_list), dtype=float)
106 cls.pmdec_truth = np.zeros(n_stars*len(cls.obs_list), dtype=float)
107 cls.vrad_truth = np.zeros(n_stars*len(cls.obs_list), dtype=float)
108 cls.amp_truth = np.zeros(n_stars*len(cls.obs_list), dtype=float)
109 cls.period_truth = np.zeros(n_stars*len(cls.obs_list), dtype=float)
111 id_offset = -n_stars
112 for obs in cls.obs_list:
113 id_offset += n_stars
114 ra_0 = obs.pointingRA
115 dec_0 = obs.pointingDec
116 rr = rng.random_sample(n_stars)
117 theta = rng.random_sample(n_stars)*2.0*np.pi
118 ra = ra_0 + rr*np.cos(theta)
119 dec = dec_0 + rr*np.sin(theta)
120 var_period = rng.random_sample(n_stars)*0.25
121 var_amp = rng.random_sample(n_stars)*1.0 + 0.01
123 subset = rng.randint(0, high=len(var_amp)-1, size=3)
124 var_amp[subset[:2]] = 0.0
125 var_amp[subset[-1]] = -1.0
127 umag = rng.random_sample(n_stars)*5.0 + 15.0
128 gmag = rng.random_sample(n_stars)*5.0 + 15.0
129 rmag = rng.random_sample(n_stars)*5.0 + 15.0
130 imag = rng.random_sample(n_stars)*5.0 + 15.0
131 zmag = rng.random_sample(n_stars)*5.0 + 15.0
132 ymag = rng.random_sample(n_stars)*5.0 + 15.0
133 px = rng.random_sample(n_stars)*0.1 # say it is arcsec
134 pmra = rng.random_sample(n_stars)*50.0+100.0 # say it is arcsec/yr
135 pmdec = rng.random_sample(n_stars)*50.0+100.0 # say it is arcsec/yr
136 vrad = rng.random_sample(n_stars)*600.0 - 300.0
138 subset = rng.randint(0, high=n_stars-1, size=3)
139 umag[subset] = 40.0
140 gmag[subset] = 40.0
141 rmag[subset] = 40.0
142 imag[subset] = 40.0
143 zmag[subset] = 40.0
144 ymag[subset] = 40.0
146 cls.ra_truth[id_offset:id_offset+n_stars] = np.round(ra, decimals=6)
147 cls.dec_truth[id_offset:id_offset+n_stars] = np.round(dec, decimals=6)
148 u_truth[id_offset:id_offset+n_stars] = np.round(umag, decimals=4)
149 g_truth[id_offset:id_offset+n_stars] = np.round(gmag, decimals=4)
150 r_truth[id_offset:id_offset+n_stars] = np.round(rmag, decimals=4)
151 i_truth[id_offset:id_offset+n_stars] = np.round(imag, decimals=4)
152 z_truth[id_offset:id_offset+n_stars] = np.round(zmag, decimals=4)
153 y_truth[id_offset:id_offset+n_stars] = np.round(ymag, decimals=4)
154 cls.px_truth[id_offset:id_offset+n_stars] = np.round(px, decimals=4)
155 cls.pmra_truth[id_offset:id_offset+n_stars] = np.round(pmra, decimals=4)
156 cls.pmdec_truth[id_offset:id_offset+n_stars] = np.round(pmdec, decimals=4)
157 cls.vrad_truth[id_offset:id_offset+n_stars] = np.round(vrad, decimals=4)
158 cls.amp_truth[id_offset:id_offset+n_stars] = np.round(var_amp, decimals=4)
159 cls.period_truth[id_offset:id_offset+n_stars] = np.round(var_period, decimals=4)
161 cls.max_str_len = -1
163 for i_star in range(n_stars):
164 if var_amp[i_star] >= -0.1:
165 varParamStr = ('{"m":"alert_test", "p":{"amp":%.4f, "per": %.4f}}'
166 % (var_amp[i_star], var_period[i_star]))
167 else:
168 varParamStr = 'None'
170 if len(varParamStr) > cls.max_str_len:
171 cls.max_str_len = len(varParamStr)
173 htmid = findHtmid(ra[i_star], dec[i_star], 21)
175 query = ('''INSERT INTO stars VALUES(%d, %d, %.6f, %.6f,
176 %.4f, %.4f, %.4f, %.4f, %.4f, %.4f,
177 %.4f, %.4f, %.4f, %.4f, '%s')'''
178 % (i_star+id_offset+1, htmid, ra[i_star], dec[i_star],
179 umag[i_star], gmag[i_star], rmag[i_star],
180 imag[i_star], zmag[i_star], ymag[i_star],
181 px[i_star], pmra[i_star], pmdec[i_star],
182 vrad[i_star], varParamStr))
184 cursor.execute(query)
185 conn.commit()
186 conn.close()
188 cls.output_dir = tempfile.mkdtemp(dir=ROOT, prefix='alert_gen_output')
189 cls.mag0_truth_dict = {}
190 cls.mag0_truth_dict[0] = u_truth
191 cls.mag0_truth_dict[1] = g_truth
192 cls.mag0_truth_dict[2] = r_truth
193 cls.mag0_truth_dict[3] = i_truth
194 cls.mag0_truth_dict[4] = z_truth
195 cls.mag0_truth_dict[5] = y_truth
197 @classmethod
198 def tearDownClass(cls):
199 sims_clean_up()
200 if os.path.exists(cls.star_db_name):
201 os.unlink(cls.star_db_name)
202 if os.path.exists(cls.input_dir):
203 shutil.rmtree(cls.input_dir)
204 for file_name in os.listdir(cls.output_dir):
205 os.unlink(os.path.join(cls.output_dir, file_name))
206 shutil.rmtree(cls.output_dir)
208 def test_alert_data_generation(self):
210 dmag_cutoff = 0.005
211 mag_name_to_int = {'u': 0, 'g': 1, 'r': 2, 'i': 3, 'z' : 4, 'y': 5}
213 _max_var_param_str = self.max_str_len
215 class StarAlertTestDBObj(StellarAlertDBObjMixin, CatalogDBObject):
216 objid = 'star_alert'
217 tableid = 'stars'
218 idColKey = 'simobjid'
219 raColName = 'ra'
220 decColName = 'dec'
221 objectTypeId = 0
222 columns = [('raJ2000', 'ra*0.01745329252'),
223 ('decJ2000', 'dec*0.01745329252'),
224 ('parallax', 'px*0.01745329252/3600.0'),
225 ('properMotionRa', 'pmra*0.01745329252/3600.0'),
226 ('properMotionDec', 'pmdec*0.01745329252/3600.0'),
227 ('radialVelocity', 'vrad'),
228 ('variabilityParameters', 'varParamStr', str, _max_var_param_str)]
230 class TestAlertsVarCatMixin(object):
232 @register_method('alert_test')
233 def applyAlertTest(self, valid_dexes, params, expmjd, variability_cache=None):
234 if len(params) == 0:
235 return np.array([[], [], [], [], [], []])
237 if isinstance(expmjd, numbers.Number):
238 dmags_out = np.zeros((6, self.num_variable_obj(params)))
239 else:
240 dmags_out = np.zeros((6, self.num_variable_obj(params), len(expmjd)))
242 for i_star in range(self.num_variable_obj(params)):
243 if params['amp'][i_star] is not None:
244 dmags = params['amp'][i_star]*np.cos(params['per'][i_star]*expmjd)
245 for i_filter in range(6):
246 dmags_out[i_filter][i_star] = dmags
248 return dmags_out
250 class TestAlertsVarCat(TestAlertsVarCatMixin, AlertStellarVariabilityCatalog):
251 pass
253 class TestAlertsTruthCat(TestAlertsVarCatMixin, CameraCoords, AstrometryStars,
254 Variability, InstanceCatalog):
255 column_outputs = ['uniqueId', 'chipName', 'dmagAlert', 'magAlert']
257 camera = obs_lsst_phosim.PhosimMapper().camera
259 @compound('delta_umag', 'delta_gmag', 'delta_rmag',
260 'delta_imag', 'delta_zmag', 'delta_ymag')
261 def get_TruthVariability(self):
262 return self.applyVariability(self.column_by_name('varParamStr'))
264 @cached
265 def get_dmagAlert(self):
266 return self.column_by_name('delta_%smag' % self.obs_metadata.bandpass)
268 @cached
269 def get_magAlert(self):
270 return self.column_by_name('%smag' % self.obs_metadata.bandpass) + \
271 self.column_by_name('dmagAlert')
273 star_db = StarAlertTestDBObj(database=self.star_db_name, driver='sqlite')
275 # assemble the true light curves for each object; we need to figure out
276 # if their np.max(dMag) ever goes over dmag_cutoff; then we will know if
277 # we are supposed to simulate them
278 true_lc_dict = {}
279 true_lc_obshistid_dict = {}
280 is_visible_dict = {}
281 obs_dict = {}
282 max_obshistid = -1
283 n_total_observations = 0
284 for obs in self.obs_list:
285 obs_dict[obs.OpsimMetaData['obsHistID']] = obs
286 obshistid = obs.OpsimMetaData['obsHistID']
287 if obshistid > max_obshistid:
288 max_obshistid = obshistid
289 cat = TestAlertsTruthCat(star_db, obs_metadata=obs)
291 for line in cat.iter_catalog():
292 if line[1] is None:
293 continue
295 n_total_observations += 1
296 if line[0] not in true_lc_dict:
297 true_lc_dict[line[0]] = {}
298 true_lc_obshistid_dict[line[0]] = []
300 true_lc_dict[line[0]][obshistid] = line[2]
301 true_lc_obshistid_dict[line[0]].append(obshistid)
303 if line[0] not in is_visible_dict:
304 is_visible_dict[line[0]] = False
306 if line[3] <= self.obs_mag_cutoff[mag_name_to_int[obs.bandpass]]:
307 is_visible_dict[line[0]] = True
309 obshistid_bits = int(np.ceil(np.log(max_obshistid)/np.log(2)))
311 skipped_due_to_mag = 0
313 objects_to_simulate = []
314 obshistid_unqid_set = set()
315 for obj_id in true_lc_dict:
317 dmag_max = -1.0
318 for obshistid in true_lc_dict[obj_id]:
319 if np.abs(true_lc_dict[obj_id][obshistid]) > dmag_max:
320 dmag_max = np.abs(true_lc_dict[obj_id][obshistid])
322 if dmag_max >= dmag_cutoff:
323 if not is_visible_dict[obj_id]:
324 skipped_due_to_mag += 1
325 continue
327 objects_to_simulate.append(obj_id)
328 for obshistid in true_lc_obshistid_dict[obj_id]:
329 obshistid_unqid_set.add((obj_id << obshistid_bits) + obshistid)
331 self.assertGreater(len(objects_to_simulate), 10)
332 self.assertGreater(skipped_due_to_mag, 0)
334 log_file_name = tempfile.mktemp(dir=self.output_dir, suffix='log.txt')
335 alert_gen = AlertDataGenerator(testing=True)
337 alert_gen.subdivide_obs(self.obs_list, htmid_level=6)
339 for htmid in alert_gen.htmid_list:
340 alert_gen.alert_data_from_htmid(htmid, star_db,
341 photometry_class=TestAlertsVarCat,
342 output_prefix='alert_test',
343 output_dir=self.output_dir,
344 dmag_cutoff=dmag_cutoff,
345 log_file_name=log_file_name)
347 dummy_sed = Sed()
349 bp_dict = BandpassDict.loadTotalBandpassesFromFiles()
351 phot_params = PhotometricParameters()
353 # First, verify that the contents of the sqlite files are all correct
355 n_tot_simulated = 0
357 alert_query = 'SELECT alert.uniqueId, alert.obshistId, meta.TAI, '
358 alert_query += 'meta.band, quiescent.flux, alert.dflux, '
359 alert_query += 'quiescent.snr, alert.snr, '
360 alert_query += 'alert.ra, alert.dec, alert.chipNum, '
361 alert_query += 'alert.xPix, alert.yPix, ast.pmRA, ast.pmDec, '
362 alert_query += 'ast.parallax '
363 alert_query += 'FROM alert_data AS alert '
364 alert_query += 'INNER JOIN metadata AS meta ON meta.obshistId=alert.obshistId '
365 alert_query += 'INNER JOIN quiescent_flux AS quiescent '
366 alert_query += 'ON quiescent.uniqueId=alert.uniqueId '
367 alert_query += 'AND quiescent.band=meta.band '
368 alert_query += 'INNER JOIN baseline_astrometry AS ast '
369 alert_query += 'ON ast.uniqueId=alert.uniqueId'
371 alert_dtype = np.dtype([('uniqueId', int), ('obshistId', int),
372 ('TAI', float), ('band', int),
373 ('q_flux', float), ('dflux', float),
374 ('q_snr', float), ('tot_snr', float),
375 ('ra', float), ('dec', float),
376 ('chipNum', int), ('xPix', float), ('yPix', float),
377 ('pmRA', float), ('pmDec', float), ('parallax', float)])
379 sqlite_file_list = os.listdir(self.output_dir)
381 n_tot_simulated = 0
382 obshistid_unqid_simulated_set = set()
383 for file_name in sqlite_file_list:
384 if not file_name.endswith('db'):
385 continue
386 full_name = os.path.join(self.output_dir, file_name)
387 self.assertTrue(os.path.exists(full_name))
388 alert_db = DBObject(full_name, driver='sqlite')
389 alert_data = alert_db.execute_arbitrary(alert_query, dtype=alert_dtype)
390 if len(alert_data) == 0:
391 continue
393 mjd_list = ModifiedJulianDate.get_list(TAI=alert_data['TAI'])
394 for i_obj in range(len(alert_data)):
395 n_tot_simulated += 1
396 obshistid_unqid_simulated_set.add((alert_data['uniqueId'][i_obj] << obshistid_bits) +
397 alert_data['obshistId'][i_obj])
399 unq = alert_data['uniqueId'][i_obj]
400 obj_dex = (unq//1024)-1
401 self.assertAlmostEqual(self.pmra_truth[obj_dex], 0.001*alert_data['pmRA'][i_obj], 4)
402 self.assertAlmostEqual(self.pmdec_truth[obj_dex], 0.001*alert_data['pmDec'][i_obj], 4)
403 self.assertAlmostEqual(self.px_truth[obj_dex], 0.001*alert_data['parallax'][i_obj], 4)
405 ra_truth, dec_truth = applyProperMotion(self.ra_truth[obj_dex], self.dec_truth[obj_dex],
406 self.pmra_truth[obj_dex], self.pmdec_truth[obj_dex],
407 self.px_truth[obj_dex], self.vrad_truth[obj_dex],
408 mjd=mjd_list[i_obj])
409 distance = angularSeparation(ra_truth, dec_truth,
410 alert_data['ra'][i_obj], alert_data['dec'][i_obj])
412 distance_arcsec = 3600.0*distance
413 msg = '\ntruth: %e %e\nalert: %e %e\n' % (ra_truth, dec_truth,
414 alert_data['ra'][i_obj],
415 alert_data['dec'][i_obj])
417 self.assertLess(distance_arcsec, 0.0005, msg=msg)
419 obs = obs_dict[alert_data['obshistId'][i_obj]]
422 chipname = chipNameFromRaDec(self.ra_truth[obj_dex], self.dec_truth[obj_dex],
423 pm_ra=self.pmra_truth[obj_dex],
424 pm_dec=self.pmdec_truth[obj_dex],
425 parallax=self.px_truth[obj_dex],
426 v_rad=self.vrad_truth[obj_dex],
427 obs_metadata=obs,
428 camera=self.camera)
430 chipnum = int(chipname.replace('R', '').replace('S', '').
431 replace(' ', '').replace(';', '').replace(',', '').
432 replace(':', ''))
434 self.assertEqual(chipnum, alert_data['chipNum'][i_obj])
436 xpix, ypix = pixelCoordsFromRaDec(self.ra_truth[obj_dex], self.dec_truth[obj_dex],
437 pm_ra=self.pmra_truth[obj_dex],
438 pm_dec=self.pmdec_truth[obj_dex],
439 parallax=self.px_truth[obj_dex],
440 v_rad=self.vrad_truth[obj_dex],
441 obs_metadata=obs,
442 camera=self.camera)
444 self.assertAlmostEqual(alert_data['xPix'][i_obj], xpix, 4)
445 self.assertAlmostEqual(alert_data['yPix'][i_obj], ypix, 4)
447 dmag_sim = -2.5*np.log10(1.0+alert_data['dflux'][i_obj]/alert_data['q_flux'][i_obj])
448 self.assertAlmostEqual(true_lc_dict[alert_data['uniqueId'][i_obj]][alert_data['obshistId'][i_obj]],
449 dmag_sim, 3)
451 mag_name = ('u', 'g', 'r', 'i', 'z', 'y')[alert_data['band'][i_obj]]
452 m5 = obs.m5[mag_name]
454 q_mag = dummy_sed.magFromFlux(alert_data['q_flux'][i_obj])
455 self.assertAlmostEqual(self.mag0_truth_dict[alert_data['band'][i_obj]][obj_dex],
456 q_mag, 4)
458 snr, gamma = calcSNR_m5(self.mag0_truth_dict[alert_data['band'][i_obj]][obj_dex],
459 bp_dict[mag_name],
460 self.obs_mag_cutoff[alert_data['band'][i_obj]],
461 phot_params)
463 self.assertAlmostEqual(snr/alert_data['q_snr'][i_obj], 1.0, 4)
465 tot_mag = self.mag0_truth_dict[alert_data['band'][i_obj]][obj_dex] + \
466 true_lc_dict[alert_data['uniqueId'][i_obj]][alert_data['obshistId'][i_obj]]
468 snr, gamma = calcSNR_m5(tot_mag, bp_dict[mag_name],
469 m5, phot_params)
470 self.assertAlmostEqual(snr/alert_data['tot_snr'][i_obj], 1.0, 4)
472 for val in obshistid_unqid_set:
473 self.assertIn(val, obshistid_unqid_simulated_set)
474 self.assertEqual(len(obshistid_unqid_set), len(obshistid_unqid_simulated_set))
476 astrometry_query = 'SELECT uniqueId, ra, dec, TAI '
477 astrometry_query += 'FROM baseline_astrometry'
478 astrometry_dtype = np.dtype([('uniqueId', int),
479 ('ra', float),
480 ('dec', float),
481 ('TAI', float)])
483 tai_list = []
484 for obs in self.obs_list:
485 tai_list.append(obs.mjd.TAI)
486 tai_list = np.array(tai_list)
488 n_tot_ast_simulated = 0
489 for file_name in sqlite_file_list:
490 if not file_name.endswith('db'):
491 continue
492 full_name = os.path.join(self.output_dir, file_name)
493 self.assertTrue(os.path.exists(full_name))
494 alert_db = DBObject(full_name, driver='sqlite')
495 astrometry_data = alert_db.execute_arbitrary(astrometry_query, dtype=astrometry_dtype)
497 if len(astrometry_data) == 0:
498 continue
500 mjd_list = ModifiedJulianDate.get_list(TAI=astrometry_data['TAI'])
501 for i_obj in range(len(astrometry_data)):
502 n_tot_ast_simulated += 1
503 obj_dex = (astrometry_data['uniqueId'][i_obj]//1024) - 1
504 ra_truth, dec_truth = applyProperMotion(self.ra_truth[obj_dex], self.dec_truth[obj_dex],
505 self.pmra_truth[obj_dex], self.pmdec_truth[obj_dex],
506 self.px_truth[obj_dex], self.vrad_truth[obj_dex],
507 mjd=mjd_list[i_obj])
509 distance = angularSeparation(ra_truth, dec_truth,
510 astrometry_data['ra'][i_obj],
511 astrometry_data['dec'][i_obj])
513 self.assertLess(3600.0*distance, 0.0005)
515 del alert_gen
516 gc.collect()
517 self.assertGreater(n_tot_simulated, 10)
518 self.assertGreater(len(obshistid_unqid_simulated_set), 10)
519 self.assertLess(len(obshistid_unqid_simulated_set), n_total_observations)
520 self.assertGreater(n_tot_ast_simulated, 0)
523class MemoryTestClass(lsst.utils.tests.MemoryTestCase):
524 pass
527if __name__ == "__main__": 527 ↛ 528line 527 didn't jump to line 528, because the condition on line 527 was never true
528 lsst.utils.tests.init()
529 unittest.main()