Coverage for tests/test_alertDataGenerator.py : 12%

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