222 def run(self, visitTable, mpcorb, de440s, sb441_n16, obsCodes, linux_p1550p2650, pck00010,
223 earth_latest_high_prec, earth_620120_250826, earth_2025_250826_2125_predict, naif0012):
224 """Parse the information on the current visit and retrieve the
225 observable solar system objects from Sorcha.
229 visitSummary : `lsst.afw.table.ExposureCatalog`
230 Has rows with .getVisitInfo, which give the center and time of exposure
232 mpcorb, de440s, sb441_n16, obsCodes, linux_p1550p2650, pck00010,
233 earth_latest_high_prec, earth_620120_250826, earth_2025_250826_2125_predict,
234 naif0012 : `lsst.pipe.tasks.sspAuxiliaryFile.SSPAuxiliaryFile`s
235 Minor Planet Center orbit table used for association
239 result : `lsst.pipe.base.Struct`
240 Results struct with components:
242 - ``ssObjects`` : `pandas.DataFrame`
243 DataFrame containing Solar System Objects near the detector
244 footprint as retrieved by Sorcha. The columns are as follows:
249 RA in decimal degrees (`float`)
251 DEC in decimal degrees (`float`)
254 if len(visitTable) == 0:
255 raise NoWorkFound(
"No visits input!")
256 visitTable = visitTable.to_pandas()
258 visits, epochMJD, fieldRA, fieldDec, visitTime
260 [
'visitId',
'expMidptMJD',
'ra',
'dec',
'expTime']
265 inputVisits = pd.DataFrame({
266 "observationMidpointMJD": epochMJD,
268 "fieldDec": fieldDec,
269 "observationId": visits,
270 "visitTime": np.ones(n) * visitTime,
271 "observationStartMJD": epochMJD - (visitTime / 2) / 86400.0,
272 "visitExposureTime": visitTime,
276 "seeingFwhmGeom": [0.01] * n,
277 "seeingFwhmEff": [0.01] * n,
278 "fiveSigmaDepth": [9999] * n,
279 "rotSkyPos": [0] * n,
282 desig_dict = {un: pack
for pack, un
in mpcorb[[
'packed_primary_provisional_designation',
283 'unpacked_primary_provisional_designation']].values}
284 inputOrbits = mpcorb[
285 [
'unpacked_primary_provisional_designation',
'q',
'e',
'i',
286 'node',
'argperi',
'peri_time',
'epoch_mjd']
287 ].rename(columns={
'unpacked_primary_provisional_designation':
'ObjID',
'epoch_mjd':
'epochMJD_TDB',
288 'i':
'inc',
'argperi':
'argPeri',
'peri_time':
't_p_MJD_TDB'})
289 inputOrbits[
'FORMAT'] =
'COM'
291 inputColors = inputOrbits[[
"ObjID"]].copy()
292 inputColors[
"H_r"] = mpcorb[
'h']
293 inputColors[
"GS"] = 0.15
295 eph_str = resources.files(
lsst.pipe.tasks).parents[3].joinpath(
"data/eph.ini").read_text()
296 eph_str = eph_str.replace(
"{OBSCODE}", self.config.observatoryCode)
297 eph_str = eph_str.replace(
"{FOV}", str(self.config.observatoryFOVRadius))
299 with TemporaryDirectory()
as tmpdirname:
300 self.log.info(f
'temp dir: {tmpdirname}')
303 conn = sqlite_connect(f
'{tmpdirname}/pointings.db')
304 inputVisits.to_sql(
'observations', conn, if_exists=
'replace', index=
False)
307 with open(f
'{tmpdirname}/eph.ini',
'w')
as ephFile:
308 ephFile.write(eph_str)
310 cache = f
'{tmpdirname}/sorcha_cache/'
311 self.log.info(
'making cache')
313 for filename, fileref
in [
314 (
'de440s.bsp', de440s),
315 (
'sb441-n16.bsp', sb441_n16),
316 (
'ObsCodes.json', obsCodes),
317 (
'linux_p1550p2650.440', linux_p1550p2650),
318 (
'pck00010.pck', pck00010),
319 (
'earth_latest_high_prec.bpc', earth_latest_high_prec),
320 (
'earth_620120_250826.bpc', earth_620120_250826),
321 (
'earth_2025_250826_2125_predict.bpc', earth_2025_250826_2125_predict),
322 (
'naif0012.tls', naif0012),
324 self.log.info(f
'writing {filename}')
325 with open(cache + filename,
'wb')
as file:
326 file.write(fileref.fileContents.read())
328 abspath = f
'{tmpdirname}/sorcha_cache/'
330 n_iter = int(len(str(abspath)) / split)
331 for n
in range(n_iter, 0, -1):
332 abspath = abspath[:split * n] +
"+' '" + abspath[split * n:]
334 meta_kernel_text = dedent(f
"""\
337 PATH_VALUES = ('{abspath}')
343 '$A/earth_620120_250826.bpc',
344 '$A/earth_2025_250826_2125_predict.bpc',
347 '$A/earth_latest_high_prec.bpc',
352 with open(f
'{tmpdirname}/sorcha_cache/meta_kernel.txt',
'w')
as meta_kernel_file:
353 meta_kernel_file.write(meta_kernel_text)
354 self.log.info(
'Sorcha process begun')
357 inputOrbits.to_csv(f
'{tmpdirname}/orbits.csv', index=
False)
358 inputColors.to_csv(f
'{tmpdirname}/colors.csv', index=
False)
363 import sbpy.photometry
364 sorcha_run = [
"sorcha",
"run"]
367 self.log.info(
"WARNING: Running sorcha-run patched for sbpy issues.")
368 sorcha_run = [
"python",
"-c", textwrap.dedent(
"""
370 print("WARNING: Running sorcha-run patched for sbpy issues.", file=sys.stderr)
372 import astropy.units.utils
373 import astropy.units.docgen
374 astropy.units.utils.generate_unit_summary = \
375 astropy.units.docgen.generate_unit_summary
377 from sorcha_cmdline.run import main
383 "-c", f
"{tmpdirname}/eph.ini",
384 "-o", f
"{tmpdirname}/",
385 "--ob", f
"{tmpdirname}/orbits.csv",
386 "-p", f
"{tmpdirname}/colors.csv",
387 "--pd", f
"{tmpdirname}/pointings.db",
388 "--ew", f
"{tmpdirname}/ephemeris",
389 "--ar", f
"{tmpdirname}/sorcha_cache/",
390 "-t",
"sorcha_output",
399 while proc.poll()
is None:
400 sleep(self.config.loggingSleepTime)
403 outfile = open(glob.glob(f
'{tmpdirname}/sorcha_output*.log')[0],
'r')
404 new_text = outfile.read()
405 if len(new_text) > 0:
406 self.log.info(new_text)
408 seconds_elapsed = n_iter * self.config.loggingSleepTime
409 self.log.info(f
"Sorcha is still running ({seconds_elapsed} seconds)")
411 stdout, stderr = proc.communicate()
412 self.log.info(f
"Sorcha STDOUT:\n {stdout}")
413 self.log.info(f
"Sorcha STDERR:\n {stderr}")
415 eph_path = f
'{tmpdirname}/sorcha_output.csv'
416 if not os.path.exists(eph_path):
417 raise FileNotFoundError(
418 " Sorcha did not create sorcha_output. Check STDOUT/STDERR above. "
419 f
"Directory contents:\n{os.listdir(tmpdirname)}"
423 ephemeris = pd.read_csv(eph_path)
424 ephemeris[
'packed_desig'] = ephemeris[
'ObjID'].map(desig_dict)
425 perVisitSsObjects = {FieldID: group
for FieldID, group
in ephemeris.groupby(
"FieldID")}
427 if v
not in perVisitSsObjects:
428 perVisitSsObjects[v] = ephemeris.iloc[:0]
429 return Struct(ssObjects=perVisitSsObjects)