231 def run(self, visitTable, mpcorb, de440s, sb441_n16, obsCodes, linux_p1550p2650, pck00010,
232 earth_latest_high_prec, earth_620120_250826, earth_2025_250826_2125_predict, naif0012):
233 """Parse the information on the current visit and retrieve the
234 observable solar system objects from Sorcha.
238 visitSummary : `lsst.afw.table.ExposureCatalog`
239 Has rows with .getVisitInfo, which give the center and time of exposure
241 mpcorb, de440s, sb441_n16, obsCodes, linux_p1550p2650, pck00010,
242 earth_latest_high_prec, earth_620120_250826, earth_2025_250826_2125_predict,
243 naif0012 : `lsst.pipe.tasks.sspAuxiliaryFile.SSPAuxiliaryFile`s
244 Minor Planet Center orbit table used for association
248 result : `lsst.pipe.base.Struct`
249 Results struct with components:
251 - ``ssObjects`` : `pandas.DataFrame`
252 DataFrame containing Solar System Objects near the detector
253 footprint as retrieved by Sorcha. The columns are as follows:
258 RA in decimal degrees (`float`)
260 DEC in decimal degrees (`float`)
263 if len(visitTable) == 0:
264 raise NoWorkFound(
"No visits input!")
265 visitTable = visitTable.to_pandas()
267 visits, epochMJD, fieldRA, fieldDec, visitTime
269 [
'visitId',
'expMidptMJD',
'ra',
'dec',
'expTime']
274 inputVisits = pd.DataFrame({
275 "observationMidpointMJD": epochMJD,
277 "fieldDec": fieldDec,
278 "observationId": visits,
279 "visitTime": np.ones(n) * visitTime,
280 "observationStartMJD": epochMJD - (visitTime / 2) / 86400.0,
281 "visitExposureTime": visitTime,
285 "seeingFwhmGeom": [0.01] * n,
286 "seeingFwhmEff": [0.01] * n,
287 "fiveSigmaDepth": [9999] * n,
288 "rotSkyPos": [0] * n,
291 desig_dict = {un: pack
for pack, un
in mpcorb[[
'packed_primary_provisional_designation',
292 'unpacked_primary_provisional_designation']]}
294 inputOrbits = Table(mpcorb[[
'unpacked_primary_provisional_designation',
'q',
'e',
'i',
295 'node',
'argperi',
'peri_time',
'epoch_mjd']])
297 inputOrbits.rename_columns(
298 [
'unpacked_primary_provisional_designation',
'epoch_mjd',
'i',
'argperi',
'peri_time'],
299 [
'ObjID',
'epochMJD_TDB',
'inc',
'argPeri',
't_p_MJD_TDB']
301 inputOrbits[
'FORMAT'] =
'COM'
303 inputColors = inputOrbits[[
"ObjID"]].copy()
304 inputColors[
"H_r"] = mpcorb[
'h']
305 inputColors[
"GS"] = 0.15
307 eph_str = resources.files(
lsst.pipe.tasks).parents[3].joinpath(
"data/eph.ini").read_text()
308 eph_str = eph_str.replace(
"{OBSCODE}", self.config.observatoryCode)
309 eph_str = eph_str.replace(
"{FOV}", str(self.config.observatoryFOVRadius))
311 with TemporaryDirectory()
as tmpdirname:
312 self.log.info(f
'temp dir: {tmpdirname}')
315 conn = sqlite_connect(f
'{tmpdirname}/pointings.db')
316 inputVisits.to_sql(
'observations', conn, if_exists=
'replace', index=
False)
319 with open(f
'{tmpdirname}/eph.ini',
'w')
as ephFile:
320 ephFile.write(eph_str)
322 cache = f
'{tmpdirname}/sorcha_cache/'
323 self.log.info(
'making cache')
325 for filename, fileref
in [
326 (
'de440s.bsp', de440s),
327 (
'sb441-n16.bsp', sb441_n16),
328 (
'ObsCodes.json', obsCodes),
329 (
'linux_p1550p2650.440', linux_p1550p2650),
330 (
'pck00010.pck', pck00010),
331 (
'earth_latest_high_prec.bpc', earth_latest_high_prec),
332 (
'earth_620120_250826.bpc', earth_620120_250826),
333 (
'earth_2025_250826_2125_predict.bpc', earth_2025_250826_2125_predict),
334 (
'naif0012.tls', naif0012),
336 self.log.info(f
'writing {filename}')
337 with open(cache + filename,
'wb')
as file:
338 file.write(fileref.fileContents.read())
340 abspath = f
'{tmpdirname}/sorcha_cache/'
342 n_iter = int(len(str(abspath)) / split)
343 for n
in range(n_iter, 0, -1):
344 abspath = abspath[:split * n] +
"+' '" + abspath[split * n:]
346 meta_kernel_text = dedent(f
"""\
349 PATH_VALUES = ('{abspath}')
355 '$A/earth_620120_250826.bpc',
356 '$A/earth_2025_250826_2125_predict.bpc',
359 '$A/earth_latest_high_prec.bpc',
364 with open(f
'{tmpdirname}/sorcha_cache/meta_kernel.txt',
'w')
as meta_kernel_file:
365 meta_kernel_file.write(meta_kernel_text)
366 self.log.info(
'Sorcha process begun')
369 inputOrbits.write(f
'{tmpdirname}/orbits.csv', format=
'ascii.csv')
370 inputColors.write(f
'{tmpdirname}/colors.csv', format=
'ascii.csv')
375 import sbpy.photometry
376 sorcha_run = [
"sorcha",
"run"]
379 self.log.info(
"WARNING: Running sorcha-run patched for sbpy issues.")
380 sorcha_run = [
"python",
"-c", textwrap.dedent(
"""
382 print("WARNING: Running sorcha-run patched for sbpy issues.", file=sys.stderr)
384 import astropy.units.utils
385 import astropy.units.docgen
386 astropy.units.utils.generate_unit_summary = \
387 astropy.units.docgen.generate_unit_summary
389 from sorcha_cmdline.run import main
395 "-c", f
"{tmpdirname}/eph.ini",
396 "-o", f
"{tmpdirname}/",
397 "--ob", f
"{tmpdirname}/orbits.csv",
398 "-p", f
"{tmpdirname}/colors.csv",
399 "--pd", f
"{tmpdirname}/pointings.db",
400 "--ew", f
"{tmpdirname}/ephemeris",
401 "--ar", f
"{tmpdirname}/sorcha_cache/",
402 "-t",
"sorcha_output",
411 while proc.poll()
is None:
412 sleep(self.config.loggingSleepTime)
415 outfile = open(glob.glob(f
'{tmpdirname}/sorcha_output*.log')[0],
'r')
416 new_text = outfile.read()
417 if len(new_text) > 0:
418 self.log.info(new_text)
420 seconds_elapsed = n_iter * self.config.loggingSleepTime
421 self.log.info(f
"Sorcha is still running ({seconds_elapsed} seconds)")
423 stdout, stderr = proc.communicate()
424 self.log.info(f
"Sorcha STDOUT:\n {stdout}")
425 self.log.info(f
"Sorcha STDERR:\n {stderr}")
427 eph_path = f
'{tmpdirname}/sorcha_output.csv'
428 if not os.path.exists(eph_path):
429 raise FileNotFoundError(
430 " Sorcha did not create sorcha_output. Check STDOUT/STDERR above. "
431 f
"Directory contents:\n{os.listdir(tmpdirname)}"
435 ephemeris = Table.read(eph_path, format=
'ascii.csv')
436 ephemeris[
'packed_desig'] = np.array([desig_dict[k]
for k
in ephemeris[
'ObjID']])
437 for col
in COL_TO_UNIT:
438 ephemeris[col] *= COL_TO_UNIT[col]
440 perVisitSsObjects = {group[
'FieldID'][0]: group
for group
in ephemeris.group_by(
"FieldID").groups}
442 if v
not in perVisitSsObjects:
443 perVisitSsObjects[v] = ephemeris[:0]
444 return Struct(ssObjects=perVisitSsObjects)